Skip to content

feat(server-utils): Restore caller context for callback tracing channels#21863

Merged
logaretm merged 5 commits into
developfrom
feat/tracing-channel-callback-context
Jun 30, 2026
Merged

feat(server-utils): Restore caller context for callback tracing channels#21863
logaretm merged 5 commits into
developfrom
feat/tracing-channel-callback-context

Conversation

@logaretm

@logaretm logaretm commented Jun 30, 2026

Copy link
Copy Markdown
Member

bindTracingChannelToSpan bound the span store on start only. That covers the synchronous frames and traces run through tracePromise. In general it covers start wrapped continuations.

We noticed that this doesn't work well for some callback-based traced calls, where asyncStart can run in a detached context. The main difference between promise-based traced calls and callback-based is callback continuations are run outside the start runStores callback. So adding a context rebind for asyncStart would fix that, and would be a safe change overall.

I used lru-memoizer as a POC to test this out. It creates no span, all it wants is the context rebind which is what the helper does anyways but was missing the asyncStart for that case specifically. With these changes we can drop the hand rolled logic.

logaretm added 2 commits June 30, 2026 10:32
`bindTracingChannelToSpan` only bound the span store on `start`, which covers
the synchronous frame but not a callback the library dispatches from a detached
async context (e.g. a socket data handler or a `setImmediate` drain). There,
native async-context propagation no longer reaches the caller, so work issued
inside the callback lost its parent.

Stash the caller's store at `start` and re-bind it on `asyncStart`, so
callback-style channels run their continuation in the caller's context — the
same way a promise's `.then` does natively. It's inert for promise channels,
which `publish` `asyncStart` rather than `runStores` it.

Migrate the lru-memoizer subscriber onto the helper (`getSpan` returns
`undefined`, so no span is created — it only needs the context rebind),
dropping its hand-rolled callback re-wrapping.
@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 27.62 kB - -
@sentry/browser - with treeshaking flags 26.05 kB - -
@sentry/browser (incl. Tracing) 46.07 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 47.82 kB - -
@sentry/browser (incl. Tracing, Profiling) 50.84 kB - -
@sentry/browser (incl. Tracing, Replay) 85.31 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 74.91 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 89.99 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 102.67 kB - -
@sentry/browser (incl. Feedback) 44.8 kB - -
@sentry/browser (incl. sendFeedback) 32.42 kB - -
@sentry/browser (incl. FeedbackAsync) 37.55 kB - -
@sentry/browser (incl. Metrics) 28.68 kB - -
@sentry/browser (incl. Logs) 28.93 kB - -
@sentry/browser (incl. Metrics & Logs) 29.61 kB - -
@sentry/react 29.41 kB - -
@sentry/react (incl. Tracing) 48.38 kB - -
@sentry/vue 32.85 kB - -
@sentry/vue (incl. Tracing) 47.93 kB - -
@sentry/svelte 27.64 kB - -
CDN Bundle 30.02 kB - -
CDN Bundle (incl. Tracing) 48.02 kB - -
CDN Bundle (incl. Logs, Metrics) 31.58 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 49.35 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 70.79 kB - -
CDN Bundle (incl. Tracing, Replay) 85.51 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 86.79 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 91.32 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.56 kB - -
CDN Bundle - uncompressed 89.42 kB - -
CDN Bundle (incl. Tracing) - uncompressed 145.35 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 94.12 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 149.32 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 218.66 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 264.36 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 268.32 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 278.06 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 282.01 kB - -
@sentry/nextjs (client) 50.76 kB - -
@sentry/sveltekit (client) 46.46 kB - -
@sentry/core/server 77.75 kB - -
@sentry/core/browser 64.06 kB - -
@sentry/node-core 61.47 kB +0.01% +1 B 🔺
@sentry/node 122.85 kB +0.03% +28 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 69.95 kB - -
@sentry/node/light 50.45 kB +0.01% +1 B 🔺
@sentry/node - without tracing 73.2 kB +0.01% +1 B 🔺
@sentry/aws-serverless 84.09 kB -0.01% -1 B 🔽
@sentry/cloudflare (withSentry) - minified 180.62 kB - -
@sentry/cloudflare (withSentry) 446.93 kB - -

View base workflow run

@logaretm logaretm marked this pull request as ready for review June 30, 2026 18:29
@logaretm logaretm requested a review from a team as a code owner June 30, 2026 18:29
@logaretm logaretm requested review from JPeer264, andreiborza, isaacs and mydea and removed request for a team June 30, 2026 18:29

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

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 910fdfe. Configure here.

Comment thread packages/server-utils/src/tracing-channel.ts
logaretm added 3 commits June 30, 2026 15:19
… caller context

The asyncStart producer used `data._sentryCallerStore ?? asyncLocalStorage.getStore()`.
When the caller had no active store, `_sentryCallerStore` is `undefined` and the
fallback bound whatever was ambient at callback time — for a callback dispatched
from a pooled socket handler, that can be another request's store. `start` always
runs first and sets `_sentryCallerStore`, so return it verbatim (no fallback),
matching the synchronous path: no caller context restores to none, not a foreign one.

@isaacs isaacs 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.

Works, and makes the mysql integration work. Will update pg PR once these two land.

@logaretm logaretm merged commit e7c24a5 into develop Jun 30, 2026
547 of 552 checks passed
@logaretm logaretm deleted the feat/tracing-channel-callback-context branch June 30, 2026 22:37
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