Skip to content

Breaking clock changes for v9: span timing and ICurrentDateProvider removal #6506

Open

Description

@runningcode

The project plan holds back every clock change that alters serialized timing or the public API until v9. No other open issue in this project tracks those changes. This one does.

Each item below is breaking. It either changes a critical customer metric or breaks the public API.

1. Span timing on one AnchoredClock per transaction

Today each span reads the wall clock for its own start (Span calls options.getDateProvider().now()). Its end is that start plus a System.nanoTime() difference. So the starts of two spans are two separate wall-clock readings. If the wall clock steps between them, a child span can start before its parent.

The fix is the design that #6045 shipped as API only. Create one AnchoredClock per transaction in SentryTracer, and project every span timestamp from it. Nothing calls AnchoredClock yet.

Why it's breaking:

  • Durations start counting deep sleep on Android. The ticker there is CLOCK_BOOTTIME. System.nanoTime() is CLOCK_MONOTONIC, which stops in deep sleep. Any span or transaction that crosses a deep sleep gets longer. This matches iOS, which is a project goal, but it changes span and transaction durations customers chart and alert on.
  • Child span start_timestamps move by less than a millisecond. Root spans don't change.

2. Decide whether app start follows

TimeSpan runs on SystemClock.uptimeMillis(), which also stops in deep sleep. It drives the app start, process init, Application.onCreate, content provider and activity lifecycle spans.

Open question: should app start count deep sleep too? #5752 argued that launches with a Doze in the middle don't reflect what users actually experience. If app start does switch, it's breaking twice:

  • Metric: a launch where the device sleeps before the first frame would include the sleep instead of leaving it out, so app-start p95/p99 go up. Incorrect app start transaction duration when app goes in sleep #5752 only drops launches where the deadline fires late, not this case.
  • API: TimeSpan is in sentry-android-core.api. Its methods are named for uptime (setStartedAt(uptimeMs), getStartUptimeMs()), and InternalSentrySdk serializes it for the hybrid SDKs.

#6104's AndroidEnvelopeCache call site doesn't depend on this decision. It can take its own boottime reading at SDK init.

3. Remove ICurrentDateProvider

Done when

  • Spans and transactions take their timestamps from one AnchoredClock per transaction.
  • The app-start decision is made, and either implemented or explicitly deferred.
  • ICurrentDateProvider is gone from sentry.api, and sentry-dart has been updated.
  • The v9 changelog lists each change.
Agent notes

Verified on main at 1b8668ae2b (2026-11-06).

  • Span start: sentry/src/main/java/io/sentry/Span.java:68, :86. Span end: SentryNanotimeDate.laterDateNanosTimestampByDiff, serialized in protocol/SentrySpan.java:73-75 and protocol/SentryTransaction.java:62-68.
  • Android date provider: SentryAndroidDateProvider → SentryNanotimeDate (System.currentTimeMillis() + System.nanoTime()).
  • Ticker: AndroidMonotonicTicker.tickNanos() = SystemClock.elapsedRealtimeNanos(). Epoch: SentryOptions.getEpochClock().
  • AnchoredClock lives in sentry/src/main/java/io/sentry/time/AnchoredClock.java. Nothing outside io.sentry.time uses it.
  • Constraint for item 1: Choreographer frame timestamps are on CLOCK_MONOTONIC. SpanFrameMetricsCollector has to convert between the two clocks instead of subtracting across them.
  • TimeSpan: sentry-android-core/src/main/java/io/sentry/android/core/performance/TimeSpan.java. Serialized for hybrid SDKs in InternalSentrySdk.java:366-370.
  • sentry-dart caller: packages/flutter/android/src/main/kotlin/io/sentry/flutter/SentryFlutterPlugin.kt, around line 293.
  • Replay TODO [v9] markers: BaseCaptureStrategy.kt:59, ReplayGestureConverter.kt:16.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions