Skip to content

Commit d09bb99

Browse files
gggritsoantonpirker
authored andcommitted
ref(database): Improve useSpanMetricsSeries (#59932)
- Add spec for `useSpanMetricsSeries` - Improve `useSpanMetricsSeries` API - allow passing _any_ string filter - remove `groupId` argument, pass it as a filter instead - pass standard filter names This is simpler and more consistent. Removing the `groupId` argument, and allowing _all_ string filters will make it easier to re-use this in other contexts. Adds a sorely needed spec.
1 parent d0f8e44 commit d09bb99

File tree

8 files changed

+156
-48
lines changed

8 files changed

+156
-48
lines changed

static/app/views/performance/browser/resources/resourceSummaryPage/resourceSummaryCharts.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ function ResourceSummaryCharts(props: {groupId: string}) {
3535

3636
const {data: spanMetricsSeriesData, isLoading: areSpanMetricsSeriesLoading} =
3737
useSpanMetricsSeries(
38-
props.groupId,
3938
{
39+
'span.group': props.groupId,
4040
...(filters[RESOURCE_RENDER_BLOCKING_STATUS]
4141
? {[RESOURCE_RENDER_BLOCKING_STATUS]: filters[RESOURCE_RENDER_BLOCKING_STATUS]}
4242
: {}),

static/app/views/performance/database/databaseSpanSummaryPage.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,13 @@ function SpanSummaryPage({params}: Props) {
9494
};
9595

9696
const {isLoading: isThroughputDataLoading, data: throughputData} = useSpanMetricsSeries(
97-
groupId,
98-
queryFilter,
97+
{...queryFilter, 'span.group': groupId},
9998
['spm()'],
10099
'api.starfish.span-summary-page-metrics-chart'
101100
);
102101

103102
const {isLoading: isDurationDataLoading, data: durationData} = useSpanMetricsSeries(
104-
groupId,
105-
queryFilter,
103+
{...queryFilter, 'span.group': groupId},
106104
[`${selectedAggregate}(${SpanMetricsField.SPAN_SELF_TIME})`],
107105
'api.starfish.span-summary-page-metrics-chart'
108106
);
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import {ReactNode} from 'react';
2+
import {Organization} from 'sentry-fixture/organization';
3+
4+
import {makeTestQueryClient} from 'sentry-test/queryClient';
5+
import {reactHooks} from 'sentry-test/reactTestingLibrary';
6+
7+
import {QueryClientProvider} from 'sentry/utils/queryClient';
8+
import {useLocation} from 'sentry/utils/useLocation';
9+
import useOrganization from 'sentry/utils/useOrganization';
10+
import usePageFilters from 'sentry/utils/usePageFilters';
11+
import {useSpanMetricsSeries} from 'sentry/views/starfish/queries/useSpanMetricsSeries';
12+
13+
jest.mock('sentry/utils/useLocation');
14+
jest.mock('sentry/utils/usePageFilters');
15+
jest.mock('sentry/utils/useOrganization');
16+
17+
function Wrapper({children}: {children?: ReactNode}) {
18+
return (
19+
<QueryClientProvider client={makeTestQueryClient()}>{children}</QueryClientProvider>
20+
);
21+
}
22+
23+
describe('useSpanMetricsSeries', () => {
24+
const organization = Organization();
25+
26+
jest.mocked(usePageFilters).mockReturnValue({
27+
isReady: true,
28+
desyncedFilters: new Set(),
29+
pinnedFilters: new Set(),
30+
shouldPersist: true,
31+
selection: {
32+
datetime: {
33+
period: '10d',
34+
start: null,
35+
end: null,
36+
utc: false,
37+
},
38+
environments: [],
39+
projects: [],
40+
},
41+
});
42+
43+
jest.mocked(useLocation).mockReturnValue({
44+
pathname: '',
45+
search: '',
46+
query: {},
47+
hash: '',
48+
state: undefined,
49+
action: 'PUSH',
50+
key: '',
51+
});
52+
53+
jest.mocked(useOrganization).mockReturnValue(organization);
54+
55+
it('queries for current selection', async () => {
56+
const eventsRequest = MockApiClient.addMockResponse({
57+
url: `/organizations/${organization.slug}/events-stats/`,
58+
method: 'GET',
59+
body: {
60+
'spm()': {
61+
data: [
62+
[1699907700, [{count: 7810.2}]],
63+
[1699908000, [{count: 1216.8}]],
64+
],
65+
},
66+
},
67+
});
68+
69+
const {result, waitForNextUpdate} = reactHooks.renderHook(
70+
({filters, yAxis}) => useSpanMetricsSeries(filters, yAxis),
71+
{
72+
wrapper: Wrapper,
73+
initialProps: {
74+
filters: {
75+
'span.group': '221aa7ebd216',
76+
transaction: '/api/details',
77+
release: '0.0.1',
78+
'resource.render_blocking_status': 'blocking' as const,
79+
},
80+
yAxis: ['spm()'],
81+
},
82+
}
83+
);
84+
85+
expect(result.current.isLoading).toEqual(true);
86+
87+
expect(eventsRequest).toHaveBeenCalledWith(
88+
'/organizations/org-slug/events-stats/',
89+
expect.objectContaining({
90+
method: 'GET',
91+
query: expect.objectContaining({
92+
query: `span.group:221aa7ebd216 transaction:/api/details release:0.0.1 resource.render_blocking_status:blocking`,
93+
dataset: 'spansMetrics',
94+
statsPeriod: '10d',
95+
referrer: 'span-metrics-series',
96+
interval: '30m',
97+
yAxis: 'spm()',
98+
}),
99+
})
100+
);
101+
102+
await waitForNextUpdate();
103+
104+
expect(result.current.isLoading).toEqual(false);
105+
expect(result.current.data).toEqual({
106+
'spm()': {
107+
data: [
108+
{name: '2023-11-13T20:35:00+00:00', value: 7810.2},
109+
{name: '2023-11-13T20:40:00+00:00', value: 1216.8},
110+
],
111+
seriesName: 'spm()',
112+
},
113+
});
114+
});
115+
});

static/app/views/starfish/queries/useSpanMetricsSeries.tsx

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@ import {PageFilters} from 'sentry/types';
55
import {Series} from 'sentry/types/echarts';
66
import EventView from 'sentry/utils/discover/eventView';
77
import {DiscoverDatasets} from 'sentry/utils/discover/types';
8+
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
89
import usePageFilters from 'sentry/utils/usePageFilters';
9-
import {SpanSummaryQueryFilters} from 'sentry/views/starfish/queries/useSpanMetrics';
10-
import {SpanMetricsField} from 'sentry/views/starfish/types';
10+
import {SpanMetricsQueryFilters} from 'sentry/views/starfish/types';
1111
import {STARFISH_CHART_INTERVAL_FIDELITY} from 'sentry/views/starfish/utils/constants';
1212
import {useSpansQuery} from 'sentry/views/starfish/utils/useSpansQuery';
13-
14-
const {SPAN_GROUP, RESOURCE_RENDER_BLOCKING_STATUS} = SpanMetricsField;
13+
import {EMPTY_OPTION_VALUE} from 'sentry/views/starfish/views/spans/selectors/emptyOption';
1514

1615
export type SpanMetrics = {
1716
interval: number;
@@ -22,19 +21,15 @@ export type SpanMetrics = {
2221
};
2322

2423
export const useSpanMetricsSeries = (
25-
group: string,
26-
queryFilters: SpanSummaryQueryFilters,
24+
filters: SpanMetricsQueryFilters,
2725
yAxis: string[] = [],
2826
referrer = 'span-metrics-series'
2927
) => {
3028
const pageFilters = usePageFilters();
3129

32-
const eventView = group
33-
? getEventView(group, pageFilters.selection, yAxis, queryFilters)
34-
: undefined;
30+
const eventView = getEventView(filters, pageFilters.selection, yAxis);
3531

36-
const enabled =
37-
Boolean(group) && Object.values(queryFilters).every(value => Boolean(value));
32+
const enabled = Object.values(filters).every(value => Boolean(value));
3833

3934
const result = useSpansQuery<SpanMetrics[]>({
4035
eventView,
@@ -62,31 +57,31 @@ export const useSpanMetricsSeries = (
6257
};
6358

6459
function getEventView(
65-
group: string,
60+
filters: SpanMetricsQueryFilters,
6661
pageFilters: PageFilters,
67-
yAxis: string[],
68-
queryFilters: SpanSummaryQueryFilters
62+
yAxis: string[]
6963
) {
70-
const query = [
71-
`${SPAN_GROUP}:${group}`,
72-
...(queryFilters?.transactionName
73-
? [`transaction:"${queryFilters?.transactionName}"`]
74-
: []),
75-
...(queryFilters?.['transaction.method']
76-
? [`transaction.method:${queryFilters?.['transaction.method']}`]
77-
: []),
78-
...(queryFilters?.release ? [`release:${queryFilters?.release}`] : []),
79-
...(queryFilters?.[RESOURCE_RENDER_BLOCKING_STATUS]
80-
? [
81-
`${RESOURCE_RENDER_BLOCKING_STATUS}:${queryFilters[RESOURCE_RENDER_BLOCKING_STATUS]}`,
82-
]
83-
: []),
84-
].join(' ');
64+
const query = new MutableSearch('');
65+
66+
Object.entries(filters).forEach(([key, value]) => {
67+
if (!value) {
68+
return;
69+
}
70+
71+
if (value === EMPTY_OPTION_VALUE) {
72+
query.addFilterValue('!has', key);
73+
}
74+
75+
query.addFilterValue(key, value, !ALLOWED_WILDCARD_FIELDS.includes(key));
76+
});
77+
78+
// TODO: This condition should be enforced everywhere
79+
// query.addFilterValue('has', 'span.description');
8580

8681
return EventView.fromNewQueryWithPageFilters(
8782
{
8883
name: '',
89-
query,
84+
query: query.formatString(),
9085
fields: [],
9186
yAxis,
9287
dataset: DiscoverDatasets.SPANS_METRICS,
@@ -96,3 +91,5 @@ function getEventView(
9691
pageFilters
9792
);
9893
}
94+
95+
const ALLOWED_WILDCARD_FIELDS = ['span.description'];

static/app/views/starfish/queries/useSpanSamples.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,7 @@ export const useSpanSamples = (options: Options) => {
7474
const dateCondtions = getDateConditions(pageFilter.selection);
7575

7676
const {isLoading: isLoadingSeries, data: spanMetricsSeriesData} = useSpanMetricsSeries(
77-
groupId,
78-
filters,
77+
{'span.group': groupId, ...filters},
7978
[`avg(${SPAN_SELF_TIME})`],
8079
'api.starfish.sidebar-span-metrics'
8180
);

static/app/views/starfish/types.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ export type SpanStringFields =
4949
| 'span.group'
5050
| 'project.id'
5151
| 'transaction'
52-
| 'transaction.method';
52+
| 'transaction.method'
53+
| 'release';
5354

5455
export type SpanMetricsQueryFilters = {
5556
[Field in SpanStringFields]?: string;

static/app/views/starfish/views/spanSummaryPage/sampleList/durationChart/index.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,7 @@ function DurationChart({
9393
data: spanMetricsSeriesData,
9494
error: spanMetricsSeriesError,
9595
} = useSpanMetricsSeries(
96-
groupId,
97-
filters,
96+
{...filters, 'span.group': groupId},
9897
[`avg(${SPAN_SELF_TIME})`],
9998
'api.starfish.sidebar-span-metrics-chart'
10099
);

static/app/views/starfish/views/spanSummaryPage/spanSummaryView.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@ import ChartPanel from 'sentry/views/starfish/components/chartPanel';
1111
import StarfishDatePicker from 'sentry/views/starfish/components/datePicker';
1212
import {SpanDescription} from 'sentry/views/starfish/components/spanDescription';
1313
import {useFullSpanFromTrace} from 'sentry/views/starfish/queries/useFullSpanFromTrace';
14-
import {
15-
SpanSummaryQueryFilters,
16-
useSpanMetrics,
17-
} from 'sentry/views/starfish/queries/useSpanMetrics';
14+
import {useSpanMetrics} from 'sentry/views/starfish/queries/useSpanMetrics';
1815
import {useSpanMetricsSeries} from 'sentry/views/starfish/queries/useSpanMetricsSeries';
1916
import {SpanFunction, SpanMetricsField} from 'sentry/views/starfish/types';
2017
import {
@@ -43,8 +40,11 @@ export function SpanSummaryView({groupId}: Props) {
4340

4441
const {data: fullSpan} = useFullSpanFromTrace(groupId);
4542

46-
const queryFilter: SpanSummaryQueryFilters = endpoint
47-
? {transactionName: endpoint, 'transaction.method': endpointMethod}
43+
const queryFilter = endpoint
44+
? {
45+
transactionName: endpoint,
46+
'transaction.method': endpointMethod,
47+
}
4848
: {};
4949

5050
const {data: spanMetrics} = useSpanMetrics(
@@ -78,8 +78,7 @@ export function SpanSummaryView({groupId}: Props) {
7878

7979
const {isLoading: areSpanMetricsSeriesLoading, data: spanMetricsSeriesData} =
8080
useSpanMetricsSeries(
81-
groupId,
82-
queryFilter,
81+
{'span.group': groupId, ...queryFilter},
8382
[`avg(${SpanMetricsField.SPAN_SELF_TIME})`, 'spm()', 'http_error_count()'],
8483
'api.starfish.span-summary-page-metrics-chart'
8584
);

0 commit comments

Comments
 (0)