Skip to content

fix(azure-durable-functions): link activity and entity spans to the HTTP trace#9394

Draft
chemystery09 wants to merge 9 commits into
masterfrom
azure-durable-functions-trace-propagation
Draft

fix(azure-durable-functions): link activity and entity spans to the HTTP trace#9394
chemystery09 wants to merge 9 commits into
masterfrom
azure-durable-functions-trace-propagation

Conversation

@chemystery09

@chemystery09 chemystery09 commented Jul 15, 2026

Copy link
Copy Markdown

Summary

Azure Durable Functions runs HTTP triggers, activities, and entities as separate invocations. The host forwards W3C trace context between them, but dd-trace wasn't using it — so activity and entity spans often showed up as separate traces (or got dropped).

This PR:

  • Reads traceparent / tracestate from the host's traceContext on activity and entity invocations
  • Continues that trace when starting spans, so they attach to the HTTP root instead of starting new roots
  • Re-applies the propagated sampling priority when the host clears the W3C sampled flag (traceparent -00) but Datadog tracestate still says keep — without overriding genuine upstream drop decisions

Sample trace:
https://ddserverless.datadoghq.com/u/06064c4b/uYC-ybf-nHW

image

Note: Orchestration spans are out of scope; this only covers activity and entity invocations. Customers may also need DD_TRACE_DISABLED_PLUGINS=http2 if the http2 plugin interferes with the host–worker gRPC channel.

Required host.json configuration

Trace context propagation only works when distributed tracing is enabled in the Durable Functions host. Add the following under extensions in host.json:

{
  "extensions": {
    "durableTask": {
      "tracing": {
        "distributedTracingEnabled": true,
        "version": "V2"
      }
    }
  }
}

Without this, the host does not forward traceContext (traceparent / tracestate) to activity and entity invocations, and spans will not be linked to the HTTP root trace.

Test plan

  • Deployed a Node.js Durable Functions app to Azure with the patched tracer
  • Ran an end-to-end order flow and confirmed activity spans share the HTTP trace in Datadog
  • Add an integration test for trace context propagation across durable invocations

…rable chunks

Extract the W3C traceparent/tracestate that the Durable Functions host
supplies on the invocation traceContext and continue that trace when
starting activity/entity spans, so they join the same trace as the HTTP
root instead of starting new roots.

The host re-propagates with the sampled flag cleared, so also force the
continued span's priority to USER_KEEP to prevent durable chunks from
being dropped independently.

Co-authored-by: Cursor <cursoragent@cursor.com>
@dd-octo-sts

dd-octo-sts Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Overall package size

Self size: 7.47 MB
Deduped: 8.13 MB
No deduping: 8.13 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

@datadog-official

