Skip to content

Commit c1351b3

Browse files
committed
fix(replay): Ensure initial event is compressed
1 parent 2964546 commit c1351b3

File tree

5 files changed

+40
-14
lines changed

5 files changed

+40
-14
lines changed

packages/replay/src/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,8 @@ export const MASK_ALL_TEXT_SELECTOR = 'body *:not(style), body *:not(script)';
2727
export const DEFAULT_FLUSH_MIN_DELAY = 5_000;
2828
export const DEFAULT_FLUSH_MAX_DELAY = 5_000;
2929

30+
/* How long to wait for error checkouts */
31+
export const ERROR_CHECKOUT_TIME = 60_000;
32+
3033
export const RETRY_BASE_INTERVAL = 5000;
3134
export const RETRY_MAX_COUNT = 3;

packages/replay/src/eventBuffer/EventBufferProxy.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export class EventBufferProxy implements EventBuffer {
2020
this._compression = new EventBufferCompressionWorker(worker);
2121
this._used = this._fallback;
2222

23-
void this._ensureWorkerIsLoaded();
23+
void this.ensureWorkerIsLoaded();
2424
}
2525

2626
/** @inheritDoc */
@@ -54,7 +54,7 @@ export class EventBufferProxy implements EventBuffer {
5454
}
5555

