Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion packages/replay/src/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,15 @@ export class ReplayContainer implements ReplayContainerInterface {
// Note this empties the event buffer regardless of outcome of sending replay
const recordingData = await this.eventBuffer.finish();

const timestamp = Date.now();

// Check total duration again, to avoid sending outdated stuff
// We leave 30s wiggle room to accomodate late flushing etc.
// This _could_ happen when the browser is suspended during flushing, in which case we just want to stop
if (timestamp - this._context.initialTimestamp > this.timeouts.maxSessionLife + 30_000) {
throw new Error('Session is too long, not sending replay');
}

// NOTE: Copy values from instance members, as it's possible they could
// change before the flush finishes.
const replayId = this.session.id;
Expand All @@ -1082,7 +1091,7 @@ export class ReplayContainer implements ReplayContainerInterface {
eventContext,
session: this.session,
options: this.getOptions(),
timestamp: Date.now(),
timestamp,
});
} catch (err) {
this._handleException(err);
Expand Down
42 changes: 42 additions & 0 deletions packages/replay/test/integration/flush.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -442,4 +442,46 @@ describe('Integration | flush', () => {

replay.getOptions()._experiments.traceInternals = false;
});

/**
* This tests the case where a flush happens in time,
* but something takes too long (e.g. because we are idle, ...)
* so by the time we actually send the replay it's too late.
* In this case, we want to stop the replay.
*/
it('stops if flushing after maxSessionLife', async () => {
replay.timeouts.maxSessionLife = 100_000;

sessionStorage.clear();
clearSession(replay);
replay['_loadAndCheckSession']();
await new Promise(process.nextTick);
jest.setSystemTime(BASE_TIMESTAMP);

replay.eventBuffer!.clear();

// We do not care about this warning here
replay.eventBuffer!.hasCheckout = true;

// We want to simulate that flushing happens _way_ late
replay['_addPerformanceEntries'] = () => {
return new Promise(resolve => setTimeout(resolve, 140_000));
};

// Add event inside of session life timespan
const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP + 100, type: 2 };
mockRecord._emitter(TEST_EVENT);

await advanceTimers(DEFAULT_FLUSH_MIN_DELAY);
await advanceTimers(160_000);

expect(mockFlush).toHaveBeenCalledTimes(2);
expect(mockSendReplay).toHaveBeenCalledTimes(0);
expect(replay.isEnabled()).toBe(false);

replay.timeouts.maxSessionLife = MAX_SESSION_LIFE;

// Start again for following tests
await replay.start();
});
});