Skip to content

[test optimization] Use real timers in test framework instrumentations#7971

Merged
juan-fernandez merged 7 commits intomasterfrom
juan-fernandez/fix-timeouts
Apr 10, 2026
Merged

[test optimization] Use real timers in test framework instrumentations#7971
juan-fernandez merged 7 commits intomasterfrom
juan-fernandez/fix-timeouts

Conversation

@juan-fernandez
Copy link
Copy Markdown
Collaborator

@juan-fernandez juan-fernandez commented Apr 10, 2026

What does this PR do?

Captures real timer functions (setTimeout, Date.now) at module load time in test framework instrumentations, before any test can install fake timers.

Motivation

When DI (Failed Test Replay) is enabled and a test uses fake timers (jest.useFakeTimers(), sinon.useFakeTimers()), dd-trace's internal timer operations get intercepted by the fake clock, causing the test process to hang indefinitely:

  • Jest/Vitest: setTimeout in the DI breakpoint grace period (handleTestEvent) never fires under fake timers
  • Mocha/Cucumber: Date.now() in the DI probe setup busy-wait loop returns a frozen timestamp, causing an infinite loop

Additional Notes

Framework Issue Fix
Jest setTimeout in test_done + session flush realSetTimeout = setTimeout
Vitest setTimeout in waitForHitProbe realSetTimeout = setTimeout
Mocha Date.now() busy-wait in probe setup realDateNow = Date.now.bind(Date)
Cucumber Date.now() busy-wait in probe setup realDateNow = Date.now.bind(Date)

dd-trace's jest/mocha/vitest instrumentations used bare `setTimeout`
inside `handleTestEvent` and session finish handlers. When a test
installs fake timers (e.g. `jest.useFakeTimers()`), these calls are
intercepted by the fake clock. Since jest-circus awaits the handler's
return promise, and the fake clock is never advanced for dd-trace's
internal operations, the test execution deadlocks.

The specific trigger: DI (Failed Test Replay) enabled + a new test
using fake timers. On the second execution, dd-trace awaits a
`setTimeout` for the breakpoint hit grace period, which never fires
under fake timers.

Fix: capture `setTimeout` at module load time (before any test can
install fake timers) and use the captured reference for all internal
timer operations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 10, 2026

Overall package size

Self size: 5.48 MB
Deduped: 6.32 MB
No deduping: 6.32 MB

Dependency sizes | name | version | self size | total size | |------|---------|-----------|------------| | import-in-the-middle | 3.0.1 | 82.56 kB | 817.39 kB | | dc-polyfill | 0.1.10 | 26.73 kB | 26.73 kB |

🤖 This report was automatically generated by heaviest-objects-in-the-universe

@datadog-prod-us1-5
Copy link
Copy Markdown

datadog-prod-us1-5 bot commented Apr 10, 2026

✅ Tests

🎉 All green!

❄️ No new flaky tests detected
🧪 All tests passed

