Просмотр исходного кода

feat(replay): Only use `replayerStepper` when finished fetching replay data (#79565)

Previously, a new ReplayReader instance would be created after every page of recording segments finishes. This meant that replays with hundreds of segments, a new `ReplayReader` would be constructed `<# of segments> / 100` times (corresponds to the number of recording segments pages requests from the API). With the DOM nodes chart, this was made 2x as bad (but that is gone now)!

We now pass the "fetching" state from `useReplayData` into `ReplayReader` and do not initialize `replayerStepper` until fetching is completed so that it is only called once.

This should fix  https://github.com/getsentry/sentry/issues/77809
Billy Vong 4 месяцев назад
Родитель
Сommit
66a70aaa86

+ 1 - 0
static/app/components/events/eventReplay/index.spec.tsx

@@ -64,6 +64,7 @@ const mockReplay = ReplayReader.factory({
     },
   }),
   errors: mockErrors,
+  fetching: false,
   attachments: RRWebInitFrameEventsFixture({
     timestamp: new Date('Sep 22, 2022 4:58:39 PM UTC'),
   }),

+ 1 - 0
static/app/components/events/eventReplay/replayClipPreview.spec.tsx

@@ -38,6 +38,7 @@ const mockReplay = ReplayReader.factory({
     duration: duration(10, 'seconds'),
   }),
   errors: [],
+  fetching: false,
   attachments: RRWebInitFrameEventsFixture({
     timestamp: new Date('Sep 22, 2022 4:58:39 PM UTC'),
   }),

+ 1 - 0
static/app/components/events/eventReplay/replayPreview.spec.tsx

@@ -43,6 +43,7 @@ const mockReplay = ReplayReader.factory({
     },
   }),
   errors: [],
+  fetching: false,
   attachments: RRWebInitFrameEventsFixture({
     timestamp: new Date('Sep 22, 2022 4:58:39 PM UTC'),
   }),

+ 2 - 0
static/app/utils/replays/getDiffTimestamps.spec.tsx

