Skip to content

Commit 152b334

Browse files
authored
ref: Replays should split memory spans apart from network spans earlier (#49123)
Related to #47991
1 parent 7700fb0 commit 152b334

File tree

7 files changed

+113
-138
lines changed

7 files changed

+113
-138
lines changed

static/app/utils/replays/replayDataUtils.spec.tsx

Lines changed: 0 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import {
22
breadcrumbFactory,
3-
isMemorySpan,
4-
isNetworkSpan,
5-
mapRRWebAttachments,
63
rrwebEventListFactory,
74
} from 'sentry/utils/replays/replayDataUtils';
85
import type {ReplayRecord} from 'sentry/views/replays/types';
@@ -21,19 +18,6 @@ const navigateSpan = TestStubs.ReplaySpanPayload({
2118
op: 'navigation.navigate',
2219
description: 'http://test.com',
2320
});
24-
const cssSpan = TestStubs.ReplaySpanPayload({
25-
op: 'resource.css',
26-
description: 'http://test.com/static/media/glyphicons-halflings-regular.448c34a5.woff2',
27-
});
28-
const memorySpan = TestStubs.ReplaySpanPayload({
29-
op: 'memory',
30-
description: 'memory',
31-
data: {
32-
jsHeapSizeLimit: 4294705152,
33-
totalJSHeapSize: 19203353,
34-
usedJSHeapSize: 16119217,
35-
},
36-
});
3721

3822
describe('breadcrumbFactory', () => {
3923
it('adds LCP as a breadcrumb', () => {
@@ -159,32 +143,6 @@ describe('breadcrumbFactory', () => {
159143
});
160144
});
161145

162-
describe('isMemorySpan', () => {
163-
it('should identify memory spans by the op field', () => {
164-
expect(isMemorySpan(memorySpan)).toBeTruthy();
165-
});
166-
167-
it('should reject spans which are not op=memory', () => {
168-
expect(isMemorySpan(cssSpan)).toBeFalsy();
169-
expect(isMemorySpan(fooSpan)).toBeFalsy();
170-
expect(isMemorySpan(lcpSpan)).toBeFalsy();
171-
expect(isMemorySpan(navigateSpan)).toBeFalsy();
172-
});
173-
});
174-
175-
describe('isNetworkSpan', () => {
176-
it('should identify network spans by the op field', () => {
177-
expect(isNetworkSpan(cssSpan)).toBeTruthy();
178-
expect(isNetworkSpan(navigateSpan)).toBeTruthy();
179-
});
180-
181-
it('should reject spans which are not some kind of op=navigation', () => {
182-
expect(isNetworkSpan(fooSpan)).toBeFalsy();
183-
expect(isNetworkSpan(lcpSpan)).toBeFalsy();
184-
expect(isNetworkSpan(memorySpan)).toBeFalsy();
185-
});
186-
});
187-
188146
describe('rrwebEventListFactory', () => {
189147
it('returns a list of replay events for highlights', function () {
190148
const replayRecord = {
@@ -230,45 +188,3 @@ describe('rrwebEventListFactory', () => {
230188
]);
231189
});
232190
});
233-
234-
describe('mapRRWebAttachments', () => {
235-
const testPayload = [
236-
...TestStubs.ReplaySegmentInit({timestamp: new Date(1654290037123)}),
237-
...TestStubs.ReplaySegmentBreadcrumb({
238-
timestamp: new Date(1654290037267),
239-
payload: {
240-
type: 'default',
241-
category: 'ui.click',
242-
message: 'body > div#root > div.App > form',
243-
data: {nodeId: 44},
244-
},
245-
}),
246-
...TestStubs.ReplaySegmentSpan({
247-
timestamp: new Date(1654290034262),
248-
payload: TestStubs.ReplaySpanPayload({
249-
op: 'navigation.navigate',
250-
description: 'http://localhost:3000/',
251-
startTimestamp: new Date(1654290034262),
252-
endTimestamp: new Date(1654290034580),
253-
data: {size: 1150},
254-
}),
255-
}),
256-
...TestStubs.ReplaySegmentSpan({
257-
timestamp: new Date(1654290034262.3),
258-
payload: TestStubs.ReplaySpanPayload({
259-
op: 'navigation.navigate',
260-
description: 'http://localhost:3000/',
261-
startTimestamp: new Date(1654290034262.3),
262-
endTimestamp: new Date(1654290034580.8),
263-
data: {size: 1150},
264-
}),
265-
}),
266-
];
267-
268-
it('should split attachments by type', () => {
269-
const {breadcrumbs, rrwebEvents, spans} = mapRRWebAttachments(testPayload);
270-
expect(breadcrumbs.length).toBe(1);
271-
expect(rrwebEvents.length).toBe(3);
272-
expect(spans.length).toBe(2);
273-
});
274-
});

static/app/utils/replays/replayDataUtils.tsx

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,56 +13,13 @@ import type {
1313
import {BreadcrumbLevelType, BreadcrumbType} from 'sentry/types/breadcrumbs';
1414
import getMinMax from 'sentry/utils/getMinMax';
1515
import type {
16-
MemorySpanType,
1716
RecordingEvent,
1817
ReplayCrumb,
1918
ReplayError,
2019
ReplayRecord,
2120
ReplaySpan,
2221
} from 'sentry/views/replays/types';
2322

24-
// Errors if it is an interface
25-
// See https://github.com/microsoft/TypeScript/issues/15300
26-
type ReplayAttachmentsByTypeMap = {
27-
breadcrumbs: ReplayCrumb[];
28-
29-
/**
30-
* The flattened list of rrweb events. These are stored as multiple attachments on the root replay object: the `event` prop.
31-
*/
32-
rrwebEvents: RecordingEvent[];
33-
spans: ReplaySpan[];
34-
};
35-
36-
export function mapRRWebAttachments(
37-
unsortedReplayAttachments: any[]
38-
): ReplayAttachmentsByTypeMap {
39-
const replayAttachments: ReplayAttachmentsByTypeMap = {
40-
breadcrumbs: [],
41-
rrwebEvents: [],
42-
spans: [],
43-
};
44-
45-
unsortedReplayAttachments.forEach(attachment => {
46-
if (attachment.data?.tag === 'performanceSpan') {
47-
replayAttachments.spans.push(attachment.data.payload);
48-
} else if (attachment?.data?.tag === 'breadcrumb') {
49-
replayAttachments.breadcrumbs.push(attachment.data.payload);
50-
} else {
51-
replayAttachments.rrwebEvents.push(attachment);
52-
}
53-
});
54-
55-
return replayAttachments;
56-
}
57-
58-
export const isMemorySpan = (span: ReplaySpan): span is MemorySpanType => {
59-
return span.op === 'memory';
60-
};
61-
62-
export const isNetworkSpan = (span: ReplaySpan) => {
63-
return span.op.startsWith('navigation.') || span.op.startsWith('resource.');
64-
};
65-
6623
export function mapResponseToReplayRecord(apiResponse: any): ReplayRecord {
6724
// Marshal special fields into tags
6825
const user = Object.fromEntries(

static/app/utils/replays/replayReader.tsx

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@ import type {Crumb} from 'sentry/types/breadcrumbs';
66
import {BreadcrumbType} from 'sentry/types/breadcrumbs';
77
import {
88
breadcrumbFactory,
9-
isMemorySpan,
10-
isNetworkSpan,
11-
mapRRWebAttachments,
129
replayTimestamps,
1310
rrwebEventListFactory,
1411
spansFactory,
1512
} from 'sentry/utils/replays/replayDataUtils';
13+
import splitAttachmentsByType from 'sentry/utils/replays/splitAttachmentsByType';
1614
import type {
15+
MemorySpan,
16+
NetworkSpan,
1717
RecordingEvent,
1818
RecordingOptions,
19+
ReplayCrumb,
1920
ReplayError,
2021
ReplayRecord,
2122
ReplaySpan,
@@ -69,14 +70,17 @@ export default class ReplayReader {
6970
replayRecord,
7071
errors,
7172
}: RequiredNotNull<ReplayReaderParams>) {
72-
const {breadcrumbs, rrwebEvents, spans} = mapRRWebAttachments(attachments);
73+
const {rawBreadcrumbs, rawRRWebEvents, rawNetworkSpans, rawMemorySpans} =
74+
splitAttachmentsByType(attachments);
75+
76+
const spans = [...rawMemorySpans, rawNetworkSpans] as ReplaySpan[];
7377

7478
// TODO(replays): We should get correct timestamps from the backend instead
7579
// of having to fix them up here.
7680
const {startTimestampMs, endTimestampMs} = replayTimestamps(
7781
replayRecord,
78-
rrwebEvents,
79-
breadcrumbs,
82+
rawRRWebEvents as RecordingEvent[],
83+
rawBreadcrumbs as ReplayCrumb[],
8084
spans
8185
);
8286
replayRecord.started_at = new Date(startTimestampMs);
@@ -89,10 +93,13 @@ export default class ReplayReader {
8993
this.breadcrumbs = breadcrumbFactory(
9094
replayRecord,
9195
errors,
92-
breadcrumbs,
96+
rawBreadcrumbs as ReplayCrumb[],
9397
this.sortedSpans
9498
);
95-
this.rrwebEvents = rrwebEventListFactory(replayRecord, rrwebEvents);
99+
this.rrwebEvents = rrwebEventListFactory(
100+
replayRecord,
101+
rawRRWebEvents as RecordingEvent[]
102+
);
96103

97104
this.replayRecord = replayRecord;
98105
}
@@ -175,3 +182,11 @@ export default class ReplayReader {
175182
);
176183
});
177184
}
185+
186+
const isMemorySpan = (span: ReplaySpan): span is MemorySpan => {
187+
return span.op === 'memory';
188+
};
189+
190+
const isNetworkSpan = (span: ReplaySpan): span is NetworkSpan => {
191+
return span.op.startsWith('navigation.') || span.op.startsWith('resource.');
192+
};
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import splitAttachmentsByType from 'sentry/utils/replays/splitAttachmentsByType';
2+
3+
describe('splitAttachmentsByType', () => {
4+
const testPayload = [
5+
...TestStubs.ReplaySegmentInit({timestamp: new Date(1654290037123)}),
6+
...TestStubs.ReplaySegmentFullsnapshot({timestamp: new Date(1654290037124)}),
7+
...TestStubs.ReplaySegmentBreadcrumb({
8+
timestamp: new Date(1654290037267),
9+
payload: {
10+
type: 'default',
11+
category: 'ui.click',
12+
message: 'body > div#root > div.App > form',
13+
data: {nodeId: 44},
14+
},
15+
}),
16+
...TestStubs.ReplaySegmentSpan({
17+
timestamp: new Date(1654290034262),
18+
payload: TestStubs.ReplaySpanPayload({
19+
op: 'navigation.navigate',
20+
description: 'http://localhost:3000/',
21+
startTimestamp: new Date(1654290034262),
22+
endTimestamp: new Date(1654290034580),
23+
data: {size: 1150},
24+
}),
25+
}),
26+
...TestStubs.ReplaySegmentSpan({
27+
timestamp: new Date(1654290034262.3),
28+
payload: TestStubs.ReplaySpanPayload({
29+
op: 'navigation.navigate',
30+
description: 'http://localhost:3000/',
31+
startTimestamp: new Date(1654290034262.3),
32+
endTimestamp: new Date(1654290034580.8),
33+
data: {size: 1150},
34+
}),
35+
}),
36+
...TestStubs.ReplaySegmentSpan({
37+
timestamp: new Date(1654290034263),
38+
payload: TestStubs.ReplaySpanPayload({
39+
op: 'memory',
40+
description: 'memory',
41+
data: {
42+
jsHeapSizeLimit: 4294705152,
43+
totalJSHeapSize: 19203353,
44+
usedJSHeapSize: 16119217,
45+
},
46+
}),
47+
}),
48+
];
49+
50+
it('should split attachments by type', () => {
51+
const {rawBreadcrumbs, rawRRWebEvents, rawNetworkSpans, rawMemorySpans} =
52+
splitAttachmentsByType(testPayload);
53+
expect(rawBreadcrumbs.length).toBe(1);
54+
expect(rawRRWebEvents.length).toBe(4);
55+
expect(rawNetworkSpans.length).toBe(2);
56+
expect(rawMemorySpans.length).toBe(1);
57+
});
58+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
export default function splitAttachmentsByType(attachments: any[]) {
2+
const rawBreadcrumbs = [] as unknown[];
3+
const rawRRWebEvents = [] as unknown[];
4+
const rawNetworkSpans = [] as unknown[];
5+
const rawMemorySpans = [] as unknown[];
6+
7+
attachments.forEach(attachment => {
8+
if (attachment.data?.tag === 'performanceSpan') {
9+
const span = attachment.data?.payload;
10+
if (span.op === 'memory') {
11+
rawMemorySpans.push(span);
12+
}
13+
if (span.op.startsWith('navigation.') || span.op.startsWith('resource.')) {
14+
rawNetworkSpans.push(span);
15+
}
16+
} else if (attachment.data?.tag === 'breadcrumb') {
17+
rawBreadcrumbs.push(attachment?.data?.payload);
18+
} else {
19+
rawRRWebEvents.push(attachment);
20+
}
21+
});
22+
23+
return {
24+
rawBreadcrumbs,
25+
rawRRWebEvents,
26+
rawNetworkSpans,
27+
rawMemorySpans,
28+
};
29+
}

static/app/views/replays/detail/memoryChart.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ import {ReactEchartsRef, Series} from 'sentry/types/echarts';
1717
import {formatBytesBase2} from 'sentry/utils';
1818
import {getFormattedDate} from 'sentry/utils/dates';
1919
import FluidHeight from 'sentry/views/replays/detail/layout/fluidHeight';
20-
import type {MemorySpanType} from 'sentry/views/replays/types';
20+
import type {MemorySpan} from 'sentry/views/replays/types';
2121

2222
interface Props {
23-
memorySpans: undefined | MemorySpanType[];
23+
memorySpans: undefined | MemorySpan[];
2424
setCurrentHoverTime: (time: undefined | number) => void;
2525
setCurrentTime: (time: number) => void;
2626
startTimestampMs: undefined | number;

static/app/views/replays/types.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export interface ReplaySpan<T = Record<string, any>> {
181181
description?: string;
182182
}
183183

184-
export type MemorySpanType = ReplaySpan<{
184+
export type MemorySpan = ReplaySpan<{
185185
memory: {
186186
jsHeapSizeLimit: number;
187187
totalJSHeapSize: number;

0 commit comments

Comments
 (0)