Skip to content

fix(core): Compute span end time from performance.now() duration - #24903

Open
Lms24 wants to merge 6 commits into
lms/fix-core-browser-timestampInSeconds-offset-clockdriftfrom
lms/fix-core-span-end-monotonic-duration
Open

Lms24 wants to merge 6 commits into
lms/fix-core-browser-timestampInSeconds-offset-clockdriftfrom
lms/fix-core-span-end-monotonic-duration

Conversation

@Lms24

@Lms24 Lms24 commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

After Spans #23054, spans that run while the time origin is corrected (e.g. after the device slept) get the drift added to their duration. However, If the wall clock jumps backwards, the duration can even be negative. Also, adding sleep time to spans is arguably unexpected and unprecedented in comparison to OpenTelemetry.

This PR changes s span.end() without a explicit timestamp to compute the end as start time + performance.now() time since the start, like OpenTelemetry does. Start times still use timestampInSeconds(), so real-time started spans and spans from performance entries stay on the same timeline.

Tradeoff: when a correction happens while a span runs, its end is now based on the old time origin. So children or errors recorded after the correction can appear after the span ended. Spans with an explicit start time keep using timestampInSeconds() for the end.

@Lms24
Lms24 added this pull request to stack #24904 September 30, 2026 15:21
@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Path Size % Change Change
@sentry/browser 29.72 kB +0.41% +120 B 🔺
@sentry/browser - with treeshaking flags 27.86 kB +0.39% +107 B 🔺
@sentry/browser - with treeshaking flags tracing without tracing 27.75 kB +0.38% +103 B 🔺
@sentry/browser (incl. Tracing) 51.75 kB +0.46% +233 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 51.75 kB +0.46% +232 B 🔺
@sentry/browser (incl. Tracing, Profiling) 54.73 kB +0.42% +225 B 🔺
@sentry/browser (incl. Tracing, Replay) 91.44 kB +0.24% +211 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 80.34 kB +0.21% +161 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 96.14 kB +0.23% +215 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 109.15 kB +0.24% +261 B 🔺
@sentry/browser (incl. Feedback) 47.24 kB +0.26% +122 B 🔺
@sentry/browser (incl. sendFeedback) 34.77 kB +0.34% +117 B 🔺
@sentry/browser (incl. FeedbackAsync) 39.85 kB +0.26% +100 B 🔺
@sentry/browser (incl. Metrics) 30.72 kB +0.37% +112 B 🔺
@sentry/browser (incl. Logs) 31.01 kB +0.39% +119 B 🔺
@sentry/browser (incl. Metrics & Logs) 31.67 kB +0.38% +117 B 🔺
@sentry/react 31.54 kB +0.35% +107 B 🔺
@sentry/react (incl. Tracing) 54.07 kB +0.44% +236 B 🔺
@sentry/vue 37.75 kB +0.55% +204 B 🔺
@sentry/vue (incl. Tracing) 54.67 kB +0.51% +276 B 🔺
@sentry/svelte 29.74 kB +0.39% +114 B 🔺
@sentry/remix (Remix 3 client bundle) 56.76 kB +0.39% +215 B 🔺
CDN Bundle 31.43 kB +0.31% +97 B 🔺
CDN Bundle (incl. Tracing) 52.28 kB +0.41% +210 B 🔺
CDN Bundle (incl. Logs, Metrics) 33.64 kB +0.24% +79 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 54.23 kB +0.38% +203 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 74.53 kB +0.21% +153 B 🔺
CDN Bundle (incl. Tracing, Replay) 89.91 kB +0.2% +174 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 91.86 kB +0.19% +169 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 96.06 kB +0.17% +160 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 98.05 kB +0.19% +178 B 🔺
CDN Bundle - uncompressed 92.72 kB +0.28% +253 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 155.25 kB +0.32% +482 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 99.25 kB +0.22% +212 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 161.21 kB +0.3% +482 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 229.22 kB +0.11% +238 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 275.35 kB +0.17% +457 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 281.29 kB +0.17% +457 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 289.05 kB +0.16% +457 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 294.98 kB +0.16% +457 B 🔺
@sentry/nextjs (client) 56.45 kB +0.46% +256 B 🔺
@sentry/sveltekit (client) 52.14 kB +0.45% +233 B 🔺
@sentry/core/server 40.85 kB +0.49% +198 B 🔺
@sentry/core/browser 13.71 kB +1.48% +199 B 🔺
@sentry/node 145.78 kB +0.15% +213 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 83.33 kB +0.14% +109 B 🔺
@sentry/node - without tracing 93.66 kB +0.22% +203 B 🔺
@sentry/node - without channel injection 123.92 kB +0.17% +201 B 🔺
@sentry/aws-serverless 101.88 kB +0.19% +193 B 🔺
@sentry/cloudflare (withSentry) - minified 209.67 kB +0.31% +638 B 🔺
@sentry/cloudflare (withSentry) 519.89 kB +0.41% +2.09 kB 🔺

