trace performance fix
This commit is contained in:
1 parent
6dc2785f71
commit
4f6e6ba35d
1 file changed
+36
-26
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState, useRef, useEffect } from "react";
|
||||
import { useMemo, useState, useRef, useEffect, useDeferredValue } from "react";
|
||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||
import {
|
||||
Sparkles,
|
||||
@@ -54,6 +54,8 @@ export type ProcessedTrace = {
|
||||
endpoint: string;
|
||||
};
|
||||
|
||||
const EMPTY_TRACES: TraceData[] = [];
|
||||
|
||||
// Flattened item for virtualized tree rendering
|
||||
interface FlattenedSpanItem {
|
||||
span: ProcessedSpan;
|
||||
@@ -96,14 +98,24 @@ const flattenSpanTree = (
|
||||
collapsedSet: Set<string>,
|
||||
traceMinTime: number,
|
||||
traceTotalRange: number,
|
||||
depth = 0,
|
||||
initialDepth = 0,
|
||||
): FlattenedSpanItem[] => {
|
||||
const result: FlattenedSpanItem[] = [];
|
||||
const pending: Array<{ span: ProcessedSpan; depth: number }> = [];
|
||||
|
||||
for (const span of nodes) {
|
||||
const hasChildren = Boolean(span.children && span.children.length > 0);
|
||||
for (let index = nodes.length - 1; index >= 0; index -= 1) {
|
||||
pending.push({ span: nodes[index], depth: initialDepth });
|
||||
}
|
||||
|
||||
// Avoid recursively spreading descendant arrays: deep parent/child chains
|
||||
// otherwise copy the same nodes once per ancestor.
|
||||
while (pending.length > 0) {
|
||||
const current = pending.pop();
|
||||
if (!current) break;
|
||||
const { span, depth } = current;
|
||||
const children = span.children ?? [];
|
||||
const hasChildren = children.length > 0;
|
||||
const isCollapsed = collapsedSet.has(span.spanId);
|
||||
|
||||
const offsetPercent =
|
||||
traceTotalRange === 0 ? 0 : ((span.startTime - traceMinTime) / traceTotalRange) * 100;
|
||||
const widthPercent =
|
||||
@@ -118,16 +130,10 @@ const flattenSpanTree = (
|
||||
widthPercent: Math.max(0.5, Math.min(widthPercent, 100 - offsetPercent)),
|
||||
});
|
||||
|
||||
if (hasChildren && !isCollapsed && span.children) {
|
||||
result.push(
|
||||
...flattenSpanTree(
|
||||
span.children,
|
||||
collapsedSet,
|
||||
traceMinTime,
|
||||
traceTotalRange,
|
||||
depth + 1,
|
||||
),
|
||||
);
|
||||
if (hasChildren && !isCollapsed) {
|
||||
for (let index = children.length - 1; index >= 0; index -= 1) {
|
||||
pending.push({ span: children[index], depth: depth + 1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,6 +545,7 @@ const VirtualizedSpanWaterfall = ({
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [errorsOnly, setErrorsOnly] = useState(false);
|
||||
const [minDurationMsFilter, setMinDurationMsFilter] = useState<number>(0);
|
||||
const deferredSearchQuery = useDeferredValue(searchQuery);
|
||||
const parentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Build tree
|
||||
@@ -563,8 +570,8 @@ const VirtualizedSpanWaterfall = ({
|
||||
const durMs = item.span.duration / 1_000_000;
|
||||
if (durMs < minDurationMsFilter) return false;
|
||||
}
|
||||
if (searchQuery.trim()) {
|
||||
const query = searchQuery.toLowerCase().trim();
|
||||
if (deferredSearchQuery.trim()) {
|
||||
const query = deferredSearchQuery.toLowerCase().trim();
|
||||
const matchesName = item.span.name.toLowerCase().includes(query);
|
||||
const matchesService = item.span.service.toLowerCase().includes(query);
|
||||
const matchesAttrs = Object.values(item.span.formattedAttributes).some((v) =>
|
||||
@@ -574,7 +581,7 @@ const VirtualizedSpanWaterfall = ({
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}, [flattenedItems, filterService, errorsOnly, minDurationMsFilter, searchQuery]);
|
||||
}, [flattenedItems, filterService, errorsOnly, minDurationMsFilter, deferredSearchQuery]);
|
||||
|
||||
|
||||
// Virtualizer
|
||||
@@ -1683,10 +1690,12 @@ export const processTraces = (
|
||||
),
|
||||
);
|
||||
|
||||
const uniqueSpans = spanAccumulator.filter(
|
||||
(span, index, arr) =>
|
||||
arr.findIndex((candidate) => candidate.spanId === span.spanId) === index,
|
||||
);
|
||||
const seenSpanIds = new Set<string>();
|
||||
const uniqueSpans = spanAccumulator.filter((span) => {
|
||||
if (seenSpanIds.has(span.spanId)) return false;
|
||||
seenSpanIds.add(span.spanId);
|
||||
return true;
|
||||
});
|
||||
|
||||
const traceGroups = uniqueSpans.reduce<Record<string, ProcessedSpan[]>>((acc, span) => {
|
||||
if (!acc[span.traceId]) acc[span.traceId] = [];
|
||||
@@ -1739,7 +1748,7 @@ export interface TraceVisualizationProps {
|
||||
}
|
||||
|
||||
export const TraceVisualization = ({ traces }: TraceVisualizationProps) => {
|
||||
const safeTraces = traces ?? [];
|
||||
const safeTraces = traces ?? EMPTY_TRACES;
|
||||
const {
|
||||
logCountBySpanId,
|
||||
activeTraceId,
|
||||
@@ -1756,6 +1765,7 @@ export const TraceVisualization = ({ traces }: TraceVisualizationProps) => {
|
||||
"recent" | "duration-desc" | "duration-asc" | "errors" | "spans"
|
||||
>("recent");
|
||||
const [groupByEndpoint, setGroupByEndpoint] = useState(false);
|
||||
const deferredTraceSearchQuery = useDeferredValue(traceSearchQuery);
|
||||
|
||||
const orderedTraces: ProcessedTrace[] = useMemo(
|
||||
() => processTraces(safeTraces, logCountBySpanId),
|
||||
@@ -1774,8 +1784,8 @@ export const TraceVisualization = ({ traces }: TraceVisualizationProps) => {
|
||||
if (traceFilter === "errors" && !t.hasError) return false;
|
||||
if (traceFilter === "slow" && t.duration / 1_000_000 < 200) return false;
|
||||
if (traceServiceFilter !== "all" && !t.services.includes(traceServiceFilter)) return false;
|
||||
if (traceSearchQuery.trim()) {
|
||||
const q = traceSearchQuery.toLowerCase().trim();
|
||||
if (deferredTraceSearchQuery.trim()) {
|
||||
const q = deferredTraceSearchQuery.toLowerCase().trim();
|
||||
const matchName = t.name.toLowerCase().includes(q);
|
||||
const matchId = t.id.toLowerCase().includes(q);
|
||||
const matchService = t.services.some((s) => s.toLowerCase().includes(q));
|
||||
@@ -1791,7 +1801,7 @@ export const TraceVisualization = ({ traces }: TraceVisualizationProps) => {
|
||||
if (traceSortBy === "spans") return b.spans.length - a.spans.length;
|
||||
return b.startTime - a.startTime; // recent
|
||||
});
|
||||
}, [orderedTraces, traceFilter, traceServiceFilter, traceSearchQuery, traceSortBy]);
|
||||
}, [orderedTraces, traceFilter, traceServiceFilter, deferredTraceSearchQuery, traceSortBy]);
|
||||
|
||||
const groupCounts = useMemo(() => {
|
||||
const counts: Record<string, number> = {};
|
||||
|
||||
Reference in new issue
Block a user