Skip to content

Commit 36cf866

Browse files
authored
fix: Dont clobber spands in waterfall when multiple spans have duplicate span id (#1396)
Resolves HDX-2885
1 parent e3643cc commit 36cf866

File tree

2 files changed

+42
-9
lines changed

2 files changed

+42
-9
lines changed

.changeset/nasty-mangos-cross.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/app": patch
3+
---
4+
5+
fix: Don't clobber spans in trace waterfall when multiple spans have duplicate span ids

packages/app/src/components/DBTraceWaterfallChart.tsx

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,8 @@ export function DBTraceWaterfallChartContainer({
465465
);
466466
}, [traceRowsData]);
467467
const rootNodes: Node[] = [];
468-
const nodesMap = new Map();
468+
const nodesMap = new Map(); // Maps result.id (or placeholder id) -> Node
469+
const spanIdMap = new Map(); // Maps SpanId -> result.id of FIRST node with that SpanId
469470

470471
for (const result of rows ?? []) {
471472
const { type, SpanId, ParentSpanId } = result;
@@ -480,11 +481,26 @@ export function DBTraceWaterfallChartContainer({
480481
const curNode = {
481482
...result,
482483
children: [],
483-
// In case we were created already previously, inherit the children built so far
484-
...nodesMap.get(nodeSpanId),
485484
};
486-
if (type === SourceKind.Trace && !nodesMap.has(nodeSpanId)) {
487-
nodesMap.set(nodeSpanId, curNode);
485+
486+
if (type === SourceKind.Trace) {
487+
// Check if this is the first node with this SpanId
488+
if (!spanIdMap.has(nodeSpanId)) {
489+
// First occurrence - this becomes the canonical node for this SpanId
490+
spanIdMap.set(nodeSpanId, result.id);
491+
492+
// Check if there's a placeholder parent waiting for this SpanId
493+
const placeholderId = `placeholder-${nodeSpanId}`;
494+
const placeholder = nodesMap.get(placeholderId);
495+
if (placeholder) {
496+
// Inherit children from placeholder
497+
curNode.children = placeholder.children || [];
498+
// Remove placeholder
499+
nodesMap.delete(placeholderId);
500+
}
501+
}
502+
// Always add to nodesMap with unique result.id
503+
nodesMap.set(result.id, curNode);
488504
}
489505

490506
// root if: is trace event, and (has no parent or parent id is not valid)
@@ -495,11 +511,23 @@ export function DBTraceWaterfallChartContainer({
495511
if (isRootNode) {
496512
rootNodes.push(curNode);
497513
} else {
498-
const parentNode = nodesMap.get(nodeParentSpanId) ?? {
499-
children: [],
500-
};
514+
// Look up parent by SpanId
515+
const parentResultId = spanIdMap.get(nodeParentSpanId);
516+
let parentNode = parentResultId
517+
? nodesMap.get(parentResultId)
518+
: undefined;
519+
520+
if (!parentNode) {
521+
// Parent doesn't exist yet, create placeholder
522+
const placeholderId = `placeholder-${nodeParentSpanId}`;
523+
parentNode = nodesMap.get(placeholderId);
524+
if (!parentNode) {
525+
parentNode = { children: [] } as any;
526+
nodesMap.set(placeholderId, parentNode);
527+
}
528+
}
529+
501530
parentNode.children.push(curNode);
502-
nodesMap.set(nodeParentSpanId, parentNode);
503531
}
504532
}
505533

0 commit comments

Comments
 (0)