11/* eslint-disable @typescript-eslint/no-unsafe-member-access */
22// TODO: figure out member access types and remove the line above
33
4- import { captureException } from '@sentry/core ' ;
4+ import type { ReplayRecordingData } from '@sentry/types ' ;
55import { logger } from '@sentry/utils' ;
66
77import type { AddEventResult , EventBuffer , RecordingEvent , WorkerRequest } from './types' ;
@@ -20,24 +20,90 @@ export function createEventBuffer({ useCompression }: CreateEventBufferParams):
2020 const workerBlob = new Blob ( [ workerString ] ) ;
2121 const workerUrl = URL . createObjectURL ( workerBlob ) ;
2222
23- try {
24- __DEBUG_BUILD__ && logger . log ( '[Replay] Using compression worker' ) ;
25- const worker = new Worker ( workerUrl ) ;
26- if ( worker ) {
27- return new EventBufferCompressionWorker ( worker ) ;
28- } else {
29- captureException ( new Error ( 'Unable to create compression worker' ) ) ;
30- }
31- } catch {
32- // catch and ignore, fallback to simple event buffer
33- }
34- __DEBUG_BUILD__ && logger . log ( '[Replay] Falling back to simple event buffer' ) ;
23+ __DEBUG_BUILD__ && logger . log ( '[Replay] Using compression worker' ) ;
24+ const worker = new Worker ( workerUrl ) ;
25+ return new EventBufferProxy ( worker ) ;
3526 }
3627
3728 __DEBUG_BUILD__ && logger . log ( '[Replay] Using simple buffer' ) ;
3829 return new EventBufferArray ( ) ;
3930}
4031
32+ /**
33+ * This proxy will try to use the compression worker, and fall back to use the simple buffer if an error occurs there.
34+ * This can happen e.g. if the worker cannot be loaded.
35+ * Exported only for testing.
36+ */
37+ export class EventBufferProxy implements EventBuffer {
38+ private _fallback : EventBufferArray ;
39+ private _compression : EventBufferCompressionWorker ;
40+ private _used : EventBuffer ;
41+
42+ public constructor ( worker : Worker ) {
43+ this . _fallback = new EventBufferArray ( ) ;
44+ this . _compression = new EventBufferCompressionWorker ( worker ) ;
45+ this . _used = this . _fallback ;
46+
47+ void this . _ensureWorkerIsLoaded ( ) ;
48+ }
49+
50+ /** @inheritDoc */
51+ public get pendingLength ( ) : number {
52+ return this . _used . pendingLength ;
53+ }
54+
55+ /** @inheritDoc */
56+ public get pendingEvents ( ) : RecordingEvent [ ] {
57+ return this . _used . pendingEvents ;
58+ }
59+
60+ /** @inheritDoc */
61+ public destroy ( ) : void {
62+ return this . _fallback . destroy ( ) ;
63+ return this . _compression . destroy ( ) ;
64+ }
65+
66+ /**
67+ * Add an event to the event buffer.
68+ *
69+ * Returns true if event was successfully added.
70+ */
71+ public addEvent ( event : RecordingEvent , isCheckout ?: boolean ) : Promise < AddEventResult > {
72+ return this . _used . addEvent ( event , isCheckout ) ;
73+ }
74+
75+ /** @inheritDoc */
76+ public finish ( ) : Promise < ReplayRecordingData > {
77+ return this . _used . finish ( ) ;
78+ }
79+
80+ /** Ensure the worker has loaded. */
81+ private async _ensureWorkerIsLoaded ( ) : Promise < void > {
82+ try {
83+ await this . _compression . ensureReady ( ) ;
84+ } catch ( error ) {
85+ // If the worker fails to load, we fall back to the simple buffer.
86+ // Nothing more to do from our side here
87+ __DEBUG_BUILD__ && logger . log ( '[Replay] Failed to load the compression worker, falling back to simple buffer' ) ;
88+ return ;
89+ }
90+
91+ // Compression worker is ready, we can use it
92+ // Now we need to switch over the array buffer to the compression worker
93+ const addEventPromises : Promise < void > [ ] = [ ] ;
94+ for ( const event of this . _fallback . pendingEvents ) {
95+ addEventPromises . push ( this . _compression . addEvent ( event ) ) ;
96+ }
97+
98+ // We switch over to the compression buffer immediately - any further events will be added
99+ // after the previously buffered ones
100+ this . _used = this . _compression ;
101+
102+ // Wait for original events to be re-added before resolving
103+ await Promise . all ( addEventPromises ) ;
104+ }
105+ }
106+
41107class EventBufferArray implements EventBuffer {
42108 private _events : RecordingEvent [ ] ;
43109
@@ -119,6 +185,31 @@ export class EventBufferCompressionWorker implements EventBuffer {
119185 return this . _pendingEvents ;
120186 }
121187
188+ /**
189+ * Ensure the worker is ready (or not).
190+ * This will either resolve when the worker is ready, or reject if an error occured.
191+ */
192+ public ensureReady ( ) : Promise < void > {
193+ return new Promise ( ( resolve , reject ) => {
194+ this . _worker . addEventListener (
195+ 'message' ,
196+ ( ) => {
197+ // We really don't care what is sent here, any message means it has loaded
198+ resolve ( ) ;
199+ } ,
200+ { once : true } ,
201+ ) ;
202+
203+ this . _worker . addEventListener (
204+ 'error' ,
205+ error => {
206+ reject ( error ) ;
207+ } ,
208+ { once : true } ,
209+ ) ;
210+ } ) ;
211+ }
212+
122213 /**
123214 * Destroy the event buffer.
124215 */
0 commit comments