Skip to content

Commit 935900c

Browse files
mjqclaude
authored andcommitted
fix(performance): Fix EAP transaction summary method filter and duration breakdown chart (#108483)
Fix two issues with the EAP transaction summary page and improve the duration breakdown chart. ## Method filter mismatch When the `performance-transaction-summary-eap` flag is enabled, dashboard widget links filter by `request.method` (the EAP field name), but the transaction summary search bar didn't recognize it as a valid key. This caused an "Invalid key" error when navigating from dashboards. Now the search bar accepts `request.method` when EAP is enabled, and the dashboard link uses the correct field name based on the flag. ## Duration breakdown chart The EAP version of the duration breakdown chart now matches the regular (non-EAP) version: - removed the extra p90 percentile - uses high-fidelity time intervals - aliases series names to match the standard format (e.g. `p50()` instead of `p50(span.duration)`) - persists legend selection via the `unselectedSeries` URL query parameter. Note that this is now a line chart instead of an area chart. As per our [Choosing The Plot Type](https://sentry.sentry.io/stories/product/views/dashboards/widgets/timeserieswidget/timeserieswidgetvisualization/#choosing-the-plot-type) guide: > Only use area charts if you want to plot multiple series and those series represent components of a total. For example, area charts are a good choice to show time spent, broken down by span operation. They are not a good choice for plotting a single duration time series. Area charts should be stacked! ## Screens Old Transaction Summary: <img width="1223" height="296" alt="Screenshot 2026-02-23 at 10 53 34" src="https://github.com/user-attachments/assets/2608f2e0-b2ac-40fb-a53d-a0a400b46489" /> EAP version: <img width="1225" height="312" alt="Screenshot 2026-02-23 at 10 49 01" src="https://github.com/user-attachments/assets/47d90d51-bb87-40a6-a7ff-4a63b31b987a" /> Closes DAIN-1226 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 222a6a8 commit 935900c

File tree

3 files changed

+37
-5
lines changed

3 files changed

+37
-5
lines changed

static/app/components/performance/transactionSearchQueryBuilder.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
isAggregateField,
2121
isMeasurement,
2222
} from 'sentry/utils/discover/fields';
23-
import {DEVICE_CLASS_TAG_VALUES, isDeviceClass} from 'sentry/utils/fields';
23+
import {DEVICE_CLASS_TAG_VALUES, FieldKind, isDeviceClass} from 'sentry/utils/fields';
2424
import {getMeasurements} from 'sentry/utils/measurements/measurements';
2525
import {getHasTag} from 'sentry/utils/tag';
2626
import useApi from 'sentry/utils/useApi';
@@ -78,9 +78,17 @@ export function TransactionSearchQueryBuilder({
7878
...tags,
7979
};
8080

81+
if (organization.features.includes('performance-transaction-summary-eap')) {
82+
combinedTags['request.method'] = {
83+
key: 'request.method',
84+
name: 'request.method',
85+
kind: FieldKind.FIELD,
86+
};
87+
}
88+
8189
combinedTags.has = getHasTag(combinedTags);
8290
return combinedTags;
83-
}, [tags]);
91+
}, [organization.features, tags]);
8492

8593
const filterKeySections = useMemo(
8694
() => [

static/app/views/dashboards/datasetConfig/spans.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,11 @@ function renderTransactionAsLinkable(data: EventData, baggage: RenderFunctionBag
443443
filters.addFilterValue('transaction.op', data[SpanFields.SPAN_OP]);
444444
}
445445
if (data[SpanFields.REQUEST_METHOD]) {
446-
filters.addFilterValue('http.method', data[SpanFields.REQUEST_METHOD]);
446+
const isEap = organization.features.includes('performance-transaction-summary-eap');
447+
filters.addFilterValue(
448+
isEap ? 'request.method' : 'http.method',
449+
data[SpanFields.REQUEST_METHOD]
450+
);
447451
}
448452

449453
const target = transactionSummaryRouteWithQuery({

static/app/views/performance/transactionSummary/transactionOverview/useWidgetChartVisualization.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import {useTheme} from '@emotion/react';
22

3+
import {getInterval, getSeriesSelection} from 'sentry/components/charts/utils';
34
import usePageFilters from 'sentry/components/pageFilters/usePageFilters';
5+
import {parseFunction} from 'sentry/utils/discover/fields';
46
import {decodeScalar} from 'sentry/utils/queryString';
57
import {useFetchSpanTimeSeries} from 'sentry/utils/timeSeries/useFetchEventsTimeSeries';
68
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
79
import {useLocation} from 'sentry/utils/useLocation';
10+
import {useNavigate} from 'sentry/utils/useNavigate';
811
import {useReleaseStats} from 'sentry/utils/useReleaseStats';
912
import {Line} from 'sentry/views/dashboards/widgets/timeSeriesWidget/plottables/line';
1013
import {TimeSeriesWidgetVisualization} from 'sentry/views/dashboards/widgets/timeSeriesWidget/timeSeriesWidgetVisualization';
@@ -73,8 +76,10 @@ function useDurationBreakdownVisualization({
7376
query,
7477
}: DurationBreakdownVisualizationOptions) {
7578
const location = useLocation();
79+
const navigate = useNavigate();
7680
const spanCategoryUrlParam = decodeScalar(location.query?.[SpanFields.SPAN_CATEGORY]);
7781
const {selection} = usePageFilters();
82+
const legendSelection = getSeriesSelection(location);
7883

7984
const {releases: releasesWithDate} = useReleaseStats(selection);
8085
const releases =
@@ -104,11 +109,11 @@ function useDurationBreakdownVisualization({
104109
'p100(span.duration)',
105110
'p99(span.duration)',
106111
'p95(span.duration)',
107-
'p90(span.duration)',
108112
'p75(span.duration)',
109113
'p50(span.duration)',
110114
],
111115
query: newQuery,
116+
interval: getInterval(selection.datetime, 'high'),
112117
enabled,
113118
},
114119
REFERRER
@@ -122,13 +127,28 @@ function useDurationBreakdownVisualization({
122127
return <TimeSeriesWidgetVisualization.LoadingPlaceholder />;
123128
}
124129

125-
const plottables = spanSeriesData?.timeSeries.map(series => new Line(series));
130+
const plottables =
131+
spanSeriesData?.timeSeries.map(series => {
132+
const name = `${parseFunction(series.yAxis)?.name}()`;
133+
return new Line(series, {name, alias: name});
134+
}) ?? [];
126135

127136
return (
128137
<TimeSeriesWidgetVisualization
129138
plottables={plottables}
130139
releases={releases}
131140
showReleaseAs="bubble"
141+
legendSelection={legendSelection}
142+
onLegendSelectionChange={selected => {
143+
const unselected = Object.keys(selected).filter(key => !selected[key]);
144+
navigate({
145+
...location,
146+
query: {
147+
...location.query,
148+
unselectedSeries: unselected,
149+
},
150+
});
151+
}}
132152
/>
133153
);
134154
}

0 commit comments

Comments
 (0)