fix(android): Read the app start type once per event processor pass - #6006
Draft
runningcode wants to merge 2 commits into
Draft
fix(android): Read the app start type once per event processor pass#6006runningcode wants to merge 2 commits into
runningcode wants to merge 2 commits into
Conversation
PerformanceAndroidEventProcessor read AppStartMetrics.getAppStartType() three separate times while building a single transaction: once for the measurement key, once to decide whether to attach the cold-start breakdown children, and once for contexts.app.start_type, which is also what is now stamped on the trace context and every child span as app.vitals.start.type. The field is written on the main thread (activity lifecycle callbacks, ApplicationStartInfo) while the processor runs on whichever thread finished the transaction, which can be the Sentry timer thread for idle and deadline timeouts. Backgrounding the app flips the type to WARM in onActivityDestroyed, so the reads can disagree and ship a transaction whose measurement says app_start_cold while every span says app.vitals.start.type = warm. Snapshot the type once at the top of process() and pass it down, and make the field volatile so the read is not a data race. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
📲 Install BuildsAndroid
|
9 tasks
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
PerformanceAndroidEventProcessor.process()readAppStartMetrics.getAppStartType()three separate times while building a single transaction:app_start_cold/app_start_warm)attachAppStartSpansto decide whether to attach the cold-start breakdown childrencontexts.app.start_type— which feat(android): Set app start vitals on standalone app start children #6005 now also stamps onto the trace context and every child span asapp.vitals.start.typeThis snapshots the type once at the top of
process()and passes it down, and makesAppStartMetrics.appStartTypevolatile.💡 Motivation and Context
appStartTypeis written on the main thread (activity lifecycle callbacks, theApplicationStartInfoblock) and read by the event processor, which runs on whichever thread finished the transaction —SentryTracer.finish()callscaptureTransaction()synchronously, so for idle and deadline timeouts that is the Sentry timer thread, not the main thread. The field was neithervolatilenor guarded, so the read was a plain data race.Beyond the JMM issue, the repeated reads can genuinely disagree.
onActivityDestroyedsetsappStartType = WARMwhen the last activity goes away, so backgrounding the app while a transaction finishes on the timer thread can produce a transaction whose measurement saysapp_start_coldwhilecontexts.app.start_typeand every span'sapp.vitals.start.typesaywarm. Since #6005 that value is the grouping key for the whole mobile vitals app start breakdown, so a mismatch splits the breakdown rather than just mislabelling one field.💚 How did you test it?
Two tests in
PerformanceAndroidEventProcessorTestasserting that the measurement key,contexts.app.start_type, the trace context and every span agree, for cold and warm standalone app starts. Full:sentry-android-core:testReleaseUnitTestpasses.Note that the race itself is not deterministically unit-testable — there is no seam to flip the type between the reads inside a single
process()call — so the tests lock in the invariant rather than reproducing the interleaving.📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps
Follow-up to #6005. See also the companion PR removing the
AppStartExtensionlock-ordering deadlock.