Skip to content

fix(replay): Measure recording deadlines on a monotonic clock (JAVA-575) - #6090

Open
runningcode wants to merge 3 commits into
mainfrom
no/java-575-replay-monotonic-intervals
Open

fix(replay): Measure recording deadlines on a monotonic clock (JAVA-575)#6090
runningcode wants to merge 3 commits into
mainfrom
no/java-575-replay-monotonic-intervals

Conversation

@runningcode

@runningcode runningcode commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

📜 Description

SessionReplay has a 1 hour session duration cap and a 50ms ACTION_MOVE debounce. These were both derived from wall clocks and not monotonic clocks. This PR fixes both of them to use a monotonic clock.

Here's a nice little table my agent came up with the explain the changes:

Site Before After
1h sessionDuration cap frameTimestamp - replayStartTimestamp >= sessionDuration Deadline.after(ticker, sessionDuration, MILLISECONDS)
50 ms ACTION_MOVE debounce lastCapturedMoveEvent + 50 > now Deadline.after(ticker, 50, MILLISECONDS)

What this PR deliberately does not change

Everything else JAVA-575 lists stays on the wall clock since these values are serialized:

  • segment timestamps and durations (SessionCaptureStrategy, BufferCaptureStrategy)
  • frame filenames and the ReplayCache.createVideoOf / rotate windows
  • gesture timeOffsets

Since those are customer facing / serialized values, we'll fix them in v9.

💡 Motivation and Context

A backward wall-clock step mid-recording (NTP correction, carrier time, user change — all realistic on phones) makes frames look "newer than now"; a forward step makes a recording look older than it is. For the two sites converted here that means:

  • Forward step past the cap → a healthy recording is stopped on the very next frame with Session replay deadline exceeded (1h).
  • Backward step → the recording never reaches its cap, and every move event is suppressed until the wall clock catches back up.

💚 How did you test it?

Existing tests pass and new tests added:

  • onScreenshotRecorded does not stop replay when the wall clock jumps past the deadline
  • onScreenshotRecorded stops replay when the wall clock steps backwards past the deadline
  • wall clock stepping backwards does not suppress move events
  • wall clock stepping forwards does not lift the debounce early

📝 Checklist

  • I added GH Issue ID & Linear ID
  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec.

🔮 Next steps

Fix the serialized values in v9 as mentioned above.

🤖 Generated with Claude Code

@linear-code

linear-code Bot commented Sep 10, 2026

Copy link
Copy Markdown

JAVA-575

@sentry

sentry Bot commented Sep 10, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
SDK Size io.sentry.tests.size 8.56.0 (1) release

⚙️ sentry-android Build Distribution Settings

// reflecting the exact time of when it was captured
val currentConfig = recorderConfig
val frameTimestamp = dateProvider.currentTimeMillis
val deadlineExceeded = replayDeadline?.hasPassed() == true

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

we check here because this preserves the existing behavior since we were previously comparing against frameTimestamp

@runningcode runningcode added the sanity-check PR needs a lightweight review for obvious issues label Sep 14, 2026
runningcode and others added 3 commits September 14, 2026 11:20
Session Replay derives every interval from the wall clock. Two of those
intervals are pure control flow, and a clock step corrupts both:

- The 1h `sessionDuration` cap was `frameTimestamp - replayStartTimestamp`.
  A forward step past the cap kills a healthy recording on the next frame;
  a backward step lets one run indefinitely.
- The 50ms ACTION_MOVE debounce was `lastCapturedMoveEvent + 50 > now`.
  A backward step suppresses every move event until the wall clock catches
  back up.

Both now use `io.sentry.time.Deadline` on the options' `MonotonicTicker`,
which is `SystemClock.elapsedRealtimeNanos()` on Android. That clock
advances across device suspend just as the wall clock does, so the
conversion preserves today's semantics and only removes the step.

The cap is still evaluated when a frame is captured rather than when its
queued task runs, so a busy replay executor cannot trip it.

Everything else JAVA-575 lists — segment timestamps and durations, frame
filenames, buffer cutoffs, gesture `timeOffset`s — stays on the wall clock:
those values are serialized, and fixing them means re-basing them onto an
`AnchoredClock`, which is a v9 change. `// TODO [MAJOR]` markers record that
at both `ICurrentDateProvider` injection sites.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@runningcode
runningcode force-pushed the no/java-575-replay-monotonic-intervals branch from a3428aa to 9b2f97c Compare September 14, 2026 09:20
@runningcode
runningcode marked this pull request as ready for review September 14, 2026 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sanity-check PR needs a lightweight review for obvious issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Session Replay drives interval/window math from the wall clock

1 participant