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