ref(core): Measure the performance-collection budget on a monotonic ticker (JAVA-579) - #6101
Draft
runningcode wants to merge 2 commits into
Draft
ref(core): Measure the performance-collection budget on a monotonic ticker (JAVA-579)#6101runningcode wants to merge 2 commits into
runningcode wants to merge 2 commits into
Conversation
…icker (JAVA-579) DefaultCompositePerformanceCollector decided a transaction had been collecting for 30s by subtracting two options.getDateProvider() readings. Those are wall-clock on every platform, including Android: SentryNanotimeDate.nanoTimestamp() returns millisToNanos(unixDateMillis), not its nanoTime component, so the extra precision that type exists for never entered this comparison. A device time change therefore ended collection early or kept it running past the budget. Each CompositeData now holds a Deadline on a MonotonicTicker. The budget is not a serialized value, so this only changes when the collector stops. Two side effects worth review: the 30s boundary is now inclusive, where the old strict `>` let a sample land exactly at 30s; and addDataAndCheckTimeout no longer takes the shared clock reading, since each transaction owns its own deadline. TestMonotonicTicker's field is now volatile, as the timer thread reads a ticker the test thread advances. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9 tasks
📲 Install BuildsAndroid
|
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
DefaultCompositePerformanceCollectordecided a transaction had been collecting for 30 seconds by subtracting twooptions.getDateProvider()readings:Those readings are wall-clock on every platform, including Android. It looks like it might be monotonic because
SentryNanotimeDatecarries aSystem.nanoTime()component, butnanoTimestamp()returnsDateUtils.millisToNanos(unixDateMillis)— the nanoTime component is only used bydiff(), which this code does not call. So the extra precision that type exists for never entered this comparison, and a device time change either ended collection early or kept it running well past the budget.Each
CompositeDatanow holds aDeadlineon aMonotonicTicker. The budget is not a serialized value — it only decides when the collector stops — so this is not gated behind the v9 work.Note the sample timestamps themselves (
new PerformanceCollectionData(options.getDateProvider().now().nanoTimestamp())) are deliberately untouched: those are serialized into profile measurements, and re-anchoring them is §C1 / JAVA-578.💡 Motivation and Context
Audit finding §C6 from the clock-usage audit, which lists four wall-clock TTL/cleanup sites. This is one of them. See "Next steps" for where the other three went.
💚 How did you test it?
The two existing 30 second tests in
DefaultCompositePerformanceCollectorTestnow advance a ticker instead of stubbingdateProvider.now()with a positional sequence of four return values:That was brittle against any change in how often the date provider gets called, and it is what let the wall-clock dependency sit here unnoticed. All 20 tests in the class pass.
📝 Checklist
sendDefaultPIIis enabled.Three details worth a reviewer's eye:
Deadline.hasPassed()is>=, where the old comparison was a strict>. A sample landing exactly on 30.000s now ends collection instead of being kept.addDataAndCheckTimeoutno longer takes a sharednowNanos. That parameter existed so one clock reading was shared across every transaction in a collection round; with a per-transactionDeadlinethere is nothing to share, and the nanosecond spread across one loop cannot matter to a 30 second budget. Say the word if you would rather keep the shared reading.TestMonotonicTicker's backing field is now@Volatile, because the collector's timer thread reads a ticker that the test thread advances. (The companionHostnameCachePR does not need this —executorService.submitgives that path a happens-before edge.)🔮 Next steps
§C6 also mentions the 100ms sampling loop in this same class running on
java.util.Timer, whose deadlines are wall-clock and whoseObject.wait()does not progress in Android deep sleep. Deliberately not in this PR: it is a scheduling change rather than a TTL one, it needs either a periodic API onISentryExecutorServiceor a self-rescheduling task, and six tests in this class assert directly against an injected mockTimer. It deserves its own review.The other two §C6 sites — profiling-trace cleanup in
Sentry.javaand envelope rotation inCacheStrategy— both compare againstFile.lastModified(), which is inherently a wall-clock value written by another process; there is no monotonic quantity to compare it against, so they are not fixable as stated.🤖 Generated with Claude Code