test: cap Node jest worker memory and fix harness teardown leaks#21268
Conversation
Node's test:base/cover:base ran jest workers with no idle-memory cap, unlike the Deno and Bun runners which already use --workerIdleMemoryLimit=512MB. Worker heap grew unbounded (~190MB to ~520MB across a run) and accumulated leaked handles, leading jest to force-exit workers. Add the same 512MB cap so workers recycle and release retained state, most impactful on legacy Node (weaker GC, memory-tighter macOS/Windows runners). Also fix two harness teardown leaks that keep a worker's event loop alive: - FakeDocument link-load timer is now unref'd (was a pending 100ms timer). - FakeWorker removes its error/message listeners on terminate().
close() destroyed the response but not the request, so calling close() before the response arrived left a pending socket holding the worker's event loop open. Store the request and destroy it in close().
|
|
This PR is packaged and the instant preview is available (a040ac1). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@a040ac1
yarn add -D webpack@https://pkg.pr.new/webpack@a040ac1
pnpm add -D webpack@https://pkg.pr.new/webpack@a040ac1 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #21268 +/- ##
========================================
Coverage 92.82% 92.82%
========================================
Files 592 592
Lines 64829 64964 +135
Branches 18067 18135 +68
========================================
+ Hits 60175 60303 +128
- Misses 4654 4661 +7
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:
|
Destroying the in-flight request in close() emitted an abort error through onerror, which lazy-compilation tests reported as "Problem communicating active modules to the server: aborted" and, via a deferred runCompiler firing after teardown, as "compiler.run undefined". Guard onerror with a closed flag so an intentional close() stays silent while still freeing the leaked socket.
Merging this PR will improve performance by 79.3%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Memory | benchmark "wasm-modules-sync", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
126 KB | 357 KB | -64.69% |
| ⚡ | Memory | benchmark "lodash", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
850.9 KB | 126.9 KB | ×6.7 |
| ⚡ | Memory | benchmark "side-effects-reexport", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
860 KB | 128.7 KB | ×6.7 |
| ⚡ | Memory | benchmark "many-modules-esm", scenario '{"name":"mode-development","mode":"development"}' |
1.9 MB | 1.1 MB | +70.08% |
| ⚡ | Memory | benchmark "devtool-eval", scenario '{"name":"mode-production","mode":"production"}' |
7.8 MB | 5.5 MB | +41.15% |
| ⚡ | Memory | benchmark "json-modules", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
366.4 KB | 281.9 KB | +30% |
| ⚡ | Memory | benchmark "future-defaults", scenario '{"name":"mode-production","mode":"production"}' |
9.3 MB | 7.7 MB | +20.69% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing test/jest-worker-memory-and-teardown-leaks (680f1ae) with main (eefaf44)
Node 20.x is in maintenance; drop it from the universal os matrix and add it back as an Ubuntu-only job like the other legacy versions. Removes the macOS and Windows 20.x cells, trimming integration from 36 to 31 jobs and freeing slots in the scarce macOS/Windows runner pools.
Types CoverageCoverage after merging test/jest-worker-memory-and-teardown-leaks into main will be
Coverage Report |
Summary
The Node
test:base/cover:basescripts ran jest with no--workerIdleMemoryLimit, unlike the Deno and Bun runners which already cap workers at 512MB. As a result worker heap grows unbounded across a run (~190MB → ~520MB in CI) and accumulated open handles make jest force-exit workers ("A worker process has failed to exit gracefully"). This is worst on legacy Node (weaker GC) and the memory-tighter macOS/Windows runners. This PR adds the same 512MB cap so workers recycle, and fixes three test-harness teardown leaks that keep a worker's event loop alive: an un-unref'dFakeDocumentlink-load timer, danglingFakeWorkerlisteners, and an un-abortedEventSourcerequest.What kind of change does this PR introduce?
test (test infrastructure / CI).
Did you add tests for your changes?
No new test cases — these are test-infrastructure fixes (jest worker config + harness teardown) that can't be meaningfully unit-tested; they are exercised by the full existing suite, which should run with bounded worker memory and without the force-exit warning.
Does this PR introduce a breaking change?
No.
If relevant, what needs to be documented once your changes are merged or what have you already documented?
n/a
Use of AI
Yes. AI (Claude) assisted with profiling the CI worker heap-usage logs, locating the harness teardown leaks, and drafting these changes. Everything was reviewed: the memory cap mirrors the existing Deno/Bun configuration and the teardown changes are standard handle cleanup.
Generated by Claude Code