View base workflow run

@Lms24
Lms24 force-pushed the lms/fix-core-span-end-monotonic-duration branch from d35bfbf to 9e95d55 Compare October 2, 2026 09:44
@Lms24 Lms24 self-assigned this Oct 2, 2026
@Lms24
Lms24 marked this pull request as ready for review October 2, 2026 10:13
@Lms24
Lms24 requested review from JPeer264, logaretm and mydea October 2, 2026 10:13
@Lms24
Lms24 force-pushed the lms/fix-core-span-end-monotonic-duration branch from 264fffd to 779d31b Compare October 2, 2026 10:18
@Lms24
Lms24 force-pushed the lms/fix-core-span-end-monotonic-duration branch from 779d31b to 8b5f7d7 Compare October 2, 2026 14:13
/**
* Returns `performance.now()` in milliseconds, or `undefined` if the Performance API is unavailable.
*/
export function safePerformanceNow(): number | undefined {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

q: Should we make an oxlint rule to not use performance.now? For server runtimes it wouldn't be necessary, but for core and browser it might make sense

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For server runtimes it wouldn't be necessary

it's necessary specifically for server. The reason for the withRandomSafeContext wrapper is that NextJS cached components break when calling random functions without this special context hack.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

RE lint rule: Could make sense, but I'd do it separately. Also I think we already thought about this when we introduced withRandomSafeContext. Will check with Charly and Awad who implemented this originally.

@Lms24 Lms24 Oct 7, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh, turns out we already have this rule: no-unsafe-random-apis. However, it's missing some syntax variations at the moment. Will open a separate PR.

Comment thread packages/core/test/lib/tracing/sentrySpan.test.ts Outdated
Comment thread packages/core/src/tracing/sentrySpan.ts

@logaretm logaretm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM! I think we talked about this, it's either we get correct timings or accurate durations. So, I think this is the right decision here.

@Lms24
Lms24 force-pushed the lms/fix-core-span-end-monotonic-duration branch 2 times, most recently from f3ec383 to b3c446c Compare October 6, 2026 10:01
@Lms24
Lms24 force-pushed the lms/fix-core-span-end-monotonic-duration branch 3 times, most recently from 5489794 to b573c7c Compare October 6, 2026 17:24
@Lms24
Lms24 force-pushed the lms/fix-core-span-end-monotonic-duration branch from b573c7c to 8e8641b Compare October 7, 2026 08:34
@Lms24
Lms24 force-pushed the lms/fix-core-span-end-monotonic-duration branch from 1dc236f to 50ac6a9 Compare October 7, 2026 09:23
Lms24 and others added 2 commits October 7, 2026 13:58
Spans that run while the time origin is reset (e.g. after the device slept)
got the drift added to their duration, which could even be negative when the
wall clock jumped backwards. Now `span.end()` without a timestamp adds the
`performance.now()` time since the span started to the start time, like
OpenTelemetry does. Spans with an explicit start time keep using
`timestampInSeconds()` for the end.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Lms24 and others added 4 commits October 7, 2026 13:58
Use loose null checks when deciding whether to compute the span end time from
the performance.now() duration, and move the misplaced _onSpanEnded doc comment
back to its method.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24 force-pushed the lms/fix-core-span-end-monotonic-duration branch from 50ac6a9 to 54b7759 Compare October 7, 2026 11:58

This branch has not been deployed

No deployments
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.

3 participants