trace performance fix
Build and Publish / Metadata (push) Successful in 9s
Build and Publish / BuildAndDeployAmd64 (push) Failing after 24s

This commit is contained in:
developer committed 2026-09-24 19:04:00 +00:00
1 parent 6dc2785f71
commit 4f6e6ba35d
1 file changed
+36 -26
+36 -26
View File
@@ -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> = {};