Skip to content

Commit

Permalink
feat(replay): Add experimental option to allow for a checkout every 6…
Browse files Browse the repository at this point in the history
… minutes

Including more checkouts will improve replayer scrubbing since it will reduce the number of mutations that need to be processed (especially for longer replays). The downside is that it will increase the size of replays since we will have up to 9 more snapshots per replay (max replay duration is 60 minutes / 6 minute checkouts).
  • Loading branch information
billyvg committed Jul 26, 2024
1 parent eaf6055 commit 48c7dbc
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 7 deletions.
16 changes: 15 additions & 1 deletion packages/replay-internal/src/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,19 @@ export class ReplayContainer implements ReplayContainerInterface {
// When running in error sampling mode, we need to overwrite `checkoutEveryNms`
// Without this, it would record forever, until an error happens, which we don't want
// instead, we'll always keep the last 60 seconds of replay before an error happened
...(this.recordingMode === 'buffer' && { checkoutEveryNms: BUFFER_CHECKOUT_TIME }),
...(this.recordingMode === 'buffer'
? { checkoutEveryNms: BUFFER_CHECKOUT_TIME }
: // Otherwise, use experimental option w/ min checkout time of 6 minutes
// This is to improve playback seeking as there could potentially be
// less mutations to process in the worse cases.
//
// checkout by "N" events is probably ideal, but means we have less
// control about the number of checkouts we make (which generally
// increases replay size)
this._options._experiments.continuousCheckout && {
// Minimum checkout time is 6 minutes
checkoutEveryNms: Math.min(360_000, this._options._experiments.continuousCheckout),
}),
emit: getHandleRecordingEmit(this),
onMutation: this._onMutationHandler,
...(canvasOptions
Expand All @@ -385,6 +397,8 @@ export class ReplayContainer implements ReplayContainerInterface {
}
: {}),
});

console.log('continuousCheckout', this._options._experiments);
} catch (err) {
this._handleException(err);
}
Expand Down
1 change: 1 addition & 0 deletions packages/replay-internal/src/types/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ export interface ReplayPluginOptions extends ReplayNetworkOptions {
_experiments: Partial<{
captureExceptions: boolean;
traceInternals: boolean;
continuousCheckout: number;
}>;
}

Expand Down
18 changes: 12 additions & 6 deletions packages/replay-internal/src/util/handleRecordingEmit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,38 +59,44 @@ export function getHandleRecordingEmit(replay: ReplayContainer): RecordingEmitCa
return false;
}

const session = replay.session;

// Additionally, create a meta event that will capture certain SDK settings.
// In order to handle buffer mode, this needs to either be done when we
// receive checkout events or at flush time.
// receive checkout events or at flush time. We have an experimental mode
// to perform multiple checkouts a session (the idea is to improve
// seeking during playback), so also only include if segmentId is 0.
//
// `isCheckout` is always true, but want to be explicit that it should
// only be added for checkouts
addSettingsEvent(replay, isCheckout);
if (session && session.segmentId === 0) {
addSettingsEvent(replay, isCheckout);
}

// If there is a previousSessionId after a full snapshot occurs, then
// the replay session was started due to session expiration. The new session
// is started before triggering a new checkout and contains the id
// of the previous session. Do not immediately flush in this case
// to avoid capturing only the checkout and instead the replay will
// be captured if they perform any follow-up actions.
if (replay.session && replay.session.previousSessionId) {
if (session && session.previousSessionId) {
return true;
}

// When in buffer mode, make sure we adjust the session started date to the current earliest event of the buffer
// this should usually be the timestamp of the checkout event, but to be safe...
if (replay.recordingMode === 'buffer' && replay.session && replay.eventBuffer) {
if (replay.recordingMode === 'buffer' && session && replay.eventBuffer) {
const earliestEvent = replay.eventBuffer.getEarliestTimestamp();
if (earliestEvent) {
logInfo(
`[Replay] Updating session start time to earliest event in buffer to ${new Date(earliestEvent)}`,
replay.getOptions()._experiments.traceInternals,
);

replay.session.started = earliestEvent;
session.started = earliestEvent;

if (replay.getOptions().stickySession) {
saveSession(replay.session);
saveSession(session);
}
}
}
Expand Down

0 comments on commit 48c7dbc

Please sign in to comment.