Skip to content

Conversation

@jpollock-ampl
Copy link
Collaborator

@jpollock-ampl jpollock-ampl commented Sep 29, 2025

Summary

This PR optimizes some of the logic around starting/restarting replay capture.

Previously, the following three actions would cause the replay to start or restart:

  • Focus Change
    • This is desired, we want to restart when this happens
  • Session ID Changes (via setSessionId())
    • This is desired, we want to restart when this happens
  • An event being emitted from the SDK
    • Undesired, a restart is unnecessary in this scenario

This PR stops the replay capture from being restarted when an event is emitted from the SDK.

How does this fix work?

The primary change is in recordEvents():

if (this.recordCancelCallback && !forceRestart) { 
  // Exit Early
}

We will check for an existing callback and if the user does not pass forceRestart (default is forceRestart=false), we will exit early and not re-create the SDK.

In the case of "focus" or "session ID changes", we pass forceRestart=true, to ensure that a new capture is created.

Verification

Beyond unit tests, I created a test app and installed this version of the SDK. I was able to observe the behavior of the SDK on a focus event and then when sending 5 events via a button click:

Screenshot 2025-09-29 at 3 41 46 PM

The first 3 logs represent the "focus" event. The fourth log represents the button click.


Notes:

Given a call to track() in your application, it goes through the Amplitude Analytics SDK's pipeline:

For us, that means the plugin execution pipeline. So it goes through timeline.ts, which executes the various types of plugins:

  • before plugins
  • enrichment plugins (this is what our plugin is)
  • destination plugins

Our plugin (the SR plugin) is an enrichment plugin (source), so its execute() method is called for every single event (source)

The execute() method calls:

The recordEvents() method:

Checklist

  • Does your PR title have the correct title format?
  • Does your PR have a breaking change?:

@jpollock-ampl jpollock-ampl changed the title Fix/sdk init fix(session-replay-browser): Optimize function restart logic Sep 29, 2025
@jpollock-ampl jpollock-ampl marked this pull request as ready for review September 29, 2025 22:56
Copy link
Collaborator

@jxiwang jxiwang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Thanks for adding this!

Copy link
Collaborator

@lewgordon-amplitude lewgordon-amplitude left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!


// NOTE: If there is already an existing active recording, exit early unless forceRestart is true
if (this.recordCancelCallback && !forceRestart) {
this.loggerProvider.debug(`A capture is already in progress and forceRestart is false. Exiting.`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Maybe instead of "Exiting" we could say "not restarting". I wouldn't want this to leak to users who made get the wrong impression.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the feedback - c81dab9

Comment on lines 558 to 559
async recordEvents(recordEventsConfig?: { shouldLogMetadata?: boolean; forceRestart?: boolean }) {
const { shouldLogMetadata = true, forceRestart = true } = recordEventsConfig || {};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking these could be non null since their defaults may not be obvious and maybe it's better to be more explicit here. It also saves a null check.

Suggested change
async recordEvents(recordEventsConfig?: { shouldLogMetadata?: boolean; forceRestart?: boolean }) {
const { shouldLogMetadata = true, forceRestart = true } = recordEventsConfig || {};
async recordEvents({shouldLogMetadata, forceRestart}: { shouldLogMetadata: boolean; forceRestart: boolean }) {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great call - f75a512

this.loggerProvider.debug(`A capture is already in progress and forceRestart is false. Exiting.`);
return;
}
this.loggerProvider.debug(`Starting new capture for session.`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit: Maybe we should include the session replay ID or session ID? I think it wouldn't hurt and would potentially help with debugging.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! c68312f

Copy link
Collaborator

@lewgordon-amplitude lewgordon-amplitude left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants