From 083e2c717353db3986cdce3f1fe9c574ad7386c3 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 26 Nov 2025 18:36:09 +0100 Subject: [PATCH 1/4] fix(waterfall): Prefer frontend root events in trace root lookup --- .../newTraceDetails/traceApi/utils.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/static/app/views/performance/newTraceDetails/traceApi/utils.tsx b/static/app/views/performance/newTraceDetails/traceApi/utils.tsx index ad7c2ee57b471b..a07f5d49c7245f 100644 --- a/static/app/views/performance/newTraceDetails/traceApi/utils.tsx +++ b/static/app/views/performance/newTraceDetails/traceApi/utils.tsx @@ -57,6 +57,8 @@ export const getRepresentativeTraceEvent = ( return {type: 'uptime_check', event: traceChild.value}; } + let preferredRootEvent: TraceTree.TraceEvent | null = null; + let eapRootEvent: TraceTree.TraceEvent | null = null; let rootEvent: TraceTree.TraceEvent | null = null; let candidateEvent: TraceTree.TraceEvent | null = null; let firstEvent: TraceTree.TraceEvent | null = null; @@ -75,8 +77,18 @@ export const getRepresentativeTraceEvent = ( } if (isEAPTransaction(event)) { - // If we find a root EAP transaction, we can stop looking and use it for the title. - break; + // We prefer certain root events over conventional root events. + // These make better titles in the waterfall view and make linked + // trace navigations work. + if (event.op && CANDIDATE_TRACE_TITLE_OPS.includes(event.op)) { + preferredRootEvent = event; + break; + } + + // Otherwise, we still prefer the first EAP root event over the conventional root event + if (!eapRootEvent) { + eapRootEvent = event; + } } // Otherwise we keep looking for a root eap transaction. If we don't find one, we use other roots, like standalone spans. continue; @@ -103,7 +115,8 @@ export const getRepresentativeTraceEvent = ( } return { - event: rootEvent ?? candidateEvent ?? firstEvent, + event: + preferredRootEvent ?? eapRootEvent ?? rootEvent ?? candidateEvent ?? firstEvent, type: 'span', }; }; From 8bc150a6c18e9f4e11794a4ee7c532d2011ff2d2 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 28 Nov 2025 11:42:08 +0100 Subject: [PATCH 2/4] remove eap-specific logic --- .../newTraceDetails/traceApi/utils.tsx | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/static/app/views/performance/newTraceDetails/traceApi/utils.tsx b/static/app/views/performance/newTraceDetails/traceApi/utils.tsx index a07f5d49c7245f..fa6ddcf31db489 100644 --- a/static/app/views/performance/newTraceDetails/traceApi/utils.tsx +++ b/static/app/views/performance/newTraceDetails/traceApi/utils.tsx @@ -3,7 +3,6 @@ import type {OurLogsResponseItem} from 'sentry/views/explore/logs/types'; import type {TraceRootEventQueryResults} from 'sentry/views/performance/newTraceDetails/traceApi/useTraceRootEvent'; import { isEAPTraceNode, - isEAPTransaction, isRootEvent, isTraceNode, isTraceSplitResult, @@ -58,7 +57,6 @@ export const getRepresentativeTraceEvent = ( } let preferredRootEvent: TraceTree.TraceEvent | null = null; - let eapRootEvent: TraceTree.TraceEvent | null = null; let rootEvent: TraceTree.TraceEvent | null = null; let candidateEvent: TraceTree.TraceEvent | null = null; let firstEvent: TraceTree.TraceEvent | null = null; @@ -71,25 +69,13 @@ export const getRepresentativeTraceEvent = ( if (isRootEvent(event)) { rootEvent = event; - if (!isEAP) { - // For non-EAP traces, we return the first root event. + // We prefer certain root events over conventional root events. + // These make better titles in the waterfall view and make linked + // trace navigations work. + if ('op' in event && event.op && CANDIDATE_TRACE_TITLE_OPS.includes(event.op)) { + preferredRootEvent = event; break; } - - if (isEAPTransaction(event)) { - // We prefer certain root events over conventional root events. - // These make better titles in the waterfall view and make linked - // trace navigations work. - if (event.op && CANDIDATE_TRACE_TITLE_OPS.includes(event.op)) { - preferredRootEvent = event; - break; - } - - // Otherwise, we still prefer the first EAP root event over the conventional root event - if (!eapRootEvent) { - eapRootEvent = event; - } - } // Otherwise we keep looking for a root eap transaction. If we don't find one, we use other roots, like standalone spans. continue; } else if ( @@ -115,8 +101,7 @@ export const getRepresentativeTraceEvent = ( } return { - event: - preferredRootEvent ?? eapRootEvent ?? rootEvent ?? candidateEvent ?? firstEvent, + event: preferredRootEvent ?? rootEvent ?? candidateEvent ?? firstEvent, type: 'span', }; }; From 84578da90217fb912d70abf7a0583ab1c5da3c5a Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 28 Nov 2025 11:52:33 +0100 Subject: [PATCH 3/4] extract op lookup and check --- .../newTraceDetails/traceApi/utils.tsx | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/static/app/views/performance/newTraceDetails/traceApi/utils.tsx b/static/app/views/performance/newTraceDetails/traceApi/utils.tsx index fa6ddcf31db489..d1bdeac9762a79 100644 --- a/static/app/views/performance/newTraceDetails/traceApi/utils.tsx +++ b/static/app/views/performance/newTraceDetails/traceApi/utils.tsx @@ -69,10 +69,7 @@ export const getRepresentativeTraceEvent = ( if (isRootEvent(event)) { rootEvent = event; - // We prefer certain root events over conventional root events. - // These make better titles in the waterfall view and make linked - // trace navigations work. - if ('op' in event && event.op && CANDIDATE_TRACE_TITLE_OPS.includes(event.op)) { + if (hasPreferredOp(event)) { preferredRootEvent = event; break; } @@ -83,13 +80,7 @@ export const getRepresentativeTraceEvent = ( // with an op that we care about, we can use it for the title. We keep looking for // a root. !candidateEvent && - CANDIDATE_TRACE_TITLE_OPS.includes( - 'transaction.op' in event - ? event['transaction.op'] - : 'op' in event - ? event.op - : '' - ) + hasPreferredOp(event) ) { candidateEvent = event; continue; @@ -117,3 +108,18 @@ export const isValidEventUUID = (id: string): boolean => { /^[0-9a-f]{8}[0-9a-f]{4}[1-5][0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}$/i; return uuidRegex.test(id); }; + +/** + * Prefer "special" root events over generic root events when generating a title + * for the waterfall view. Picking these improves contextual navigation for linked + * traces, resulting in more meaningful waterfall titles. + */ +function hasPreferredOp(event: TraceTree.TraceEvent): boolean { + const op = + 'op' in event + ? event.op + : 'transaction.op' in event + ? event['transaction.op'] + : undefined; + return !!op && CANDIDATE_TRACE_TITLE_OPS.includes(op); +} From 82e5ee6df42f8b65774dff9018a8041778d99d8d Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 28 Nov 2025 11:56:09 +0100 Subject: [PATCH 4/4] fall back to first root event (instead of last) --- .../views/performance/newTraceDetails/traceApi/utils.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/static/app/views/performance/newTraceDetails/traceApi/utils.tsx b/static/app/views/performance/newTraceDetails/traceApi/utils.tsx index d1bdeac9762a79..7c79a0dc5481c3 100644 --- a/static/app/views/performance/newTraceDetails/traceApi/utils.tsx +++ b/static/app/views/performance/newTraceDetails/traceApi/utils.tsx @@ -57,7 +57,7 @@ export const getRepresentativeTraceEvent = ( } let preferredRootEvent: TraceTree.TraceEvent | null = null; - let rootEvent: TraceTree.TraceEvent | null = null; + let firstRootEvent: TraceTree.TraceEvent | null = null; let candidateEvent: TraceTree.TraceEvent | null = null; let firstEvent: TraceTree.TraceEvent | null = null; @@ -67,7 +67,9 @@ export const getRepresentativeTraceEvent = ( : [...traceNode.value.transactions, ...traceNode.value.orphan_errors]; for (const event of events) { if (isRootEvent(event)) { - rootEvent = event; + if (!firstRootEvent) { + firstRootEvent = event; + } if (hasPreferredOp(event)) { preferredRootEvent = event; @@ -92,7 +94,7 @@ export const getRepresentativeTraceEvent = ( } return { - event: preferredRootEvent ?? rootEvent ?? candidateEvent ?? firstEvent, + event: preferredRootEvent ?? firstRootEvent ?? candidateEvent ?? firstEvent, type: 'span', }; };