fix(core): support manual span parenting on runtimes without native enterWith - #9445
fix(core): support manual span parenting on runtimes without native enterWith#9445bm1549 wants to merge 2 commits into
Conversation
…nterWith() workerd implements AsyncLocalStorage.run()/getStore() but throws on the imperative enterWith()/disable(), so scope.js's activate() (and thus any tracer.trace()/tracer.wrap()/nested manual span) threw on Cloudflare Workers — only flat, non-activated spans worked. Every activate()/bind() call site has a bounding callback, so the enterWith()+finally-restore path can be swapped for a scoped storage.run() where enterWith() isn't available. datadog-core/src/storage.js now feature-detects `hasNativeEnterWith` once at load (independent of the existing isACFActive probe, since workerd is treated as ACF-active but still lacks enterWith()), and scope.js picks between the two activate() implementations once at module load, so there is no per-call feature-detect on the hot path. The native-enterWith path is unchanged, so Node's context propagation is unaffected. This does not fix automatic plugin instrumentation, which calls enterWith() imperatively outside of any bounding callback — that remains a documented limitation blocked on workerd's own omission of enterWith(). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Upgrade the wrangler acceptance test to prove scope.js's storage.run()-based fallback (previous commit) actually parents spans inside real workerd, not just in Node unit tests with a stubbed capability. The worker now also handles a /parented route that nests tracer.trace() calls; the test asserts the FakeAgent's OTLP receiver captured both spans in the same trace with the child's parentSpanId equal to the parent's spanId. The original flat-span assertion is unchanged. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Overall package sizeSelf size: 6.81 MB Dependency sizes| name | version | self size | total size | |------|---------|-----------|------------| | import-in-the-middle | 3.3.1 | 122.62 kB | 438.86 kB | | opentracing | 0.14.7 | 194.81 kB | 194.81 kB | | dc-polyfill | 0.1.11 | 25.74 kB | 25.74 kB |🤖 This report was automatically generated by heaviest-objects-in-the-universe |
BenchmarksBenchmark execution time: 2026-07-20 20:21:51 Comparing candidate commit b894da1 in PR branch Found 0 performance improvements and 11 performance regressions! Performance is the same for 2313 metrics, 34 unstable metrics.
|
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: b894da1 | Docs | Datadog PR Page | Give us feedback! |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## brian.marks/cf-wrangler-ci #9445 +/- ##
==============================================================
+ Coverage 95.34% 98.34% +3.00%
==============================================================
Files 920 925 +5
Lines 122856 123240 +384
Branches 10681 10890 +209
==============================================================
+ Hits 117139 121206 +4067
+ Misses 5717 2034 -3683
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
What does this PR do?
Enables manual span parenting on runtimes that lack native
AsyncLocalStorage.prototype.enterWith(Cloudflare Workers / workerd).Scope.activate()(used bytracer.trace()/tracer.wrap()and nested manual spans) previously set context viaenterWith(), which workerd doesn't implement — so only flat, non-activated spans worked there. Since everyscope.jsactivate/bindcall site has a bounding callback, the fallback uses the scopedlegacyStorage.run(store, callback)instead:datadog-coregains ahasNativeEnterWithcapability probe (feature-detected once at module load).scope.jsselects the activation strategy once at load — the existingenterWith+finallypath when nativeenterWithis present (Node: byte-identical behavior), therun()-based path when it's absent (workerd). No per-activate()feature-detect on the hot path.child.parentSpanId === parent.spanId, sametraceId), verified in real workerd.Motivation
Flat spans already worked in workerd (prior PRs in this effort); this makes nested manual tracing —
tracer.trace('parent', () => tracer.trace('child', ...))— work too, which is how a Workers app instruments itself. Part of the Cloudflare Workers support effort (#1892).Additional Notes
brian.marks/cf-wrangler-ci(test(core): add Cloudflare Workers (workerd) acceptance test #9442). Review the 2 commits unique to this branch.semver-patch— no public API change; Node context propagation is unchanged (therun()path is taken only when nativeenterWithis absent; thehasNativeEnterWith === truecase is directly asserted instorage.spec.jsas a regression tripwire).enterWithwith no bounding callback, which can't be expressed viarun(). workerd omitsenterWithdeliberately/permanently (workerd PR Support for sampling priority #208); the eventual fix is TC39AsyncContext, not yet shipped. This PR covers manual instrumentation only.