datadog-official Bot commented Jul 15, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 98.42% (+0.08%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 2d65ca1 | Docs | Datadog PR Page | Give us feedback!

@pr-commenter

pr-commenter Bot commented Jul 15, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-07-20 18:34:52

Comparing candidate commit 2d65ca1 in PR branch azure-durable-functions-trace-propagation with baseline commit 1b3a347 in branch master.

📊 Benchmarking dashboard

Found 0 performance improvements and 0 performance regressions! Performance is the same for 2326 metrics, 32 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

Unstable benchmarks

These benchmarks have a confidence interval too wide to call a change; treat them as noise rather than signal.

scenario:appsec-appsec-enabled-24

  • unstable execution_time [-210952.546µs; +211902.480µs] or [-7.956%; +7.991%]

scenario:appsec-appsec-enabled-26

  • unstable execution_time [-236.040ms; +231.025ms] or [-9.266%; +9.070%]

scenario:appsec-appsec-enabled-with-attacks-24

  • unstable execution_time [-155.190ms; +168.751ms] or [-5.022%; +5.461%]

scenario:appsec-appsec-enabled-with-attacks-26

  • unstable execution_time [-188857.873µs; +187689.240µs] or [-6.508%; +6.467%]

scenario:appsec-control-20

  • unstable execution_time [-115.025ms; +130.785ms] or [-6.992%; +7.950%]

scenario:appsec-control-24

  • unstable execution_time [-111.829ms; +115.375ms] or [-9.038%; +9.325%]

scenario:appsec-control-26

  • unstable execution_time [-132.979ms; +124.020ms] or [-10.775%; +10.049%]

scenario:appsec-iast-no-vulnerability-iast-enabled-default-config-20

  • unstable execution_time [-12.159ms; +15.815ms] or [-4.688%; +6.097%]

scenario:appsec-iast-with-vulnerability-control-20

  • unstable execution_time [-29.527ms; +34.318ms] or [-5.332%; +6.197%]

scenario:appsec-iast-with-vulnerability-iast-enabled-default-config-20

  • unstable execution_time [-25.764ms; +32.190ms] or [-4.697%; +5.868%]

scenario:child_process-shell-string-24

  • unstable execution_time [-13.789ms; +22.045ms] or [-4.295%; +6.867%]

scenario:debugger-line-probe-with-snapshot-default-26

  • unstable cpu_user_time [-2302.896ms; +718.813ms] or [-24.045%; +7.505%]
  • unstable execution_time [-2314.590ms; +721.186ms] or [-22.466%; +7.000%]
  • unstable instructions [-20.3G instructions; +6.6G instructions] or [-25.443%; +8.246%]
  • unstable throughput [-139.914op/s; +458.627op/s] or [-4.351%; +14.262%]

scenario:debugger-line-probe-with-snapshot-minimal-26

  • unstable cpu_user_time [-3662.559ms; +3671.678ms] or [-35.443%; +35.531%]
  • unstable execution_time [-3837.491ms; +3879.645ms] or [-34.679%; +35.060%]
  • unstable instructions [-32234.5M instructions; +32186.9M instructions] or [-37.241%; +37.186%]
  • unstable max_rss_usage [-9.374MB; +11.550MB] or [-5.752%; +7.088%]
  • unstable throughput [-746.990op/s; +738.123op/s] or [-24.212%; +23.925%]

scenario:debugger-line-probe-without-snapshot-24

  • unstable cpu_user_time [-2694.240ms; +2646.796ms] or [-30.421%; +29.885%]
  • unstable execution_time [-2707.655ms; +2667.978ms] or [-28.300%; +27.885%]
  • unstable instructions [-23363.8M instructions; +23335.2M instructions] or [-32.247%; +32.208%]
  • unstable max_rss_usage [-11383.038KB; +12065.438KB] or [-7.146%; +7.574%]
  • unstable throughput [-722.170op/s; +731.618op/s] or [-20.612%; +20.882%]

scenario:dogstatsd-with-tags-20

  • unstable cpu_user_time [-394.424ms; +213.124ms] or [-8.318%; +4.494%]
  • unstable execution_time [-389.738ms; +216.778ms] or [-8.087%; +4.498%]
  • unstable throughput [-67636.789op/s; +146653.050op/s] or [-3.908%; +8.474%]

scenario:fs-subscribed-24

  • unstable execution_time [-19.396ms; +34.024ms] or [-4.797%; +8.414%]

scenario:plugin-claude-agent-sdk-compact-stream-scan-26

  • unstable cpu_usage_percentage [-3.256%; +6.830%]

scenario:plugin-pg-service-20

  • unstable execution_time [-137.895ms; +55.161ms] or [-8.344%; +3.338%]

scenario:test-optimization-large-suite-20

  • unstable max_rss_usage [-4.288MB; +6.487MB] or [-5.401%; +8.170%]

@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.42%. Comparing base (1b3a347) to head (2d65ca1).
⚠️ Report is 19 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #9394      +/-   ##
==========================================
+ Coverage   98.34%   98.42%   +0.07%     
==========================================
  Files         924      938      +14     
  Lines      123056   126079    +3023     
  Branches    10673    11058     +385     
==========================================
+ Hits       121024   124093    +3069     
+ Misses       2032     1986      -46     
Flag Coverage Δ
aiguard 57.88% <ø> (-0.37%) ⬇️
aiguard-integration 56.34% <ø> (-0.75%) ⬇️
apm-bucket-0 57.73% <ø> (-0.78%) ⬇️
apm-bucket-1 63.81% <ø> (-0.81%) ⬇️
apm-bucket-2 62.83% <ø> (-0.84%) ⬇️
apm-bucket-3 60.18% <ø> (-0.78%) ⬇️
apm-capabilities-tracing 64.25% <19.56%> (+1.93%) ⬆️
apm-integrations-aerospike 56.73% <ø> (-0.91%) ⬇️
apm-integrations-confluentinc-kafka-javascript 61.68% <ø> (-0.88%) ⬇️
apm-integrations-couchbase 57.17% <ø> (-0.74%) ⬇️
apm-integrations-http 62.87% <ø> (-0.79%) ⬇️
apm-integrations-kafkajs 62.27% <ø> (-0.86%) ⬇️
apm-integrations-next 59.29% <ø> (-0.76%) ⬇️
apm-integrations-prisma 58.76% <ø> (-0.71%) ⬇️
appsec 73.07% <ø> (-0.90%) ⬇️
appsec-express_fastify_graphql 70.54% <ø> (-1.01%) ⬇️
appsec-integration 51.46% <0.00%> (-0.44%) ⬇️
appsec-kafka_ldapjs_lodash 64.07% <ø> (-0.77%) ⬇️
appsec-mongodb-core_mongoose_mysql 67.81% <ø> (-0.76%) ⬇️
appsec-next 57.58% <ø> (-0.63%) ⬇️
appsec-node-serialize_passport_postgres 67.52% <ø> (-0.74%) ⬇️
appsec-sourcing_stripe_template 65.82% <ø> (-0.74%) ⬇️
debugger 65.06% <ø> (-0.85%) ⬇️
instrumentations-bucket-0 51.68% <ø> (-0.74%) ⬇️
instrumentations-bucket-1 60.30% <ø> (-0.85%) ⬇️
instrumentations-bucket-10 61.88% <ø> (-1.13%) ⬇️
instrumentations-bucket-11 56.76% <ø> (+4.32%) ⬆️
instrumentations-bucket-12 52.01% <ø> (-0.87%) ⬇️
instrumentations-bucket-13 52.06% <ø> (-0.32%) ⬇️
instrumentations-bucket-2 53.35% <ø> (-1.00%) ⬇️
instrumentations-bucket-3 53.65% <ø> (-6.47%) ⬇️
instrumentations-bucket-4 59.29% <ø> (+6.28%) ⬆️
instrumentations-bucket-5 50.04% <ø> (-8.31%) ⬇️
instrumentations-bucket-6 61.03% <ø> (-0.68%) ⬇️
instrumentations-bucket-7 58.44% <ø> (-0.81%) ⬇️
instrumentations-bucket-8 59.53% <ø> (-0.99%) ⬇️
instrumentations-bucket-9 52.06% <ø> (-10.32%) ⬇️
instrumentations-instrumentation-couchbase 51.11% <ø> (-0.83%) ⬇️
instrumentations-instrumentation-zlib 51.68% <ø> (?)
instrumentations-integration-esbuild 34.12% <0.00%> (+0.06%) ⬆️
llmobs-ai_anthropic_bedrock 62.64% <ø> (-0.71%) ⬇️
llmobs-bucket-1 61.94% <ø> (-0.70%) ⬇️
llmobs-openai 62.55% <ø> (-0.77%) ⬇️
llmobs-sdk 66.14% <ø> (+0.70%) ⬆️
llmobs-vertex-ai 59.25% <ø> (-0.75%) ⬇️
master-coverage 98.42% <100.00%> (?)
openfeature 54.05% <ø> (-0.65%) ⬇️
openfeature-unit 52.79% <ø> (-0.67%) ⬇️
platform-core_esbuild_instrumentations-misc 40.38% <0.00%> (-0.30%) ⬇️
platform-integration 61.59% <ø> (-0.73%) ⬇️
platform-shimmer_unit-guardrails_webpack 39.08% <0.00%> (-0.28%) ⬇️
plugins-body-parser_bullmq_cassandra 63.90% <ø> (?)
plugins-bucket-0 57.13% <ø> (-0.65%) ⬇️
plugins-bucket-1 56.23% <100.00%> (+1.06%) ⬆️
plugins-bucket-11 ?
plugins-bucket-12 62.30% <ø> (?)
plugins-bucket-15 59.32% <ø> (?)
plugins-bucket-18 61.99% <ø> (-0.94%) ⬇️
plugins-bucket-19 58.67% <ø> (-2.32%) ⬇️
plugins-bucket-20 61.85% <ø> (-1.08%) ⬇️
plugins-bucket-21 62.44% <ø> (?)
plugins-bucket-4 56.80% <ø> (-2.63%) ⬇️
plugins-bullmq_cassandra_cookie ?
plugins-cookie-parser_crypto_dd-trace-api ?
plugins-cookie_cookie-parser_crypto 51.33% <ø> (?)
plugins-fastify_fetch_fs 61.30% <ø> (?)
plugins-fetch_fs_generic-pool ?
plugins-generic-pool_google-cloud-pubsub_grpc 64.84% <ø> (?)
plugins-google-cloud-pubsub_grpc_handlebars ?
plugins-handlebars_hapi_hono 58.94% <ø> (?)
plugins-hapi_hono_ioredis ?
plugins-ioredis_jest_knex 57.94% <ø> (?)
plugins-jest_knex_langgraph ?
plugins-langgraph_ldapjs_light-my-request 58.11% <ø> (?)
plugins-ldapjs_light-my-request_limitd-client ?
plugins-limitd-client_lodash_mariadb 57.97% <ø> (?)
plugins-lodash_mariadb_memcached ?
plugins-memcached_mercurius_microgateway-core 61.36% <ø> (?)
plugins-moleculer_mongodb_mongodb-core ?
plugins-mongodb-core_mongoose_multer 59.36% <ø> (?)
plugins-mongoose_multer_mysql ?
plugins-mysql2_nats_node-serialize ?
plugins-mysql_mysql2_nats 61.09% <ø> (?)
plugins-opensearch_passport-http_pino ?
plugins-pino_postgres_process 58.79% <ø> (?)
plugins-postgres_process_pug ?
plugins-pug_redis_router 62.22% <ø> (?)
plugins-redis_router_sequelize ?
plugins-test-and-upstream-rhea_undici_url ?
plugins-url_valkey_vm 57.63% <ø> (?)
plugins-valkey_vm_winston ?
plugins-winston_ws 60.04% <ø> (?)
plugins-ws ?
profiling 62.31% <ø> (-0.79%) ⬇️
serverless-aws-sdk-aws-sdk 55.14% <ø> (-0.59%) ⬇️
serverless-aws-sdk-bedrockruntime 54.83% <ø> (-0.62%) ⬇️
serverless-aws-sdk-client 56.51% <ø> (-0.68%) ⬇️
serverless-aws-sdk-dynamodb 55.75% <ø> (-0.64%) ⬇️
serverless-aws-sdk-eventbridge 49.44% <ø> (-0.51%) ⬇️
serverless-aws-sdk-kinesis 59.46% <ø> (-0.71%) ⬇️
serverless-aws-sdk-lambda 57.49% <ø> (-0.67%) ⬇️
serverless-aws-sdk-s3 55.69% <ø> (-0.63%) ⬇️
serverless-aws-sdk-serverless-peer-service 59.87% <ø> (-0.72%) ⬇️
serverless-aws-sdk-sns 60.26% <ø> (-0.73%) ⬇️
serverless-aws-sdk-sqs 60.69% <ø> (-0.76%) ⬇️
serverless-aws-sdk-stepfunctions 55.67% <ø> (-0.64%) ⬇️
serverless-aws-sdk-util 51.57% <ø> (-0.65%) ⬇️
serverless-bucket-0 58.00% <100.00%> (+2.78%) ⬆️
serverless-bucket-1 59.37% <ø> (-0.75%) ⬇️
test-optimization-cucumber 71.91% <ø> (-1.11%) ⬇️
test-optimization-cypress 65.87% <ø> (-0.47%) ⬇️
test-optimization-jest 73.39% <ø> (-1.05%) ⬇️
test-optimization-mocha 73.56% <ø> (-1.12%) ⬇️
test-optimization-playwright-playwright-atr 60.49% <ø> (-0.95%) ⬇️
test-optimization-playwright-playwright-efd 60.67% <ø> (-0.95%) ⬇️
test-optimization-playwright-playwright-final-status 60.65% <ø> (-1.12%) ⬇️
test-optimization-playwright-playwright-impacted-tests 60.37% <ø> (-0.78%) ⬇️
test-optimization-playwright-playwright-reporting 60.27% <ø> (-0.88%) ⬇️
test-optimization-playwright-playwright-test-management 61.19% <ø> (-1.08%) ⬇️
test-optimization-playwright-playwright-test-span 60.40% <ø> (-1.01%) ⬇️
test-optimization-selenium 59.89% <ø> (-0.99%) ⬇️
test-optimization-testopt 58.51% <ø> (-0.62%) ⬇️
test-optimization-vitest 70.30% <ø> (-1.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…recisely

Rework the durable trace-continuation keep so it no longer force-keeps
every activity/entity invocation:

- Only re-apply a keep when the Durable Functions host cleared the W3C
  sampled flag (traceparent `-00`) but the datadog tracestate decision
  (`s`) still indicates keep. Genuine upstream drops (no dd tracestate,
  or `s` <= 0) are now honored so the customer's sampling config is
  respected instead of retaining 100% of durable chunks.
- Continue with the exact propagated priority (tracestate `s`) rather
  than always USER_KEEP, so the durable chunk matches the rest of the
  trace and the propagated decision maker is preserved.
- Normalize a failed `extract()` (null) to undefined so startSpan still
  falls back to an in-process parent instead of re-rooting.

Co-authored-by: Cursor <cursoragent@cursor.com>
@chemystery09 chemystery09 changed the title fix(azure-durable-functions): continue host trace context across durable invocations fix(azure-durable-functions): link activity and entity spans to the HTTP trace Jul 16, 2026
chemystery09 and others added 5 commits July 16, 2026 16:53
…nd integration tests

Add plugin unit tests for trace extraction, sampling re-application, and entity
metadata so patch coverage meets the codecov threshold. Extend the integration
test to assert activity and entity spans share the HTTP trace id.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ce integration check

Wire the azure-durable-functions job through plugins/test so the new unit spec
is exercised in CI. Remove the integration assertion for shared trace ids because
the local Functions host fixture does not propagate traceContext to activities.

Co-authored-by: Cursor <cursoragent@cursor.com>
…st step

SPEC=index limits test:plugins to index.spec.js so the unit job does not
re-run integration tests or install azure-functions-core-tools into the
plugin sandbox.

Co-authored-by: Cursor <cursoragent@cursor.com>
When the loader already runs init/instrument.mjs, the separate ESM
instrumentation test spawns the same child back-to-back and can hang on
Node 22.0.0 under DD_INJECTION_ENABLED.

Co-authored-by: Cursor <cursoragent@cursor.com>
@chemystery09
chemystery09 marked this pull request as ready for review July 16, 2026 21:50
@chemystery09
chemystery09 requested review from a team as code owners July 16, 2026 21:50
@chemystery09
chemystery09 requested review from bojbrook and litianningdatadog and removed request for a team July 16, 2026 21:50
This change belongs in a separate PR; keep azure-durable-functions work scoped.

Co-authored-by: Cursor <cursoragent@cursor.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@ojproductions ojproductions self-assigned this Jul 17, 2026
@ojproductions

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 1a8f532215

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chemystery09
chemystery09 marked this pull request as draft July 17, 2026 15:25

@ojproductions ojproductions left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this looks great! for now, as @jcstorms1 said, lets keep this as a draft, until we get a proper rfc and approval for all the durable functions work.

I do have 2 notes:

  • I tried running your code and realized it doesnt work unless the function's host.json config has the following:
    "durableTask": {
      "tracing": {
        "distributedTracingEnabled": true,
        "version": "V2"
      }
    }

This is what allows the traceContext to be propagated to the activity/entity spans. I think itd be good to include this in your PR description as I had no clue about this config

Secondly: this might be a personal peeve, but I think its still worth mentioning. Some of these comments are unnecessarily long. it adds extra context and detail that a human reader ends up having to decipher alongside the code. this ends up just making code harder to read. Please consider, shortening some of it 🙏

Comment thread packages/datadog-plugin-azure-durable-functions/src/index.js Outdated
Trim the inline comment per review feedback while keeping the key
behavior: re-apply propagated tracestate priority when the host clears
the W3C sampled flag.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants