Skip to content

Commit e4abf10

Browse files
authored
ref(replay): Accept replay attachments in-order so we do not have to manually sort later (#50901)
This is a followup from #50707 Fixes #50891
1 parent 5caebfd commit e4abf10

File tree

5 files changed

+11
-21
lines changed

5 files changed

+11
-21
lines changed

static/app/components/replays/replayContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ export function Provider({
222222
}: Props) {
223223
const config = useLegacyStore(ConfigStore);
224224
const organization = useOrganization();
225-
const events = replay?.getRRWebEvents();
225+
const events = replay?.getRRWebFrames();
226226
const savedReplayConfigRef = useRef<ReplayConfig>(
227227
JSON.parse(localStorage.getItem(ReplayLocalstorageKeys.REPLAY_CONFIG) || '{}')
228228
);

static/app/utils/replays/hooks/useReplayData.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {useCallback, useEffect, useMemo, useState} from 'react';
1+
import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
22
import * as Sentry from '@sentry/react';
33

44
import {Client} from 'sentry/api';
@@ -104,6 +104,7 @@ function useReplayData({
104104

105105
const [state, setState] = useState<State>(INITIAL_STATE);
106106
const [attachments, setAttachments] = useState<unknown[]>([]);
107+
const attachmentMap = useRef<Map<string, unknown[]>>(new Map()); // Map keys are always iterated by insertion order
107108
const [errors, setErrors] = useState<ReplayError[]>([]);
108109
const [replayRecord, setReplayRecord] = useState<ReplayRecord>();
109110

@@ -134,6 +135,7 @@ function useReplayData({
134135

135136
const pages = Math.ceil(replayRecord.count_segments / segmentsPerPage);
136137
const cursors = new Array(pages).fill(0).map((_, i) => `0:${segmentsPerPage * i}:0`);
138+
cursors.forEach(cursor => attachmentMap.current.set(cursor, []));
137139

138140
await Promise.allSettled(
139141
cursors.map(cursor => {
@@ -148,7 +150,9 @@ function useReplayData({
148150
}
149151
);
150152
promise.then(response => {
151-
setAttachments(prev => (prev ?? []).concat(...response));
153+
attachmentMap.current.set(cursor, response);
154+
const flattened = Array.from(attachmentMap.current.values()).flat(2);
155+
setAttachments(flattened);
152156
});
153157
return promise;
154158
})

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ describe('ReplayReader', () => {
115115

116116
it.each([
117117
{
118-
method: 'getSortedRRWebFrames',
118+
method: 'getRRWebFrames',
119119
expected: [
120120
{
121121
type: EventType.Custom,

static/app/utils/replays/replayReader.tsx

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,9 @@ export default class ReplayReader {
132132

133133
// Insert extra records to satisfy minimum requirements for the UI
134134
this._breadcrumbFrames.push(replayInitBreadcrumb(replayRecord));
135-
this._rrwebEvents.push(recordingStartFrame(replayRecord));
135+
this._rrwebEvents.unshift(recordingStartFrame(replayRecord));
136136
this._rrwebEvents.push(recordingEndFrame(replayRecord));
137137

138-
// Sort what needs sorting
139-
// TODO(replay): We could remove this sort call if useReplayData was more
140-
// careful about maintaining cursor order when calling setAttachments()
141-
this._rrwebEvents.sort((a, b) => a.timestamp - b.timestamp);
142-
143138
/*********************/
144139
/** OLD STUFF BELOW **/
145140
/*********************/
@@ -206,10 +201,6 @@ export default class ReplayReader {
206201

207202
getRRWebFrames = () => this._rrwebEvents;
208203

209-
getSortedRRWebFrames = memoize(() =>
210-
this.getRRWebFrames().sort((a, b) => a.timestamp - b.timestamp)
211-
);
212-
213204
getConsoleFrames = memoize(() =>
214205
this._breadcrumbFrames.filter(frame => frame.category === 'console')
215206
);
@@ -265,11 +256,6 @@ export default class ReplayReader {
265256
/*********************/
266257
/** OLD STUFF BELOW **/
267258
/*********************/
268-
269-
getRRWebEvents = () => {
270-
return this.rrwebEvents;
271-
};
272-
273259
getCrumbsWithRRWebNodes = memoize(() =>
274260
this.breadcrumbs.filter(
275261
crumb => crumb.data && typeof crumb.data === 'object' && 'nodeId' in crumb.data
@@ -314,7 +300,7 @@ export default class ReplayReader {
314300
getDomNodes = memoize(() =>
315301
extractDomNodes({
316302
crumbs: this.getCrumbsWithRRWebNodes(),
317-
rrwebEvents: this.getRRWebEvents(),
303+
rrwebEvents: this.getRRWebFrames(),
318304
finishedAt: this.replayRecord.finished_at,
319305
})
320306
);

static/app/views/replays/details.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function ReplayDetails({params: {replaySlug}}: Props) {
110110
);
111111
}
112112

113-
if (!fetching && replay && replay.getRRWebEvents().length < 2) {
113+
if (!fetching && replay && replay.getRRWebFrames().length < 2) {
114114
return (
115115
<Page
116116
orgSlug={orgSlug}

0 commit comments

Comments
 (0)