@@ -625,7 +625,7 @@ export class ReplayContainer implements ReplayContainerInterface {
625
625
// Send replay when the page/tab becomes hidden. There is no reason to send
626
626
// replay if it becomes visible, since no actions we care about were done
627
627
// while it was hidden
628
- this . _conditionalFlush ( { finishImmediate : true } ) ;
628
+ this . _conditionalFlush ( { sync : true } ) ;
629
629
}
630
630
631
631
/**
@@ -756,8 +756,8 @@ export class ReplayContainer implements ReplayContainerInterface {
756
756
* Page is likely to unload so need to bypass debounce completely and
757
757
* synchronously retrieve pending events from buffer and send request asap.
758
758
*/
759
- if ( options . finishImmediate ) {
760
- void this . _runFlush ( options ) ;
759
+ if ( options . sync ) {
760
+ this . _flushSync ( ) ;
761
761
return ;
762
762
}
763
763
@@ -804,81 +804,143 @@ export class ReplayContainer implements ReplayContainerInterface {
804
804
*
805
805
* Should never be called directly, only by `flush`
806
806
*/
807
- private async _runFlush ( options : FlushOptions = { } ) : Promise < void > {
808
- if ( ! this . session || ! this . eventBuffer ) {
809
- __DEBUG_BUILD__ && logger . error ( '[Replay] No session or eventBuffer found to flush.' ) ;
810
- return ;
811
- }
812
-
807
+ private async _runFlush ( ) : Promise < void > {
813
808
try {
814
- this . _debouncedFlush . cancel ( ) ;
809
+ const flushData = this . _prepareFlush ( ) ;
810
+
811
+ if ( ! flushData ) {
812
+ return ;
813
+ }
815
814
816
- const promises : Promise < any > [ ] = [ ] ;
815
+ const { promises, replayId , segmentId , eventContext , eventBuffer , session } = flushData ;
817
816
818
- promises . push ( this . _addPerformanceEntries ( ) ) ;
817
+ // NOTE: Be mindful that nothing after this point (the first `await`)
818
+ // will run after when the page is unloaded.
819
+ await Promise . all ( promises ) ;
819
820
820
- // Do not continue if there are no pending events in buffer
821
- if ( ! this . eventBuffer || ! this . eventBuffer . pendingLength ) {
821
+ // This can be empty due to blur events calling `runFlush` directly. In
822
+ // the case where we have a snapshot checkout and a blur event
823
+ // happening near the same time, the blur event can end up emptying the
824
+ // buffer even if snapshot happens first.
825
+ if ( ! eventBuffer . pendingLength ) {
822
826
return ;
823
827
}
824
828
825
- // Only attach memory entry if eventBuffer is not empty
826
- promises . push ( addMemoryEntry ( this ) ) ;
829
+ // This empties the event buffer regardless of outcome of sending replay
830
+ const recordingData = await eventBuffer . finish ( ) ;
827
831
828
- // NOTE: Copy values from instance members, as it's possible they could
829
- // change before the flush finishes.
830
- const replayId = this . session . id ;
831
- const eventContext = this . _popEventContext ( ) ;
832
- // Always increment segmentId regardless of outcome of sending replay
833
- const segmentId = this . session . segmentId ++ ;
834
-
835
- // Save session (new segment id) after we save flush data assuming either
836
- // 1) request succeeds or 2) it fails or never happens, in which case we
837
- // need to retry this segment.
838
- this . _maybeSaveSession ( ) ;
832
+ await sendReplay ( {
833
+ replayId,
834
+ recordingData,
835
+ segmentId,
836
+ includeReplayStartTimestamp : segmentId === 0 ,
837
+ eventContext,
838
+ session,
839
+ options : this . getOptions ( ) ,
840
+ timestamp : new Date ( ) . getTime ( ) ,
841
+ } ) ;
842
+ } catch ( err ) {
843
+ this . _handleSendError ( err ) ;
844
+ }
845
+ }
839
846
840
- let recordingData : ReplayRecordingData ;
847
+ /**
848
+ * Flush event buffer synchonously.
849
+ * This is necessary e.g. when running flush on page unload or similar.
850
+ */
851
+ private _flushSync ( ) : void {
852
+ try {
853
+ const flushData = this . _prepareFlush ( ) ;
841
854
842
- if ( options . finishImmediate && this . eventBuffer . pendingLength ) {
843
- recordingData = this . eventBuffer . finishImmediate ( ) ;
844
- } else {
845
- // NOTE: Be mindful that nothing after this point (the first `await`)
846
- // will run after when the page is unloaded.
847
- await Promise . all ( promises ) ;
848
-
849
- // This can be empty due to blur events calling `runFlush` directly. In
850
- // the case where we have a snapshot checkout and a blur event
851
- // happening near the same time, the blur event can end up emptying the
852
- // buffer even if snapshot happens first.
853
- if ( ! this . eventBuffer . pendingLength ) {
854
- return ;
855
- }
856
- // This empties the event buffer regardless of outcome of sending replay
857
- recordingData = await this . eventBuffer . finish ( ) ;
855
+ if ( ! flushData ) {
856
+ return ;
858
857
}
859
858
860
- await sendReplay ( {
859
+ const { replayId, segmentId, eventContext, eventBuffer, session } = flushData ;
860
+
861
+ const recordingData = eventBuffer . finishSync ( ) ;
862
+
863
+ sendReplay ( {
861
864
replayId,
862
865
recordingData,
863
866
segmentId,
864
867
includeReplayStartTimestamp : segmentId === 0 ,
865
868
eventContext,
866
- session : this . session ,
869
+ session,
867
870
options : this . getOptions ( ) ,
868
871
timestamp : new Date ( ) . getTime ( ) ,
872
+ } ) . catch ( err => {
873
+ this . _handleSendError ( err ) ;
869
874
} ) ;
870
875
} catch ( err ) {
871
- this . _handleException ( err ) ;
876
+ this . _handleSendError ( err ) ;
877
+ }
878
+ }
872
879
873
- if ( err instanceof RateLimitError ) {
874
- this . _handleRateLimit ( err . rateLimits ) ;
875
- return ;
880
+ /** Prepare flush data */
881
+ private _prepareFlush ( ) :
882
+ | {
883
+ replayId : string ;
884
+ eventContext : PopEventContext ;
885
+ segmentId : number ;
886
+ promises : Promise < unknown > [ ] ;
887
+ eventBuffer : EventBuffer ;
888
+ session : Session ;
876
889
}
890
+ | undefined {
891
+ if ( ! this . session || ! this . eventBuffer ) {
892
+ __DEBUG_BUILD__ && logger . error ( '[Replay] No session or eventBuffer found to flush.' ) ;
893
+ return ;
894
+ }
877
895
878
- // This means we retried 3 times, and all of them failed
879
- // In this case, we want to completely stop the replay - otherwise, we may get inconsistent segments
880
- this . stop ( ) ;
896
+ this . _debouncedFlush . cancel ( ) ;
897
+
898
+ const promises : Promise < unknown > [ ] = [ ] ;
899
+
900
+ promises . push ( this . _addPerformanceEntries ( ) ) ;
901
+
902
+ // Do not continue if there are no pending events in buffer
903
+ if ( ! this . eventBuffer || ! this . eventBuffer . pendingLength ) {
904
+ return ;
881
905
}
906
+
907
+ // Only attach memory entry if eventBuffer is not empty
908
+ promises . push ( addMemoryEntry ( this ) ) ;
909
+
910
+ // NOTE: Copy values from instance members, as it's possible they could
911
+ // change before the flush finishes.
912
+ const replayId = this . session . id ;
913
+ const eventContext = this . _popEventContext ( ) ;
914
+ // Always increment segmentId regardless of outcome of sending replay
915
+ const segmentId = this . session . segmentId ++ ;
916
+
917
+ // Save session (new segment id) after we save flush data assuming either
918
+ // 1) request succeeds or 2) it fails or never happens, in which case we
919
+ // need to retry this segment.
920
+ this . _maybeSaveSession ( ) ;
921
+
922
+ return {
923
+ replayId,
924
+ eventContext,
925
+ segmentId,
926
+ promises,
927
+ eventBuffer : this . eventBuffer ,
928
+ session : this . session ,
929
+ } ;
930
+ }
931
+
932
+ /** Handle an error when sending a replay. */
933
+ private _handleSendError ( error : unknown ) : void {
934
+ this . _handleException ( error ) ;
935
+
936
+ if ( error instanceof RateLimitError ) {
937
+ this . _handleRateLimit ( error . rateLimits ) ;
938
+ return ;
939
+ }
940
+
941
+ // This means we retried 3 times, and all of them failed
942
+ // In this case, we want to completely stop the replay - otherwise, we may get inconsistent segments
943
+ this . stop ( ) ;
882
944
}
883
945
884
946
/**
0 commit comments