Skip to content

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

Merged
runningcode merged 5 commits into
mainfrom
no/java-575-replay-monotonic-intervals
Oct 16, 2026
Merged

runningcode merged 5 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.61.0 (1) release

⚙️ sentry-android Build Distribution Settings

@runningcode runningcode added the sanity-check PR needs a lightweight review for obvious issues label Sep 14, 2026
@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

@0xadam-brown 0xadam-brown left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🕐 💯

@romtsn romtsn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, but one question - does it fully resolve #5578 because there are other things like segment windows and gesture batching that still use wall time?

@runningcode

runningcode commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

No, its a good point. its listed in the PR description under What this PR deliberately does not change. They aren't resolved since they are either customer facing or serialized to disk/server. I'll fix those in v9.

@romtsn

romtsn commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

cool, I guess we could drop the "resolves #5578" line from the description so it doesn't get auto-closed?

runningcode and others added 3 commits October 16, 2026 09:19
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 9b2f97c to 7a08bb5 Compare October 16, 2026 07:21
runningcode and others added 2 commits October 16, 2026 09:26
The executor-delay test only moved the wall clock while the frame task
was queued, so it no longer guarded the 1h cap once that cap moved onto
the monotonic ticker: checking the deadline inside the task would still
pass. Advancing the ticker as well makes it fail on that regression.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The monotonic ticker brings the capture-strategy constructors to seven
parameters now that main injects a persisting executor, and the
executor-delay ticker advance tips SessionCaptureStrategyTest over the
LargeClass threshold.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@runningcode
runningcode merged commit 3cf91ba into main Oct 16, 2026
70 checks passed
@runningcode
runningcode deleted the no/java-575-replay-monotonic-intervals branch October 16, 2026 08:17
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.

3 participants