fix(replay): Measure recording deadlines on a monotonic clock (JAVA-575) - #6090
Open
runningcode wants to merge 3 commits into
Open
fix(replay): Measure recording deadlines on a monotonic clock (JAVA-575)#6090runningcode wants to merge 3 commits into
runningcode wants to merge 3 commits into
Conversation
📲 Install BuildsAndroid
|
This was referenced Sep 11, 2026
runningcode
commented
Sep 14, 2026
| // reflecting the exact time of when it was captured | ||
| val currentConfig = recorderConfig | ||
| val frameTimestamp = dateProvider.currentTimeMillis | ||
| val deadlineExceeded = replayDeadline?.hasPassed() == true |
Contributor
Author
There was a problem hiding this comment.
we check here because this preserves the existing behavior since we were previously comparing against frameTimestamp
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
force-pushed
the
no/java-575-replay-monotonic-intervals
branch
from
September 14, 2026 09:20
a3428aa to
9b2f97c
Compare
runningcode
marked this pull request as ready for review
September 14, 2026 09:23
runningcode
requested review from
0xadam-brown,
adinauer,
markushi and
romtsn
as code owners
September 14, 2026 09:23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📜 Description
SessionReplay has a 1 hour session duration cap and a 50ms
ACTION_MOVEdebounce. 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:
sessionDurationcapframeTimestamp - replayStartTimestamp >= sessionDurationDeadline.after(ticker, sessionDuration, MILLISECONDS)ACTION_MOVEdebouncelastCapturedMoveEvent + 50 > nowDeadline.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:
SessionCaptureStrategy,BufferCaptureStrategy)ReplayCache.createVideoOf/rotatewindowstimeOffsetsSince 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:
Session replay deadline exceeded (1h).💚 How did you test it?
Existing tests pass and new tests added:
onScreenshotRecorded does not stop replay when the wall clock jumps past the deadlineonScreenshotRecorded stops replay when the wall clock steps backwards past the deadlinewall clock stepping backwards does not suppress move eventswall clock stepping forwards does not lift the debounce early📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps
Fix the serialized values in v9 as mentioned above.
🤖 Generated with Claude Code