Skip to content

ref(node): Fix flaky ANR stop-and-restart test with deterministic worker-ready signal - #23514

Merged
mydea merged 2 commits into
developfrom
fn/fix-anr-worker-restart-flake
Sep 9, 2026
Merged

ref(node): Fix flaky ANR stop-and-restart test with deterministic worker-ready signal#23514
mydea merged 2 commits into
developfrom
fn/fix-anr-worker-restart-flake

Conversation

@mydea

@mydea mydea commented Aug 24, 2026

Copy link
Copy Markdown
Member

The anr/stop-and-start integration test restarts the ANR worker and then blocks the event loop, expecting the restarted worker to sample and report the ANR. The gate before longWork was ineffective, so on slow CI the event loop could block before the new worker had reconnected — and the ANR was missed, making the test flaky.

Root cause

waitForDebuggerReady polls inspector.url(). That works for the initial worker start (where inspector.open(0) flips inspector.url() from unset to set), but it cannot gate a restart:

  • The ANR integration opens the main-thread inspector once and never calls inspector.close(), so inspector.url() stays truthy across stopWorker()/startWorker(). The poll returns immediately and only a fixed ~200ms delay remains.
  • _startWorker is async (it awaits getContexts), so when the poll passes the new worker may not even be spawned yet.

The main thread has no observable signal that the new worker's InspectorSession has reconnected, so any main-thread poll or fixed delay is a race.

Change

The worker now posts a worker-ready message once it is fully set up (inspector session connected when capturing stack traces, watchdog armed, message handler registered). The integration tracks this and exposes an internal waitUntilWorkerReady(), which the restart path awaits instead of relying on a fixed delay. This makes readiness deterministic rather than timing-dependent.

The flake exists on develop too (the restart path there blocks after a bare setTimeout(..., 0)), which is why this is a standalone fix rather than part of any feature branch.

@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 28.81 kB +0.04% +11 B 🔺
@sentry/browser - with treeshaking flags 27.12 kB +0.04% +10 B 🔺
@sentry/browser - with treeshaking flags tracing without tracing 27.01 kB +0.03% +7 B 🔺
@sentry/browser (incl. Tracing) 49.22 kB -0.01% -2 B 🔽
@sentry/browser (incl. Tracing + Span Streaming) 49.22 kB +0.02% +5 B 🔺
@sentry/browser (incl. Tracing, Profiling) 52.15 kB +0.05% +21 B 🔺
@sentry/browser (incl. Tracing, Replay) 88.76 kB -0.01% -2 B 🔽
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 77.94 kB -0.02% -10 B 🔽
@sentry/browser (incl. Tracing, Replay with Canvas) 93.44 kB +0.01% +1 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 106.38 kB +0.01% +10 B 🔺
@sentry/browser (incl. Feedback) 46.3 kB +0.02% +5 B 🔺
@sentry/browser (incl. sendFeedback) 33.87 kB +0.02% +6 B 🔺
@sentry/browser (incl. FeedbackAsync) 38.99 kB +0.04% +13 B 🔺
@sentry/browser (incl. Metrics) 29.84 kB +0.08% +23 B 🔺
@sentry/browser (incl. Logs) 30.1 kB +0.06% +18 B 🔺
@sentry/browser (incl. Metrics & Logs) 30.78 kB +0.11% +31 B 🔺
@sentry/react 30.57 kB +0.05% +14 B 🔺
@sentry/react (incl. Tracing) 51.57 kB +0.03% +12 B 🔺
@sentry/vue 36.07 kB +0.05% +17 B 🔺
@sentry/vue (incl. Tracing) 51.49 kB +0.04% +19 B 🔺
@sentry/svelte 28.85 kB +0.06% +15 B 🔺
CDN Bundle 30.56 kB +0.05% +15 B 🔺
CDN Bundle (incl. Tracing) 49.76 kB +0.06% +25 B 🔺
CDN Bundle (incl. Logs, Metrics) 32.83 kB +0.03% +9 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 51.7 kB -0.01% -1 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) 73.48 kB -0.01% -4 B 🔽
CDN Bundle (incl. Tracing, Replay) 87.28 kB -0.01% -8 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 89.2 kB +0.03% +20 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 93.21 kB -0.01% -3 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 95.22 kB +0.01% +8 B 🔺
CDN Bundle - uncompressed 90.47 kB +0.01% +8 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 148.17 kB -0.01% -1 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed 97.04 kB +0.01% +8 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 154.13 kB -0.01% -1 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 226.3 kB +0.01% +8 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 267.76 kB -0.01% -1 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 273.71 kB -0.01% -1 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 281.46 kB -0.01% -1 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 287.4 kB -0.01% -1 B 🔽
@sentry/nextjs (client) 54.03 kB +0.06% +31 B 🔺
@sentry/sveltekit (client) 49.68 kB +0.06% +26 B 🔺
@sentry/core/server 36.99 kB - -
@sentry/core/browser 13.55 kB - -
@sentry/node 127.86 kB +0.06% +67 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 81.61 kB - -
@sentry/node - without tracing 88.73 kB +0.03% +22 B 🔺
@sentry/node - without channel injection 107.11 kB +0.08% +81 B 🔺
@sentry/aws-serverless 97.12 kB +0.03% +28 B 🔺
@sentry/cloudflare (withSentry) - minified 201.98 kB - -
@sentry/cloudflare (withSentry) 502.68 kB - -

