You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 66f67ec
Browse filesBrowse the repository at this point in the historyBrowse files
feat(time): Add Timestamp, EpochClock and AnchoredClock (JAVA-572) (#6045)
SentryDate is asked to be four things at once: an epoch instant to
serialize, one endpoint of a monotonic interval, a carrier of a hidden
System.nanoTime() reading, and an opaque foreign timestamp. Nothing in the
type separates them, so the guarantees are decided by the runtime class of
both operands -- SentryNanotimeDate.diff() is monotonic only when the other
date is also a SentryNanotimeDate, and silently subtracts two wall-clock
readings otherwise. On the JVM, where SentryAutoDateProvider picks
SentryInstantDate, neither endpoint has a monotonic component and span
durations are not monotonic at all.
The fix is not to type the instants more carefully. It is to stop producing
them independently. A group of instants that will be compared against each
other -- the spans of a transaction, the samples of a profile chunk, the
segments of a replay -- reads the epoch once and builds the rest from the
monotonic ticker:
Timestamp an epoch instant. No arithmetic between instants; equality
is by instant.
EpochClock the wall clock, for stamping a moment that leaves the
process. Not for computing durations.
AnchoredClock one wall-clock reading pinned to one tick, from which now()
builds.
Subtracting two instants from one anchor is subtracting two ticks, so a
duration is monotonic by construction rather than by convention, and a clock
step cannot make a child span start before its parent. It also gives Android
nanosecond resolution it cannot read directly, the epoch being
millisecond-granular there -- the workaround SentryNanotimeDate describes,
applied once per group instead of between each pair of readings.
OpenTelemetry's SDK anchors per local root span for the same two reasons.
Only what a caller needs today is exposed. Placing an externally measured
tick on the anchor's timeline, and recovering the tick a timestamp was built
from, are both exact and both trivial to add, but neither is reachable until
spans carry an anchor. They belong to the change that migrates them rather
than to this one, where they would arrive with no caller.
Timing is dropped rather than kept. It paired one Timestamp with one
Stopwatch, which is what AnchoredClock does for a whole group, and no call
site would have wanted the single-interval version.
Nothing calls any of it yet.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,6 +11,7 @@
11
11
### Internal
12
12
13
13
- Add an internal `MonotonicTicker` abstraction with `Deadline` and `Stopwatch` primitives ([#6028](https://github.com/getsentry/sentry-java/pull/6028))
14
+
- Add internal `Timestamp`, `EpochClock` and `AnchoredClock`, so related instants project from one wall-clock reading instead of each reading the clock ([#6045](https://github.com/getsentry/sentry-java/pull/6045))
Copy file name to clipboardExpand all lines: sentry/api/sentry.api
+24Lines changed: 24 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -3707,6 +3707,7 @@ public class io/sentry/SentryOptions {
3707
3707
public fun getEnvelopeDiskCache ()Lio/sentry/cache/IEnvelopeCache;
3708
3708
public fun getEnvelopeReader ()Lio/sentry/IEnvelopeReader;
3709
3709
public fun getEnvironment ()Ljava/lang/String;
3710
+
public fun getEpochClock ()Lio/sentry/time/EpochClock;
3710
3711
public fun getEventProcessors ()Ljava/util/List;
3711
3712
public fun getExecutorService ()Lio/sentry/ISentryExecutorService;
3712
3713
public fun getExperimental ()Lio/sentry/ExperimentalOptions;
@@ -7598,6 +7599,12 @@ public final class io/sentry/rrweb/RRWebVideoEvent$JsonKeys {
7598
7599
public fun <init> ()V
7599
7600
}
7600
7601
7602
+
public final class io/sentry/time/AnchoredClock {
7603
+
public static fun create (Lio/sentry/time/EpochClock;Lio/sentry/time/MonotonicTicker;)Lio/sentry/time/AnchoredClock;
7604
+
public fun now ()Lio/sentry/time/Timestamp;
7605
+
public fun startTime ()Lio/sentry/time/Timestamp;
7606
+
}
7607
+
7601
7608
public final class io/sentry/time/Deadline {
7602
7609
public static fun after (Lio/sentry/time/MonotonicTicker;JLjava/util/concurrent/TimeUnit;)Lio/sentry/time/Deadline;
7603
7610
public fun hasPassed ()Z
@@ -7606,6 +7613,10 @@ public final class io/sentry/time/Deadline {
7606
7613
public fun remaining (Ljava/util/concurrent/TimeUnit;)J
7607
7614
}
7608
7615
7616
+
public abstract interface class io/sentry/time/EpochClock {
7617
+
public abstract fun now ()Lio/sentry/time/Timestamp;
7618
+
}
7619
+
7609
7620
public final class io/sentry/time/JavaMonotonicTicker : io/sentry/time/MonotonicTicker {
7610
7621
public static fun getInstance ()Lio/sentry/time/MonotonicTicker;
7611
7622
public fun tickNanos ()J
@@ -7621,6 +7632,19 @@ public final class io/sentry/time/Stopwatch {
7621
7632
public static fun started (Lio/sentry/time/MonotonicTicker;)Lio/sentry/time/Stopwatch;
7622
7633
}
7623
7634
7635
+
public final class io/sentry/time/SystemEpochClock : io/sentry/time/EpochClock {
7636
+
public static fun getInstance ()Lio/sentry/time/EpochClock;
7637
+
public fun now ()Lio/sentry/time/Timestamp;
7638
+
}
7639
+
7640
+
public final class io/sentry/time/Timestamp {
7641
+
public fun epochNanos ()J
7642
+
public fun equals (Ljava/lang/Object;)Z
7643
+
public fun hashCode ()I
7644
+
public static fun ofEpochNanos (J)Lio/sentry/time/Timestamp;
7645
+
public fun toString ()Ljava/lang/String;
7646
+
}
7647
+
7624
7648
public final class io/sentry/transport/AsyncHttpTransport : io/sentry/transport/ITransport {
7625
7649
public fun <init> (Lio/sentry/SentryOptions;Lio/sentry/transport/RateLimiter;Lio/sentry/transport/ITransportGate;Lio/sentry/RequestDetails;)V
7626
7650
public fun <init> (Lio/sentry/transport/QueuedThreadPoolExecutor;Lio/sentry/SentryOptions;Lio/sentry/transport/RateLimiter;Lio/sentry/transport/ITransportGate;Lio/sentry/transport/HttpConnection;)V
0 commit comments