Skip to content

Commit a04effd

Browse files
feat(performance): Fill out more samples data and update tables and charts in destination summary for queues module (#70394)
- Updates latency charts to include receive latency - Update destination summary transactions table to show operation type - Update samples table to show message size, id, and status
1 parent 72b1a6b commit a04effd

File tree

9 files changed

+127
-22
lines changed

9 files changed

+127
-22
lines changed

static/app/views/performance/queues/charts/latencyChart.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ export function LatencyChart({error}: Props) {
3333
data={
3434
[
3535
{
36-
seriesName: t('Average Processing Latency'),
36+
seriesName: t('Average Time in Queue'),
37+
data: data['avg(messaging.message.receive.latency)'].data,
38+
},
39+
{
40+
seriesName: t('Average Processing Time'),
3741
data: data['avg_if(span.self_time,span.op,queue.process)'].data,
3842
},
3943
] ?? []
@@ -42,6 +46,7 @@ export function LatencyChart({error}: Props) {
4246
error={error}
4347
chartColors={CHART_PALETTE[2].slice(1)}
4448
type={ChartType.AREA}
49+
stacked
4550
/>
4651
</ChartPanel>
4752
);

static/app/views/performance/queues/destinationSummary/transactionsTable.spec.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ describe('transactionsTable', () => {
9494
expect(screen.getByRole('cell', {name: '2'})).toBeInTheDocument();
9595
expect(screen.getByRole('cell', {name: '6.00ms'})).toBeInTheDocument();
9696
expect(screen.getByRole('cell', {name: '20.00ms'})).toBeInTheDocument();
97+
expect(screen.getByRole('cell', {name: 'Consumer'})).toBeInTheDocument();
9798
expect(screen.getByRole('button', {name: 'Next'})).toBeInTheDocument();
9899
});
99100
});

static/app/views/performance/queues/destinationSummary/transactionsTable.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,20 @@ function renderBodyCell(
127127
location: Location,
128128
organization: Organization
129129
) {
130+
const op = row['span.op'];
131+
const isProducer = op === 'queue.publish';
132+
const isConsumer = op === 'queue.process';
130133
const key = column.key;
131-
if (row[key] === undefined) {
134+
if (
135+
row[key] === undefined ||
136+
(isConsumer && ['count_op(queue.publish)'].includes(key)) ||
137+
(isProducer &&
138+
[
139+
'count_op(queue.process)',
140+
'avg(messaging.message.receive.latency)',
141+
'avg_if(span.self_time,span.op,queue.process)',
142+
].includes(key))
143+
) {
132144
return (
133145
<AlignRight>
134146
<NoValue>{' \u2014 '}</NoValue>
@@ -149,6 +161,17 @@ function renderBodyCell(
149161
return renderer(key, row);
150162
}
151163

164+
if (key === 'span.op') {
165+
switch (row[key]) {
166+
case 'queue.publish':
167+
return t('Producer');
168+
case 'queue.process':
169+
return t('Consumer');
170+
default:
171+
return row[key];
172+
}
173+
}
174+
152175
const renderer = getFieldRenderer(column.key, meta.fields, false);
153176
return renderer(row, {
154177
location,

static/app/views/performance/queues/messageConsumerSamplesPanel.spec.tsx

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {OrganizationFixture} from 'sentry-fixture/organization';
22

3-
import {render, screen} from 'sentry-test/reactTestingLibrary';
3+
import {render, screen, waitForElementToBeRemoved} from 'sentry-test/reactTestingLibrary';
44

55
import {useLocation} from 'sentry/utils/useLocation';
66
import useOrganization from 'sentry/utils/useOrganization';
@@ -14,7 +14,7 @@ jest.mock('sentry/utils/useOrganization');
1414
describe('messageConsumerSamplesPanel', () => {
1515
const organization = OrganizationFixture();
1616

17-
let eventsRequestMock, eventsStatsRequestMock;
17+
let eventsRequestMock, eventsStatsRequestMock, samplesRequestMock;
1818

1919
jest.mocked(usePageFilters).mockReturnValue({
2020
isReady: true,
@@ -63,14 +63,31 @@ describe('messageConsumerSamplesPanel', () => {
6363
meta: {},
6464
},
6565
});
66+
67+
samplesRequestMock = MockApiClient.addMockResponse({
68+
url: `/api/0/organizations/${organization.slug}/spans-samples/`,
69+
method: 'GET',
70+
body: {
71+
data: [
72+
{
73+
span_id: '123',
74+
trace: 'abc',
75+
project: 'project',
76+
timestamp: '2024-03-25T20:31:36+00:00',
77+
'span.self_time': 320.300102,
78+
},
79+
],
80+
},
81+
});
6682
});
6783

6884
afterAll(() => {
6985
jest.resetAllMocks();
7086
});
7187

72-
it('renders', () => {
88+
it('renders', async () => {
7389
render(<MessageConsumerSamplesPanel />);
90+
await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
7491
expect(eventsStatsRequestMock).toHaveBeenCalled();
7592
expect(eventsRequestMock).toHaveBeenCalledWith(
7693
`/organizations/${organization.slug}/events/`,
@@ -97,6 +114,32 @@ describe('messageConsumerSamplesPanel', () => {
97114
}),
98115
})
99116
);
117+
expect(samplesRequestMock).toHaveBeenCalledWith(
118+
`/api/0/organizations/${organization.slug}/spans-samples/`,
119+
expect.objectContaining({
120+
query: expect.objectContaining({
121+
additionalFields: [
122+
'trace',
123+
'transaction.id',
124+
'span.description',
125+
'measurements.messaging.message.body.size',
126+
'measurements.messaging.message.receive.latency',
127+
'messaging.message.id',
128+
'trace.status',
129+
'span.self_time',
130+
],
131+
firstBound: 2666.6666666666665,
132+
lowerBound: 0,
133+
project: [],
134+
query:
135+
'span.op:queue.process OR span.op:queue.publish transaction:sentry.tasks.store.save_event messaging.destination.name:event-queue',
136+
referrer: undefined,
137+
secondBound: 5333.333333333333,
138+
statsPeriod: '10d',
139+
upperBound: 8000,
140+
}),
141+
})
142+
);
100143
expect(screen.getByRole('table', {name: 'Span Samples'})).toBeInTheDocument();
101144
});
102145
});

static/app/views/performance/queues/messageConsumerSamplesPanel.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {Button} from 'sentry/components/button';
66
import Link from 'sentry/components/links/link';
77
import {t} from 'sentry/locale';
88
import {space} from 'sentry/styles/space';
9-
import {DurationUnit} from 'sentry/utils/discover/fields';
9+
import {DurationUnit, SizeUnit} from 'sentry/utils/discover/fields';
1010
import {PageAlertProvider} from 'sentry/utils/performance/contexts/pageAlert';
1111
import {decodeScalar} from 'sentry/utils/queryString';
1212
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
@@ -26,6 +26,7 @@ import {useQueuesMetricsQuery} from 'sentry/views/performance/queues/queries/use
2626
import {computeAxisMax} from 'sentry/views/starfish/components/chart';
2727
import DetailPanel from 'sentry/views/starfish/components/detailPanel';
2828
import {useSpanMetricsSeries} from 'sentry/views/starfish/queries/useSeries';
29+
import {SpanIndexedField} from 'sentry/views/starfish/types';
2930
import {useSampleScatterPlotSeries} from 'sentry/views/starfish/views/spanSummaryPage/sampleList/durationChart/useSampleScatterPlotSeries';
3031

3132
// We're defining our own query filter here, apart from settings.ts because the spans endpoint doesn't accept IN operations
@@ -92,6 +93,16 @@ export function MessageConsumerSamplesPanel() {
9293
min: 0,
9394
max: durationAxisMax,
9495
enabled: isPanelOpen && durationAxisMax > 0,
96+
fields: [
97+
SpanIndexedField.TRACE,
98+
SpanIndexedField.TRANSACTION_ID,
99+
SpanIndexedField.SPAN_DESCRIPTION,
100+
SpanIndexedField.MESSAGING_MESSAGE_BODY_SIZE,
101+
SpanIndexedField.MESSAGING_MESSAGE_RECEIVE_LATENCY,
102+
SpanIndexedField.MESSAGING_MESSAGE_ID,
103+
SpanIndexedField.TRACE_STATUS,
104+
SpanIndexedField.SPAN_SELF_TIME,
105+
],
95106
});
96107

97108
const sampledSpanDataSeries = useSampleScatterPlotSeries(
@@ -220,8 +231,12 @@ export function MessageConsumerSamplesPanel() {
220231
meta={{
221232
fields: {
222233
'span.self_time': 'duration',
234+
'measurements.messaging.message.body.size': 'size',
235+
},
236+
units: {
237+
'span.self_time': DurationUnit.MILLISECOND,
238+
'measurements.messaging.message.body.size': SizeUnit.BYTE,
223239
},
224-
units: {},
225240
}}
226241
/>
227242
</ModuleLayout.Full>

static/app/views/performance/queues/messageSpanSamplesTable.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ type DataRowKeys =
2424
| SpanIndexedField.TIMESTAMP
2525
| SpanIndexedField.ID
2626
| SpanIndexedField.SPAN_DESCRIPTION
27-
| SpanIndexedField.RESPONSE_CODE;
27+
| SpanIndexedField.MESSAGING_MESSAGE_BODY_SIZE
28+
| SpanIndexedField.MESSAGING_MESSAGE_RECEIVE_LATENCY
29+
| SpanIndexedField.MESSAGING_MESSAGE_ID
30+
| SpanIndexedField.TRACE_STATUS
31+
| SpanIndexedField.SPAN_SELF_TIME;
2832

2933
type ColumnKeys =
3034
| SpanIndexedField.ID
31-
| SpanIndexedField.MESSAGE_ID
32-
| SpanIndexedField.MESSAGE_SIZE
33-
| SpanIndexedField.MESSAGE_STATUS
35+
| SpanIndexedField.MESSAGING_MESSAGE_ID
36+
| SpanIndexedField.MESSAGING_MESSAGE_BODY_SIZE
37+
| SpanIndexedField.TRACE_STATUS
3438
| SpanIndexedField.SPAN_SELF_TIME;
3539

3640
type DataRow = Pick<IndexedResponse, DataRowKeys>;
@@ -41,10 +45,10 @@ const COLUMN_ORDER: Column[] = [
4145
{
4246
key: SpanIndexedField.ID,
4347
name: t('Span ID'),
44-
width: COL_WIDTH_UNDEFINED,
48+
width: 150,
4549
},
4650
{
47-
key: SpanIndexedField.MESSAGE_ID,
51+
key: SpanIndexedField.MESSAGING_MESSAGE_ID,
4852
name: t('Message ID'),
4953
width: COL_WIDTH_UNDEFINED,
5054
},
@@ -54,12 +58,12 @@ const COLUMN_ORDER: Column[] = [
5458
width: COL_WIDTH_UNDEFINED,
5559
},
5660
{
57-
key: SpanIndexedField.MESSAGE_SIZE,
61+
key: SpanIndexedField.MESSAGING_MESSAGE_BODY_SIZE,
5862
name: t('Message Size'),
5963
width: COL_WIDTH_UNDEFINED,
6064
},
6165
{
62-
key: SpanIndexedField.MESSAGE_STATUS,
66+
key: SpanIndexedField.TRACE_STATUS,
6367
name: t('Status'),
6468
width: COL_WIDTH_UNDEFINED,
6569
},

static/app/views/starfish/components/chart.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ export const STARFISH_FIELDS: Record<string, {outputType: AggregationOutputType}
7575
[SpanMetricsField.CACHE_ITEM_SIZE]: {
7676
outputType: 'size',
7777
},
78+
[SpanMetricsField.MESSAGING_MESSAGE_RECEIVE_LATENCY]: {
79+
outputType: 'duration',
80+
},
7881
};
7982

8083
export enum ChartType {

static/app/views/starfish/components/tableCells/renderHeadCell.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ type Options = {
2626
const DEFAULT_SORT_PARAMETER_NAME = 'sort';
2727

2828
const {SPAN_SELF_TIME, HTTP_RESPONSE_CONTENT_LENGTH, CACHE_ITEM_SIZE} = SpanMetricsField;
29-
const {RESPONSE_CODE} = SpanIndexedField;
29+
const {RESPONSE_CODE, MESSAGING_MESSAGE_BODY_SIZE} = SpanIndexedField;
3030
const {
3131
TIME_SPENT_PERCENTAGE,
3232
SPS,
@@ -61,6 +61,8 @@ const NUMERIC_FIELDS = new Set([
6161
`${RESPONSE_CODE}`,
6262
CACHE_ITEM_SIZE,
6363
'transaction.duration',
64+
SpanIndexedField.SPAN_SELF_TIME,
65+
MESSAGING_MESSAGE_BODY_SIZE,
6466
]);
6567

6668
export const renderHeadCell = ({column, location, sort, sortParameterName}: Options) => {

static/app/views/starfish/types.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,10 @@ export enum SpanIndexedField {
187187
RESPONSE_CODE = 'span.status_code',
188188
CACHE_HIT = 'cache.hit',
189189
CACHE_ITEM_SIZE = 'measurements.cache.item_size',
190-
MESSAGE_ID = 'message.id',
191-
MESSAGE_SIZE = 'message.size',
192-
MESSAGE_STATUS = 'message.status',
190+
TRACE_STATUS = 'trace.status',
191+
MESSAGING_MESSAGE_ID = 'messaging.message.id',
192+
MESSAGING_MESSAGE_BODY_SIZE = 'measurements.messaging.message.body.size',
193+
MESSAGING_MESSAGE_RECEIVE_LATENCY = 'measurements.messaging.message.receive.latency',
193194
}
194195

195196
export type IndexedResponse = {
@@ -226,9 +227,10 @@ export type IndexedResponse = {
226227
[SpanIndexedField.RESPONSE_CODE]: string;
227228
[SpanIndexedField.CACHE_HIT]: '' | 'true' | 'false';
228229
[SpanIndexedField.CACHE_ITEM_SIZE]: number;
229-
[SpanIndexedField.MESSAGE_ID]: string;
230-
[SpanIndexedField.MESSAGE_SIZE]: number;
231-
[SpanIndexedField.MESSAGE_STATUS]: string;
230+
[SpanIndexedField.TRACE_STATUS]: string;
231+
[SpanIndexedField.MESSAGING_MESSAGE_ID]: string;
232+
[SpanIndexedField.MESSAGING_MESSAGE_BODY_SIZE]: number;
233+
[SpanIndexedField.MESSAGING_MESSAGE_RECEIVE_LATENCY]: number;
232234
};
233235

234236
export type IndexedProperty = keyof IndexedResponse;
@@ -246,6 +248,7 @@ export enum SpanFunction {
246248
HTTP_RESPONSE_RATE = 'http_response_rate',
247249
CACHE_HIT_RATE = 'cache_hit_rate',
248250
CACHE_MISS_RATE = 'cache_miss_rate',
251+
COUNT_OP = 'count_op',
249252
}
250253

251254
export const StarfishDatasetFields = {
@@ -299,6 +302,12 @@ export const STARFISH_AGGREGATION_FIELDS: Record<
299302
kind: FieldKind.FUNCTION,
300303
valueType: FieldValueType.NUMBER,
301304
},
305+
[SpanFunction.COUNT_OP]: {
306+
desc: t('Count of spans with matching operation'),
307+
defaultOutputType: 'integer',
308+
kind: FieldKind.FUNCTION,
309+
valueType: FieldValueType.NUMBER,
310+
},
302311
};
303312

304313
// TODO - add more functions and fields, combine shared ones, etc

0 commit comments

Comments
 (0)