View base workflow run

@mydea
mydea force-pushed the fn/fix-anr-worker-restart-flake branch from f7d93b2 to 05ee48e Compare September 7, 2026 12:32
@mydea mydea changed the title test(node): Fix flaky ANR stop-and-restart test with deterministic worker-ready signal ref(node): Fix flaky ANR stop-and-restart test with deterministic worker-ready signal Sep 7, 2026
@mydea
mydea marked this pull request as ready for review September 7, 2026 12:33
@mydea
mydea requested review from a team as code owners September 7, 2026 12:33
@mydea
mydea requested review from andreiborza, isaacs and nicohrubec and removed request for a team September 7, 2026 12:33

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

Lint nit, but otherwise LGTM.

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.

Should probably just remove this method, since it's not used?

mydea and others added 2 commits September 9, 2026 13:54
…y signal

The `stop-and-start` ANR test restarts the worker and then blocks the event loop,
expecting the restarted worker to sample and report the ANR. It previously ran
`longWork` almost immediately after `startWorker()`.

`waitForDebuggerReady` (polling `inspector.url()`) cannot gate this: the ANR
integration opens the main-thread inspector once and never closes it, so
`inspector.url()` stays truthy across restarts and the poll returns immediately.
`_startWorker` is also async, so the new worker may not even be spawned yet. On
slow CI the event loop can block before the new worker reconnects its inspector
session, and the ANR is missed.

The worker now posts a `worker-ready` message once it is fully set up, and the
integration exposes `waitUntilWorkerReady()` so the restart path can await the
new worker's actual readiness instead of a fixed delay.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mydea
mydea force-pushed the fn/fix-anr-worker-restart-flake branch from 05ee48e to 5ed3395 Compare September 9, 2026 11:55
@mydea
mydea enabled auto-merge (squash) September 9, 2026 11:55
Comment on lines 92 to 98
}
},
stopWorker: () => {
if (worker) {
workerReady = undefined;
// eslint-disable-next-line @typescript-eslint/no-floating-promises
worker.then(stop => {

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.

Bug: A race condition in disableAnrDetectionForCallback with a synchronous callback can permanently disable ANR detection because startWorker exits early before the old worker is fully terminated.
Severity: HIGH

Suggested Fix

To fix this, ensure worker is cleared before or at the same time as workerReady in stopWorker. For example, set workerReady to undefined inside the .then() block after the worker has been stopped and its promise cleared. Alternatively, modify startWorker to handle this state correctly and re-initialize the worker.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: packages/node/src/integrations/anr/index.ts#L92-L98

Potential issue: A race condition occurs when `disableAnrDetectionForCallback` is used
with a synchronous callback. The `stopWorker` function sets `workerReady` to `undefined`
synchronously but only schedules the termination of the `worker` promise asynchronously
via a microtask. If the synchronous callback finishes quickly, `startWorker` is called
before the microtask runs. Inside `startWorker`, the check `if (worker)` is true,
causing it to return early without starting a new worker. When the microtask eventually
runs, it clears the `worker`, leaving the integration in a state with no worker running
and no `workerReady` promise. This permanently disables ANR detection for the
application's lifecycle.

Also affects:

  • packages/node/src/integrations/anr/index.ts:68~70

Did we get this right? 👍 / 👎 to inform future reviews.

@mydea
mydea merged commit e4a55c3 into develop Sep 9, 2026
214 of 215 checks passed
@mydea
mydea deleted the fn/fix-anr-worker-restart-flake branch September 9, 2026 12:11
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