5656
/** Ensure the worker has loaded. */
57-
private async _ensureWorkerIsLoaded(): Promise<void> {
57+
public async ensureWorkerIsLoaded(): Promise<void> {
5858
try {
5959
await this._compression.ensureReady();
6060
} catch (error) {

packages/replay/src/replay.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,16 @@ import type { RateLimits } from '@sentry/utils';
55
import { disabledUntil, logger } from '@sentry/utils';
66
import { EventType, record } from 'rrweb';
77

8-
import { MAX_SESSION_LIFE, SESSION_IDLE_DURATION, VISIBILITY_CHANGE_TIMEOUT, WINDOW } from './constants';
8+
import {
9+
ERROR_CHECKOUT_TIME,
10+
MAX_SESSION_LIFE,
11+
SESSION_IDLE_DURATION,
12+
VISIBILITY_CHANGE_TIMEOUT,
13+
WINDOW,
14+
} from './constants';
915
import { setupPerformanceObserver } from './coreHandlers/performanceObserver';
1016
import { createEventBuffer } from './eventBuffer';
17+
import { EventBufferProxy } from './eventBuffer/EventBufferProxy';
1118
import { getSession } from './session/getSession';
1219
import { saveSession } from './session/saveSession';
1320
import type {
@@ -196,7 +203,7 @@ export class ReplayContainer implements ReplayContainerInterface {
196203
// When running in error sampling mode, we need to overwrite `checkoutEveryNms`
197204
// Without this, it would record forever, until an error happens, which we don't want
198205
// instead, we'll always keep the last 60 seconds of replay before an error happened
199-
...(this.recordingMode === 'error' && { checkoutEveryNms: 60000 }),
206+
...(this.recordingMode === 'error' && { checkoutEveryNms: ERROR_CHECKOUT_TIME }),
200207
emit: this._handleRecordingEmit,
201208
});
202209
} catch (err) {
@@ -323,9 +330,18 @@ export class ReplayContainer implements ReplayContainerInterface {
323330
* from calling both `flush` and `_debouncedFlush`. Otherwise, there could be
324331
* cases of mulitple flushes happening closely together.
325332
*/
326-
public flushImmediate(): Promise<void> {
333+
public async flushImmediate(waitForCompression?: boolean): Promise<void> {
327334
this._debouncedFlush();
328335
// `.flush` is provided by the debounced function, analogously to lodash.debounce
336+
337+
// Ensure the worker is loaded, so the sent event is compressed
338+
if (waitForCompression && this.eventBuffer instanceof EventBufferProxy) {
339+
try {
340+
await this.eventBuffer.ensureWorkerIsLoaded();
341+
} catch (error) {
342+
// If this fails, we'll just send uncompressed events
343+
}
344+
}
329345
return this._debouncedFlush.flush() as Promise<void>;
330346
}
331347

@@ -536,7 +552,8 @@ export class ReplayContainer implements ReplayContainerInterface {
536552
// replays (e.g. opening and closing a tab quickly), but these can be
537553
// filtered on the UI.
538554
if (this.recordingMode === 'session') {
539-
void this.flushImmediate();
555+
// We want to ensure the worker is ready, as otherwise we'd always send the first event uncompressed
556+
void this.flushImmediate(true);
540557
}
541558

542559
return true;

packages/replay/test/integration/errorSampleRate.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { captureException } from '@sentry/core';
22

3-
import { DEFAULT_FLUSH_MIN_DELAY, REPLAY_SESSION_KEY, VISIBILITY_CHANGE_TIMEOUT, WINDOW } from '../../src/constants';
3+
import {
4+
DEFAULT_FLUSH_MIN_DELAY,
5+
ERROR_CHECKOUT_TIME,
6+
REPLAY_SESSION_KEY,
7+
VISIBILITY_CHANGE_TIMEOUT,
8+
WINDOW,
9+
} from '../../src/constants';
410
import type { ReplayContainer } from '../../src/replay';
511
import { addEvent } from '../../src/util/addEvent';
612
import { PerformanceEntryResource } from '../fixtures/performanceEntry/resource';
@@ -322,7 +328,7 @@ describe('Integration | errorSampleRate', () => {
322328
});
323329

324330
it('has correct timestamps when error occurs much later than initial pageload/checkout', async () => {
325-
const ELAPSED = 60000;
331+
const ELAPSED = ERROR_CHECKOUT_TIME;
326332
const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 3 };
327333
mockRecord._emitter(TEST_EVENT);
328334

packages/replay/test/unit/eventBuffer.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe('Unit | eventBuffer', () => {
5858
expect(buffer).toBeInstanceOf(EventBufferProxy);
5959

6060
// Ensure worker is ready
61-
await buffer['_ensureWorkerIsLoaded']();
61+
await buffer.ensureWorkerIsLoaded();
6262

6363
buffer.addEvent(TEST_EVENT);
6464
buffer.addEvent(TEST_EVENT);
@@ -80,7 +80,7 @@ describe('Unit | eventBuffer', () => {
8080
expect(buffer).toBeInstanceOf(EventBufferProxy);
8181

8282
// Ensure worker is ready
83-
await buffer['_ensureWorkerIsLoaded']();
83+
await buffer.ensureWorkerIsLoaded();
8484

8585
await buffer.addEvent(TEST_EVENT);
8686
await buffer.addEvent(TEST_EVENT);
@@ -103,7 +103,7 @@ describe('Unit | eventBuffer', () => {
103103
expect(buffer).toBeInstanceOf(EventBufferProxy);
104104

105105
// Ensure worker is ready
106-
await buffer['_ensureWorkerIsLoaded']();
106+
await buffer.ensureWorkerIsLoaded();
107107

108108
buffer.addEvent(TEST_EVENT);
109109

@@ -127,7 +127,7 @@ describe('Unit | eventBuffer', () => {
127127
expect(buffer).toBeInstanceOf(EventBufferProxy);
128128

129129
// Ensure worker is ready
130-
await buffer['_ensureWorkerIsLoaded']();
130+
await buffer.ensureWorkerIsLoaded();
131131

132132
buffer.addEvent(TEST_EVENT);
133133

@@ -177,7 +177,7 @@ describe('Unit | eventBuffer', () => {
177177
expect(result).toEqual(JSON.stringify([TEST_EVENT, TEST_EVENT]));
178178

179179
// Now actually finish loading the worker
180-
await buffer['_ensureWorkerIsLoaded']();
180+
await buffer.ensureWorkerIsLoaded();
181181

182182
buffer.addEvent(TEST_EVENT);
183183
buffer.addEvent(TEST_EVENT);
@@ -211,7 +211,7 @@ describe('Unit | eventBuffer', () => {
211211
expect(result).toEqual(JSON.stringify([TEST_EVENT, TEST_EVENT]));
212212

213213
// Now actually finish loading the worker - which triggers an error
214-
await buffer['_ensureWorkerIsLoaded']();
214+
await buffer.ensureWorkerIsLoaded();
215215

216216
buffer.addEvent(TEST_EVENT);
217217
buffer.addEvent(TEST_EVENT);

0 commit comments

Comments
 (0)