@@ -67,6 +67,7 @@ function getMockReplay(rrwebEvents: any[], errors: ReplayError[]) {
   const replay = ReplayReader.factory({
     replayRecord,
     errors,
+    fetching: false,
     attachments,
   });
 
@@ -97,6 +98,7 @@ function getMockReplayWithCrumbFrame(
   const replay = ReplayReader.factory({
     replayRecord,
     errors,
+    fetching: false,
     attachments,
   });
 

+ 4 - 2
static/app/utils/replays/hooks/useReplayReader.tsx

@@ -28,7 +28,7 @@ export default function useReplayReader({
 }: Props): ReplayReaderResult {
   const replayId = parseReplayId(replaySlug);
 
-  const {attachments, errors, replayRecord, ...replayData} = useReplayData({
+  const {attachments, errors, replayRecord, fetching, ...replayData} = useReplayData({
     orgSlug,
     replayId,
   });
@@ -63,15 +63,17 @@ export default function useReplayReader({
         clipWindow: memoizedClipWindow,
         errors,
         featureFlags,
+        fetching,
         replayRecord,
       }),
-    [attachments, memoizedClipWindow, errors, featureFlags, replayRecord]
+    [attachments, memoizedClipWindow, errors, featureFlags, fetching, replayRecord]
   );
 
   return {
     ...replayData,
     attachments,
     errors,
+    fetching,
     replay,
     replayId,
     replayRecord,

+ 2 - 0
static/app/utils/replays/playback/providers/replayPlayerEventsContext.spec.tsx

@@ -24,6 +24,7 @@ describe('replayPlayerEventsContext', () => {
     const mockReplay = ReplayReader.factory({
       attachments: [],
       errors: [],
+      fetching: false,
       replayRecord: ReplayRecordFixture(),
     });
 
@@ -42,6 +43,7 @@ describe('replayPlayerEventsContext', () => {
     const mockReplay = ReplayReader.factory({
       attachments: [],
       errors: [],
+      fetching: false,
       replayRecord: ReplayRecordFixture(),
     });
     const mockRRwebFrames: any[] = [];

+ 12 - 0
static/app/utils/replays/replayReader.spec.tsx

@@ -32,6 +32,7 @@ describe('ReplayReader', () => {
     const missingAttachments = ReplayReader.factory({
       attachments: undefined,
       errors: [],
+      fetching: false,
       replayRecord,
     });
     expect(missingAttachments).toBeNull();
@@ -39,6 +40,7 @@ describe('ReplayReader', () => {
     const missingErrors = ReplayReader.factory({
       attachments: [],
       errors: undefined,
+      fetching: false,
       replayRecord,
     });
     expect(missingErrors).toBeNull();
@@ -46,6 +48,7 @@ describe('ReplayReader', () => {
     const missingRecord = ReplayReader.factory({
       attachments: [],
       errors: [],
+      fetching: false,
       replayRecord: undefined,
     });
     expect(missingRecord).toBeNull();
@@ -61,6 +64,7 @@ describe('ReplayReader', () => {
         ReplayConsoleEventFixture({timestamp: minuteTen}),
       ],
       errors: [],
+      fetching: false,
       replayRecord: ReplayRecordFixture({
         started_at: new Date('2023-12-25T00:01:00'),
         finished_at: new Date('2023-12-25T00:09:00'),
@@ -79,6 +83,7 @@ describe('ReplayReader', () => {
     const replay = ReplayReader.factory({
       attachments: [],
       errors: [],
+      fetching: false,
       replayRecord,
     });
 
@@ -211,6 +216,7 @@ describe('ReplayReader', () => {
       const replay = ReplayReader.factory({
         attachments,
         errors: [],
+        fetching: false,
         replayRecord,
       });
 
@@ -231,6 +237,7 @@ describe('ReplayReader', () => {
         }),
       ],
       errors: [],
+      fetching: false,
       replayRecord,
     });
 
@@ -253,6 +260,7 @@ describe('ReplayReader', () => {
           }),
         ],
         errors: [],
+        fetching: false,
         replayRecord,
       });
 
@@ -292,6 +300,7 @@ describe('ReplayReader', () => {
           }),
         ],
         errors: [],
+        fetching: false,
         replayRecord,
       });
 
@@ -320,6 +329,7 @@ describe('ReplayReader', () => {
     const replay = ReplayReader.factory({
       attachments,
       errors: [],
+      fetching: false,
       replayRecord,
     });
 
@@ -352,6 +362,7 @@ describe('ReplayReader', () => {
     const replay = ReplayReader.factory({
       attachments: [snapshot, increment],
       errors: [],
+      fetching: false,
       replayRecord,
     });
 
@@ -426,6 +437,7 @@ describe('ReplayReader', () => {
         breadcrumbAttachment3,
       ],
       errors: [error1, error2, error3],
+      fetching: false,
       replayRecord: ReplayRecordFixture({
         started_at: replayStartedAt,
         finished_at: replayFinishedAt,

+ 14 - 0
static/app/utils/replays/replayReader.tsx

@@ -65,6 +65,11 @@ interface ReplayReaderParams {
    */
   errors: ReplayError[] | undefined;
 
+  /**
+   * Is replay data still fetching?
+   */
+  fetching: boolean;
+
   /**
    * The root Replay event, created at the start of the browser session.
    */
@@ -168,6 +173,7 @@ export default class ReplayReader {
     replayRecord,
     clipWindow,
     featureFlags,
+    fetching,
   }: ReplayReaderParams) {
     if (!attachments || !replayRecord || !errors) {
       return null;
@@ -179,6 +185,7 @@ export default class ReplayReader {
         errors,
         replayRecord,
         featureFlags,
+        fetching,
         clipWindow,
       });
     } catch (err) {
@@ -192,6 +199,7 @@ export default class ReplayReader {
         attachments: [],
         errors: [],
         featureFlags,
+        fetching,
         replayRecord,
         clipWindow,
       });
@@ -202,10 +210,12 @@ export default class ReplayReader {
     attachments,
     errors,
     featureFlags,
+    fetching,
     replayRecord,
     clipWindow,
   }: RequiredNotNull<ReplayReaderParams>) {
     this._cacheKey = domId('replayReader-');
+    this._fetching = fetching;
 
     if (replayRecord.is_archived) {
       this._replayRecord = replayRecord;
@@ -305,6 +315,7 @@ export default class ReplayReader {
   private _duration: Duration = duration(0);
   private _errors: ErrorFrame[] = [];
   private _featureFlags: string[] | undefined = [];
+  private _fetching: boolean = true;
   private _optionFrame: undefined | OptionFrame;
   private _replayRecord: ReplayRecord;
   private _sortedBreadcrumbFrames: BreadcrumbFrame[] = [];
@@ -437,6 +448,9 @@ export default class ReplayReader {
   };
 
   getExtractDomNodes = memoize(async () => {
+    if (this._fetching) {
+      return null;
+    }
     const {onVisitFrame, shouldVisitFrame} = extractDomNodes;
 
     const results = await replayerStepper({

+ 1 - 0
static/app/views/issueDetails/groupReplays/groupReplays.spec.tsx

@@ -63,6 +63,7 @@ const mockReplay = ReplayReader.factory({
     duration: duration(10, 'seconds'),
   }),
   errors: [],
+  fetching: false,
   attachments: RRWebInitFrameEventsFixture({
     timestamp: new Date('Sep 22, 2022 4:58:39 PM UTC'),
   }),

+ 1 - 0
static/app/views/replays/detail/tagPanel/index.spec.tsx

@@ -18,6 +18,7 @@ const mockReplay = ReplayReader.factory({
     },
   }),
   errors: [],
+  fetching: false,
   attachments: [],
 });