Skip to content

feat(extend-app-start): [3/4] Eagerly create the extended app start transaction#5608

Open
buenaflor wants to merge 10 commits into
feat/app-start-extension-androidfrom
feat/app-start-extension-materialize
Open

feat(extend-app-start): [3/4] Eagerly create the extended app start transaction#5608
buenaflor wants to merge 10 commits into
feat/app-start-extension-androidfrom
feat/app-start-extension-materialize

Conversation

@buenaflor

@buenaflor buenaflor commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

PR Stack (Extend App Start)


📜 Description

Registers the extend-listener and wires the extension through to the trace and the vital, making the feature work end-to-end. The extension is eager and standalone-only — only the standalone app.start transaction is independent of an activity and can be created in Application.onCreate.

Class Change
ActivityLifecycleIntegration Registers the extend-listener, but only when enableStandaloneAppStartTracing is on (this is what makes the API standalone-only). On Sentry.extendAppStart() the listener eagerly creates the standalone app.start transaction (waitForChildren + deadline) and its app.start.extended child, and returns them as an ExtendedAppStart.
PerformanceAndroidEventProcessor App start vital is now max(natural first-frame duration, extended end), so an early finish never shortens it. On deadline the measurement is suppressed entirely (no inflated ~30s value); spans still attach. Non-extended starts are unchanged.
AppStartExtension Adds isExtended() (the processor's gate) and setData(...) (to attach the screen once the first activity is known).

Notes

  • The transaction is owned by the extension, not the integration's appStartTransaction field, so per-activity cleanup can't cancel it.
  • The first activity continues the eager trace into ui.load (via isActive()) instead of starting a second app.start, and attaches the screen so the foreground app.start keeps its app.vitals.start.screen.
  • The first frame finishes the transaction; waitForChildren holds it open until Sentry.finishAppStart(). Headless finishes it at the headless stop time. A shared createStandaloneAppStartTransaction(...) helper backs both paths.
  • The legacy deferred-span path and the non-standalone (ui.load) extension path are removed — eager creation makes them unnecessary.

💡 Motivation and Context

Part of the app start extension API stack (#5553). This is where the extension actually affects the trace and the vital, scoped to standalone app start tracing because only the standalone app.start transaction can be created eagerly in onCreate.

💚 How did you test it?

Unit tests (TDD):

  • ActivityLifecycleIntegrationTest — eager app.start + extended child on extend; first activity continues the trace with no second app.start and the screen attached; standalone & headless stay open until finishAppStart(); standalone-off is a no-op; the eager txn survives activity destroy.
  • PerformanceAndroidEventProcessorTest — extended end drives the cold measurement; an early finish never reports shorter than the first-frame duration; a deadline finish suppresses the measurement.

apiCheck, spotless, and the :sentry-android-core unit suite pass.

📝 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.

🔮 Next steps

[4/4] — add the public Sentry.extendAppStart() / Sentry.finishAppStart() / Sentry.getExtendedAppStartSpan() facade.

⚠️ Merge this PR using a merge commit (not squash). Only the collection branch is squash-merged into main.

#skip-changelog

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor
Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 571416e

@sentry

sentry Bot commented Jun 23, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

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

⚙️ sentry-android Build Distribution Settings

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 309.45 ms 363.17 ms 53.72 ms
Size 0 B 0 B 0 B

Baseline results on branch: feat/app-start-extension-android

Startup times

Revision Plain With Sentry Diff
201a6fd 326.00 ms 371.92 ms 45.92 ms
388ffc5 315.18 ms 362.43 ms 47.25 ms
db5be2f 311.84 ms 365.94 ms 54.10 ms
b76203f 336.02 ms 403.42 ms 67.40 ms

App size

Revision Plain With Sentry Diff
201a6fd 0 B 0 B 0 B
388ffc5 0 B 0 B 0 B
db5be2f 0 B 0 B 0 B
b76203f 0 B 0 B 0 B

Previous results on branch: feat/app-start-extension-materialize

Startup times

Revision Plain With Sentry Diff
4cf89ba 340.51 ms 416.92 ms 76.41 ms
4451634 321.21 ms 376.21 ms 55.00 ms
20bbe9a 305.48 ms 387.25 ms 81.77 ms
69caaee 313.73 ms 373.92 ms 60.19 ms

App size

Revision Plain With Sentry Diff
4cf89ba 0 B 0 B 0 B
4451634 0 B 0 B 0 B
20bbe9a 0 B 0 B 0 B
69caaee 0 B 0 B 0 B

@buenaflor buenaflor force-pushed the feat/app-start-extension-android branch from 54be119 to d2b96ad Compare June 24, 2026 10:47
@buenaflor buenaflor force-pushed the feat/app-start-extension-materialize branch from 0f8ee4d to 5dfb0e1 Compare June 24, 2026 23:28
@buenaflor buenaflor force-pushed the feat/app-start-extension-materialize branch from 5dfb0e1 to 0bd259a Compare June 25, 2026 09:59
@buenaflor buenaflor changed the title feat(extend-app-start): [3/4] Materialize extended app start span and extend the vital feat(extend-app-start): [3/4] Eagerly create the extended app start transaction Jun 25, 2026
@buenaflor buenaflor force-pushed the feat/app-start-extension-android branch from 5dd39af to 3bc1643 Compare June 25, 2026 11:07
@buenaflor buenaflor force-pushed the feat/app-start-extension-materialize branch from 0bd259a to 0b6ada0 Compare June 25, 2026 11:07
@buenaflor buenaflor force-pushed the feat/app-start-extension-android branch from 3bc1643 to 45f6c0b Compare June 25, 2026 11:37
@buenaflor buenaflor force-pushed the feat/app-start-extension-materialize branch 5 times, most recently from c3f3627 to cbaef2f Compare June 25, 2026 12:23
…tion (standalone-only)

Registers an extend-listener on AppStartExtension that eagerly creates the
standalone app.start transaction + extended child span in Application.onCreate,
held open via waitForChildren until Sentry.finishAppStart() or the deadline. The
first activity continues the eager trace into ui.load (attaching the screen so it
stays a foreground app.start) instead of creating a second app.start; headless
finishes the eager txn. The app start vital is max(natural, extended) so an early
finish never shortens it, and is suppressed on deadline. Standalone-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@buenaflor buenaflor force-pushed the feat/app-start-extension-android branch from 2d5188e to 01b1dc4 Compare June 25, 2026 12:32
@buenaflor buenaflor force-pushed the feat/app-start-extension-materialize branch from cbaef2f to d996425 Compare June 25, 2026 12:32
buenaflor and others added 8 commits June 25, 2026 14:52
…on extendAppStart

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… coverage

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nd trim comments

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…app start path

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@buenaflor buenaflor marked this pull request as ready for review June 25, 2026 15:27
Copilot AI review requested due to automatic review settings June 25, 2026 15:27
// time to continue this trace.
metrics.setAppStartEndTime(endTime);

transaction.finish(SpanStatus.OK, endTime);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Headless path duplicates finished extension

Medium Severity

onHeadlessAppStart only skips creating a new standalone app.start when AppStartExtension.isActive() is true. If Sentry.finishAppStart() completes the eager extended transaction before the main-looper headless idle runs, isActive() is false and a second standalone app.start is created and finished, duplicating headless app-start tracing for one launch.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 66cd87d. Configure here.

// creating a second one.
if (metrics.getAppStartExtension().isActive()) {
metrics.getAppStartExtension().finishTransaction(endTime);
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Extended headless omits end time

Low Severity

When headless startup reuses an active extended eager transaction, onHeadlessAppStart calls finishTransaction and returns without calling setAppStartEndTime. The non-extended headless path persists that end time so a later ui.load can use the one-minute continuation window; extended headless always treats the gap as unknown and may continue the trace incorrectly.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 66cd87d. Configure here.

Comment on lines +1029 to +1032
if (metrics.getAppStartExtension().isActive()) {
metrics.getAppStartExtension().finishTransaction(endTime);
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: An extended headless app start fails to set appStartEndTime, causing all subsequent activities to be incorrectly attached to the initial app start trace.
Severity: MEDIUM

Suggested Fix

After the call to metrics.getAppStartExtension().finishTransaction(endTime) in the extended headless start code path, add a call to metrics.setAppStartEndTime(endTime) to properly close the app start time window.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location:
sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java#L1029-L1032

Potential issue: In the case of an extended headless app start, the code finishes the
transaction but fails to call `metrics.setAppStartEndTime(endTime)`. Consequently,
`AppStartMetrics.getInstance().getAppStartEndTime()` returns `null`. This causes the
`isWithinAppStartContinuationWindow` check to always return `true`, effectively creating
an infinite time window for trace continuation. As a result, all subsequent activities,
even those occurring much later, are incorrectly attached to the initial app start
trace, leading to inaccurate performance metrics.

Did we get this right? 👍 / 👎 to inform future reviews.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Implements end-to-end wiring for the Android “extend app start” feature by registering an extend listener (standalone-only), eagerly creating/holding open a standalone app.start transaction + extended child span, and updating app start vital calculation logic to account for the extension (including deadline suppression).

Changes:

  • ActivityLifecycleIntegration registers the extend listener when standalone app start tracing is enabled, eagerly creates the standalone app.start transaction + app.start.extended_app_start span on extension, and continues the trace into ui.load while attaching the first activity screen to the eager transaction.
  • PerformanceAndroidEventProcessor computes the app start measurement as max(natural first-frame duration, extended end) when extended, and suppresses the measurement entirely on deadline while still attaching app start spans.
  • AppStartExtension adds isExtended() (processor gate) and setData(...) (attach screen name to the eager transaction once known), with expanded unit test coverage.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java Registers extend listener (standalone-only), eagerly creates standalone app.start + extended span, continues trace into ui.load, and finishes the eager txn at first-frame/headless end while waiting for children.
sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java Adjusts app start measurement logic for extended flows (never shorten; suppress on deadline) while still finalizing/attaching app start spans.
sentry-android-core/src/main/java/io/sentry/android/core/AppStartExtension.java Adds setData for eager txn mutation and isExtended for processor gating.
sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt Adds tests covering eager standalone app.start creation, trace continuation into ui.load, screen attachment, wait-for-children behavior, standalone-off no-op, and survival across activity destroy.
sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt Adds tests for extended end driving cold measurement, never reporting shorter than natural duration, and deadline suppression behavior.
sentry-android-core/src/test/java/io/sentry/android/core/AppStartExtensionTest.kt Adds test ensuring isExtended remains true after the transaction finishes.
sentry-android-core/api/sentry-android-core.api API dump updated for new AppStartExtension methods (isExtended, setData).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

….extended

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 571416e. Configure here.

AppStartMetrics.getInstance()
.getAppStartExtension()
.setData(APP_START_SCREEN_DATA, activityName);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Extended headless vitals dropped after screen

Medium Severity

Attaching app.vitals.start.screen to the eager extended app.start transaction makes the processor classify it as non-headless, while shouldSendStartMeasurements still requires appLaunchedInForeground. Headless extended starts that later open an activity can skip cold/warm app-start measurements entirely.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 571416e. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants