ci: fix the flaky "database is locked" failure in the Deno job - #666
ci: fix the flaky "database is locked" failure in the Deno job#666alexander-akait wants to merge 3 commits into
Conversation
jest clears mocks by walking the globals of the test sandbox and testing `"_isMockFunction" in value` on each one (`ModuleMocker.clearMocksOnScope`). Its node environment exposes `localStorage` there, and in Deno that global is backed by a SQLite database under `DENO_DIR` that is opened on first access. Jest workers are separate Deno processes, so on a cold cache they all race to create that database and the losers throw "database is locked", failing a random test suite. Preload an in-memory `Storage` over both web storage globals instead. The workers inherit `NODE_OPTIONS`, so the shim reaches every process and the database is never opened, which keeps the suite parallel. Neither the library nor the suite uses web storage, so nothing needs to persist. The preload would fail silently if it ever stopped being applied, so the job now also asserts that no web storage database was created. Fixes #660 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016aGHrb1YaEvaGwNELGEHjF
`--max-concurrency` caps how many `test.concurrent()` tests run at once. The suite declares none and the job does not pass `--concurrent`, so the flag never applied to anything: on bun 1.4.2 the suite runs in ~9.2s with or without it. It was added in 6f87171, whose message ("run bun concurrently") describes the opposite of what it did, and the bun job was green without it in the run right before. Removing it restores the intent and leaves behavior unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016aGHrb1YaEvaGwNELGEHjF
|
Merging this PR will degrade performance by 6.54%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Memory | alias-wildcard-scan: 100+1 wildcard + 1 exact |
1.5 KB | 2.7 KB | -46.84% |
| ⚡ | Memory | deep-package-subpath: pkg/a/b/c requests (warm) |
3.6 KB | 2.2 KB | +64.31% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing claude/enhanced-resolve-660-qo14h7 (a1a5bb5) with main (c3eee53)
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #666 +/- ##
=======================================
Coverage 98.14% 98.14%
=======================================
Files 49 49
Lines 10018 10018
=======================================
Hits 9832 9832
Misses 186 186
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:
|
`lint:types-test` type-checks `test/*.js` with the Node.js types only, so a bare `typeof Deno` reference fails with TS2304. Probe the global with `"Deno" in global` instead, which needs no declaration. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016aGHrb1YaEvaGwNELGEHjF
Fixes #660
The failure
The
denoleg of Cross-runtime fails intermittently with a random test suite dying before it runs:Root cause
jest clears mocks by walking the globals of the test sandbox and testing
"_isMockFunction" in valueon each one. Instrumenting the global catches the exact caller:jest-environment-nodeexposeslocalStoragein the sandbox, and in Deno that global is backed by a SQLite database underDENO_DIRwhich is opened on first access. Thatincheck is the first access. Jest workers are separate Deno processes, so on a cold cache (CI never cachesDENO_DIR) they all race to create that database and the losers throwdatabase is locked, taking down whichever suite they were holding.Reproduced standalone — 6 Deno processes opening
localStorageat a synchronized instant:Fix
Preload an in-memory
StorageoverlocalStorage/sessionStoragebefore jest starts. Jest workers inheritNODE_OPTIONS, so the shim reaches every process — which keeps the suite parallel instead of serialising it with--runInBand. Neither the library nor the suite uses web storage, so nothing needs to persist.Measured on Deno 2.9.6 from a cold cache:
--runInBandAll four processes (parent + 3 workers) report the preload installed and
DENO_DIR/location_datanever appears, so the race is structurally impossible rather than merely unlikely.A
--requirepreload fails silently if it ever stops being applied, which would quietly restore the flake, so the job now also asserts that no web storage database was created. Tested both ways: passes with the shim, errors withNODE_OPTIONSunset.Also: the bun job's
--max-concurrency 1Dropped as dead config.
--max-concurrencycaps how manytest.concurrent()tests run at once; the suite declares none and the job doesn't pass--concurrent, so it never gated anything — on bun 1.4.2 (whatlatestresolves to today) the suite runs in 9.27s / 9.25s / 9.11s without it versus 9.44s with it. Bun also doesn't implement web storage at all (typeof localStorage === "undefined") and uses its own runner, so it never had the Deno problem.It arrived in 6f87171, whose message ("ci: run bun concurrently") describes the opposite of what the diff did, and the bun job was green without it in the run immediately before. Happy to drop this commit if it was deliberate for something not visible in the logs.
Verification
lint:code,lint:types,fmt:check,lint:spellcheckand the Node suite (1522 pass) are all green. I could not runlint:special— it needs thetoolingdependency, which this sandbox's proxy blocks fromcodeload.github.com; it only regenerates types fromlib/, which this PR does not touch.No changeset: CI-only change, matching how previous
ci:commits were handled.🤖 Generated with Claude Code
https://claude.ai/code/session_016aGHrb1YaEvaGwNELGEHjF
Generated by Claude Code