Skip to content

Commit 4c61a85

Browse files
committed
chore: performance refactor
1 parent b16d22e commit 4c61a85

File tree

1 file changed

+57
-41
lines changed

1 file changed

+57
-41
lines changed

frontend/app/src/components/v1/agent-prism/convert-otel-spans-to-agent-prism-span-tree.ts

Lines changed: 57 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,6 @@ function countParents(nodes: OtelSpanTree[]): Map<string, number> {
6767
return counts;
6868
}
6969

70-
function hasInProgress(nodes: OtelSpanTree[]): boolean {
71-
return nodes.some((n) => n.inProgress || hasInProgress(n.children));
72-
}
73-
7470
// ---------------------------------------------------------------------------
7571
// Synthetic span factories
7672
// ---------------------------------------------------------------------------
@@ -96,7 +92,6 @@ function makeSyntheticRoot(
9692
createdAt: new Date(earliestStart).toISOString(),
9793
spanAttributes: { [ATTR.INSTRUMENTOR]: 'hatchet' },
9894
children,
99-
inProgress: children.some((s) => s.inProgress),
10095
...overrides,
10196
};
10297
}
@@ -312,30 +307,42 @@ function reparentOrphans(rootSpans: OtelSpanTree[]): void {
312307
const stepRunIndex = new Map<string, OtelSpanTree>();
313308
buildStepRunIndex(rootSpans, stepRunIndex);
314309

315-
const reparented = new Set<string>();
310+
const ancestorIds = new Set<string>();
316311

317-
for (const orphan of rootSpans) {
318-
if (!orphan.parentSpanId) {
319-
continue;
320-
}
321-
const stepRunId = getStepRunId(orphan);
322-
if (!stepRunId) {
323-
continue;
312+
const reparentLevel = (nodes: OtelSpanTree[]) => {
313+
const reparented = new Set<string>();
314+
315+
for (const orphan of nodes) {
316+
if (!orphan.parentSpanId) {
317+
continue;
318+
}
319+
const stepRunId = getStepRunId(orphan);
320+
if (!stepRunId) {
321+
continue;
322+
}
323+
const surrogate = stepRunIndex.get(stepRunId);
324+
if (
325+
surrogate &&
326+
surrogate.spanId !== orphan.spanId &&
327+
!ancestorIds.has(surrogate.spanId)
328+
) {
329+
surrogate.children.push(orphan);
330+
reparented.add(orphan.spanId);
331+
}
324332
}
325-
const surrogate = stepRunIndex.get(stepRunId);
326-
if (surrogate && surrogate.spanId !== orphan.spanId) {
327-
surrogate.children.push(orphan);
328-
reparented.add(orphan.spanId);
333+
334+
if (reparented.size > 0) {
335+
removeByPredicate(nodes, (n) => reparented.has(n.spanId));
329336
}
330-
}
331337

332-
if (reparented.size > 0) {
333-
removeByPredicate(rootSpans, (n) => reparented.has(n.spanId));
334-
}
338+
for (const node of nodes) {
339+
ancestorIds.add(node.spanId);
340+
reparentLevel(node.children);
341+
ancestorIds.delete(node.spanId);
342+
}
343+
};
335344

336-
for (const node of rootSpans) {
337-
reparentOrphans(node.children);
338-
}
345+
reparentLevel(rootSpans);
339346
}
340347

341348
function suppressOrphanedChildWorkflows(
@@ -484,12 +491,11 @@ function synthesizePendingTaskSpans(
484491
// ---------------------------------------------------------------------------
485492

486493
function sortChildrenStable(nodes: OtelSpanTree[]): void {
487-
const keys = new Map<OtelSpanTree, number>();
488-
for (const node of nodes) {
489-
const src = node.queuedPhase ?? node;
490-
keys.set(node, new Date(src.createdAt).getTime());
491-
}
492-
nodes.sort((a, b) => keys.get(a)! - keys.get(b)!);
494+
nodes.sort((a, b) => {
495+
const aKey = (a.queuedPhase ?? a).createdAt;
496+
const bKey = (b.queuedPhase ?? b).createdAt;
497+
return aKey < bKey ? -1 : aKey > bKey ? 1 : 0;
498+
});
493499

494500
for (const node of nodes) {
495501
if (node.children.length > 1) {
@@ -542,11 +548,20 @@ function attachWorkflowQueuedPhase(
542548
};
543549
}
544550

545-
function computeHasErrorInSubtree(node: OtelSpanTree): boolean {
546-
const childHasError = node.children.some(computeHasErrorInSubtree);
551+
function computeSubtreeFlags(node: OtelSpanTree): boolean {
552+
let childHasError = false;
553+
let childHasInProgress = false;
554+
for (const child of node.children) {
555+
if (computeSubtreeFlags(child)) {
556+
childHasInProgress = true;
557+
}
558+
if (child.hasErrorInSubtree) {
559+
childHasError = true;
560+
}
561+
}
547562
node.hasErrorInSubtree =
548563
node.statusCode === OtelStatusCode.ERROR || childHasError;
549-
return node.hasErrorInSubtree;
564+
return node.inProgress === true || childHasInProgress;
550565
}
551566

552567
// ---------------------------------------------------------------------------
@@ -606,7 +621,11 @@ function wrapMultipleRoots(
606621
if (workflowRunTiming && rootSpans.length > 0) {
607622
attachWorkflowQueuedPhase(rootSpans[0], workflowRunTiming);
608623
}
609-
rootSpans.forEach(computeHasErrorInSubtree);
624+
for (const root of rootSpans) {
625+
if (computeSubtreeFlags(root)) {
626+
root.inProgress = true;
627+
}
628+
}
610629
return rootSpans;
611630
}
612631

@@ -638,14 +657,15 @@ function wrapMultipleRoots(
638657
[ATTR.INSTRUMENTOR]: 'hatchet',
639658
...(workflowName && { [ATTR.WORKFLOW_NAME]: workflowName }),
640659
},
641-
inProgress: hasInProgress(rootSpans),
642660
});
643661

644662
if (workflowRunTiming) {
645663
attachWorkflowQueuedPhase(syntheticRoot, workflowRunTiming);
646664
}
647665

648-
computeHasErrorInSubtree(syntheticRoot);
666+
if (computeSubtreeFlags(syntheticRoot)) {
667+
syntheticRoot.inProgress = true;
668+
}
649669
return [syntheticRoot];
650670
}
651671

@@ -670,7 +690,7 @@ function buildTreeFromTaskSummaries(
670690
});
671691
attachWorkflowQueuedPhase(syntheticRoot, workflowRunTiming);
672692
if (syntheticRoot.queuedPhase) {
673-
computeHasErrorInSubtree(syntheticRoot);
693+
computeSubtreeFlags(syntheticRoot);
674694
return [syntheticRoot];
675695
}
676696
}
@@ -727,9 +747,5 @@ export const convertOtelSpansToOtelSpanTree = (
727747

728748
sortChildrenStable(rootSpans);
729749

730-
if (rootSpans.length === 1 && hasInProgress(rootSpans[0].children)) {
731-
rootSpans[0].inProgress = true;
732-
}
733-
734750
return wrapMultipleRoots(rootSpans, workflowRunTiming);
735751
};

0 commit comments

Comments
 (0)