🎯 Code Coverage (details)
Patch Coverage: 42.86%
Overall Coverage: 68.24% (+0.00%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: ce68dbe | Docs | Datadog PR Page | Was this helpful? React with 👍/👎 or give us feedback!

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 10, 2026

Codecov Report

❌ Patch coverage is 42.85714% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.82%. Comparing base (abc727b) to head (ce68dbe).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
packages/datadog-plugin-cucumber/src/index.js 40.00% 3 Missing ⚠️
packages/datadog-instrumentations/src/jest.js 33.33% 2 Missing ⚠️
packages/datadog-plugin-mocha/src/index.js 33.33% 2 Missing ⚠️
...ckages/datadog-instrumentations/src/mocha/utils.js 50.00% 1 Missing ⚠️
...ackages/datadog-instrumentations/src/playwright.js 50.00% 1 Missing ⚠️
packages/datadog-instrumentations/src/selenium.js 50.00% 1 Missing ⚠️
packages/datadog-instrumentations/src/vitest.js 50.00% 1 Missing ⚠️
packages/datadog-plugin-jest/src/index.js 50.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master    #7971   +/-   ##
=======================================
  Coverage   73.82%   73.82%           
=======================================
  Files         773      773           
  Lines       35972    35983   +11     
=======================================
+ Hits        26556    26565    +9     
- Misses       9416     9418    +2     
Flag Coverage Δ
aiguard-macos 36.42% <ø> (-0.09%) ⬇️
aiguard-ubuntu 36.53% <ø> (-0.09%) ⬇️
aiguard-windows 36.33% <ø> (-0.09%) ⬇️
apm-capabilities-tracing-macos 48.42% <40.00%> (+0.04%) ⬆️
apm-capabilities-tracing-ubuntu 48.46% <40.00%> (+<0.01%) ⬆️
apm-capabilities-tracing-windows 48.24% <40.00%> (+<0.01%) ⬆️
apm-integrations-child-process 36.06% <ø> (-0.10%) ⬇️
apm-integrations-couchbase-18 34.85% <ø> (-0.23%) ⬇️
apm-integrations-couchbase-eol 35.03% <ø> (-0.12%) ⬇️
apm-integrations-oracledb 35.03% <ø> (-0.09%) ⬇️
appsec-express 52.79% <ø> (-0.07%) ⬇️
appsec-fastify 49.27% <ø> (-0.07%) ⬇️
appsec-graphql 49.63% <ø> (+0.03%) ⬆️
appsec-kafka 42.09% <ø> (-0.09%) ⬇️
appsec-ldapjs 41.39% <ø> (-0.08%) ⬇️
appsec-lodash 41.42% <ø> (-0.08%) ⬇️
appsec-macos 56.75% <50.00%> (-0.07%) ⬇️
appsec-mongodb-core 46.03% <ø> (-0.07%) ⬇️
appsec-mongoose 46.60% <ø> (-0.07%) ⬇️
appsec-mysql 48.73% <ø> (-0.07%) ⬇️
appsec-node-serialize 40.59% <ø> (-0.08%) ⬇️
appsec-passport 44.55% <ø> (-0.08%) ⬇️
appsec-postgres 48.31% <ø> (-0.17%) ⬇️
appsec-sourcing 40.05% <ø> (-0.08%) ⬇️
appsec-stripe 42.31% <ø> (-0.08%) ⬇️
appsec-template 40.75% <ø> (-0.08%) ⬇️
appsec-ubuntu 56.82% <50.00%> (-0.07%) ⬇️
appsec-windows 56.64% <50.00%> (-0.05%) ⬇️
instrumentations-instrumentation-bluebird 29.79% <ø> (-0.10%) ⬇️
instrumentations-instrumentation-body-parser 37.64% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-child_process 35.44% <ø> (-0.10%) ⬇️
instrumentations-instrumentation-cookie-parser 31.73% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-express 31.95% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-express-mongo-sanitize 31.85% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-express-session 37.27% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-fs 29.46% <ø> (-0.10%) ⬇️
instrumentations-instrumentation-generic-pool 31.14% <50.00%> (+0.01%) ⬆️
instrumentations-instrumentation-http 36.88% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-knex 29.76% <ø> (-0.10%) ⬇️
instrumentations-instrumentation-mongoose 30.88% <ø> (-0.16%) ⬇️
instrumentations-instrumentation-multer 37.42% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-mysql2 35.38% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-passport 41.17% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-passport-http 40.86% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-passport-local 41.36% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-pg 34.90% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-promise 29.72% <ø> (-0.10%) ⬇️
instrumentations-instrumentation-promise-js 29.73% <ø> (-0.10%) ⬇️
instrumentations-instrumentation-q 29.76% <ø> (-0.10%) ⬇️
instrumentations-instrumentation-url 29.72% <ø> (-0.10%) ⬇️
instrumentations-instrumentation-when 29.74% <ø> (-0.10%) ⬇️
llmobs-ai 38.38% <ø> (-0.09%) ⬇️
llmobs-anthropic 37.84% <ø> (-0.09%) ⬇️
llmobs-bedrock 37.11% <ø> (-0.08%) ⬇️
llmobs-google-genai 37.53% <ø> (-0.08%) ⬇️
llmobs-langchain 37.00% <ø> (-0.18%) ⬇️
llmobs-openai 41.22% <ø> (-0.08%) ⬇️
llmobs-vertex-ai 37.70% <ø> (-0.09%) ⬇️
platform-core 31.10% <ø> (ø)
platform-esbuild 33.97% <ø> (ø)
platform-instrumentations-misc 40.97% <ø> (ø)
platform-shimmer 37.00% <ø> (ø)
platform-unit-guardrails 32.49% <ø> (ø)
platform-webpack 20.84% <45.45%> (+0.02%) ⬆️
plugins-azure-durable-functions 25.87% <ø> (ø)
plugins-azure-event-hubs 26.03% <ø> (ø)
plugins-azure-service-bus 25.40% <ø> (ø)
plugins-bullmq 40.72% <ø> (-0.09%) ⬇️
plugins-cassandra 35.17% <ø> (-0.09%) ⬇️
plugins-cookie 27.06% <ø> (ø)
plugins-cookie-parser 26.85% <ø> (ø)
plugins-crypto 26.55% <ø> (ø)
plugins-dd-trace-api 35.42% <ø> (-0.10%) ⬇️
plugins-express-mongo-sanitize 26.99% <ø> (ø)
plugins-express-session 26.81% <ø> (ø)
plugins-fastify 39.35% <ø> (-0.09%) ⬇️
plugins-fetch 35.71% <ø> (-0.09%) ⬇️
plugins-fs 35.67% <ø> (-0.10%) ⬇️
plugins-generic-pool 25.92% <ø> (ø)
plugins-google-cloud-pubsub 43.02% <ø> (-0.08%) ⬇️
plugins-grpc 38.02% <ø> (-0.09%) ⬇️
plugins-handlebars 27.03% <ø> (ø)
plugins-hapi 37.23% <ø> (-0.09%) ⬇️
plugins-hono 37.50% <ø> (-0.09%) ⬇️
plugins-ioredis 35.61% <ø> (-0.09%) ⬇️
plugins-knex 26.67% <ø> (ø)
plugins-langgraph 35.05% <ø> (-0.09%) ⬇️
plugins-ldapjs 24.45% <ø> (ø)
plugins-light-my-request 26.41% <ø> (ø)
plugins-limitd-client 30.00% <ø> (-0.10%) ⬇️
plugins-lodash 26.00% <ø> (ø)
plugins-mariadb 36.48% <ø> (-0.09%) ⬇️
plugins-memcached 35.26% <ø> (-0.09%) ⬇️
plugins-microgateway-core 36.31% <ø> (-0.09%) ⬇️
plugins-moleculer 38.03% <ø> (-0.09%) ⬇️
plugins-mongodb 36.37% <ø> (-0.09%) ⬇️
plugins-mongodb-core 36.07% <ø> (-0.09%) ⬇️
plugins-mongoose 36.01% <ø> (-0.09%) ⬇️
plugins-multer 26.81% <ø> (ø)
plugins-mysql 36.35% <ø> (-0.09%) ⬇️
plugins-mysql2 36.33% <ø> (-0.09%) ⬇️
plugins-node-serialize 27.10% <ø> (ø)
plugins-opensearch 34.93% <ø> (-0.09%) ⬇️
plugins-passport-http 26.85% <ø> (ø)
plugins-postgres 34.32% <50.00%> (-0.11%) ⬇️
plugins-process 26.55% <ø> (ø)
plugins-pug 27.06% <ø> (ø)
plugins-redis 35.82% <ø> (-0.09%) ⬇️
plugins-router 39.98% <ø> (-0.09%) ⬇️
plugins-sequelize 25.68% <ø> (ø)
plugins-test-and-upstream-amqp10 35.63% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-amqplib 40.83% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-apollo 36.50% <ø> (-0.08%) ⬇️
plugins-test-and-upstream-avsc 35.52% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-bunyan 31.16% <ø> (-0.10%) ⬇️
plugins-test-and-upstream-connect 37.83% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-graphql 37.17% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-koa 37.44% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-protobufjs 35.73% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-rhea 40.94% <ø> (-0.09%) ⬇️
plugins-undici 36.52% <ø> (-0.09%) ⬇️
plugins-url 26.55% <ø> (ø)
plugins-valkey 35.29% <ø> (-0.09%) ⬇️
plugins-vm 26.55% <ø> (ø)
plugins-winston 31.60% <ø> (-0.09%) ⬇️
plugins-ws 38.92% <ø> (-0.09%) ⬇️
profiling-macos 37.94% <ø> (-0.55%) ⬇️
profiling-ubuntu 38.10% <ø> (-0.09%) ⬇️
profiling-windows 39.47% <ø> (-0.09%) ⬇️
serverless-azure-functions-client 25.75% <ø> (ø)
serverless-azure-functions-eventhubs 25.75% <ø> (ø)
serverless-azure-functions-servicebus 25.75% <ø> (ø)

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

☔ View full report in Codecov by Sentry.
📢 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.

@pr-commenter
Copy link
Copy Markdown

pr-commenter bot commented Apr 10, 2026

Benchmarks

Benchmark execution time: 2026-04-10 10:10:29

Comparing candidate commit ce68dbe in PR branch juan-fernandez/fix-timeouts with baseline commit abc727b in branch master.

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

juan-fernandez and others added 2 commits April 10, 2026 10:56
Adds jest and vitest integration tests that verify dd-trace completes
normally when tests use fake timers with DI (Failed Test Replay)
enabled. The tests install fake timers in beforeAll, run a failing test
that triggers ATR retries, and verify the process doesn't hang on the
DI breakpoint grace period setTimeout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…erns

The previous names (flaky-fails-with-fake-timers, dynamic-instrumentation-with-fake-timers)
were matched by existing broad TESTS_TO_RUN regexes, causing extra tests
to run in unrelated test suites.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@juan-fernandez juan-fernandez changed the title fix(ci-vis): use real timers in test framework instrumentations [test optimization] Use real timers in test framework instrumentations Apr 10, 2026
The DI probe setup in mocha and cucumber uses a synchronous busy-wait
loop with Date.now() to wait for breakpoint registration. When
sinon.useFakeTimers() is active, Date.now() returns a frozen timestamp,
causing an infinite loop.

Capture Date.now at module load time (before any test installs fake
timers) and use the captured reference in the busy-wait loops.

Also adds cucumber and mocha integration test fixtures and tests for
fake timers + DI interaction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@juan-fernandez juan-fernandez marked this pull request as ready for review April 10, 2026 09:35
@juan-fernandez juan-fernandez requested a review from a team as a code owner April 10, 2026 09:35
juan-fernandez and others added 3 commits April 10, 2026 11:44
…est assertions

- Fix remaining setTimeout in cucumber plugin's hitBreakpointPromise
  (packages/datadog-plugin-cucumber/src/index.js:235) which was still
  using the ambient setTimeout under fake timers
- Strengthen all four integration test assertions to verify retries
  actually happened (2 tests total, 1 with TEST_IS_RETRY) instead of
  just asserting tests.length > 0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The withTimeout function in the jest plugin uses setTimeout to cap the
DI probe setup wait. This is awaited inside handleTestEvent under fake
timers. Use the captured realSetTimeout instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Defensive hardening — same pattern as the other test framework fixes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@juan-fernandez juan-fernandez requested a review from a team as a code owner April 10, 2026 09:56
@juan-fernandez juan-fernandez enabled auto-merge (squash) April 10, 2026 10:15
@juan-fernandez juan-fernandez merged commit 36ebc73 into master Apr 10, 2026
893 of 894 checks passed
@juan-fernandez juan-fernandez deleted the juan-fernandez/fix-timeouts branch April 10, 2026 10:16
@dd-octo-sts dd-octo-sts bot mentioned this pull request Apr 10, 2026
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.

2 participants