ci: use process-safe coverage pools

This commit is contained in:
Tianyi Cui
2026-07-22 19:37:45 +08:00
parent 02f6d919c8
commit 17983d72bb
5 changed files with 56 additions and 11 deletions
@@ -16,7 +16,7 @@ The organization keeps twelve x64 larger-runner pools in the repo-restricted `ds
Production CI uses five larger-runner executions and one standard-runner aggregator. The primary Node inventory is not sharded:
- `node 24 / complete` uses one 96-core Linux runner. One checkout, setup, cache restore, and install feeds all 42 primary gates. `run-gates` starts up to 12 independent gates; ESLint uses 16 workers, coverage uses at most 12, and snapshot replay uses at most 8. Build starts as soon as the first short gates release scheduler slots, while snapshot replay and publication consumers retain explicit dependencies on emitted `lib/` output. Pull requests restore pnpm and ESLint caches without saving them, so cache compression and upload do not extend the required job; the master serial references refresh those caches outside the pull-request critical path. The read-only job does not persist checkout credentials and restores the stable pnpm store root directly, avoiding a separate store-discovery step.
- `node 24 / complete` uses one 96-core Linux runner. One checkout, setup, cache restore, and install feeds all 42 primary gates. `run-gates` starts up to 12 independent gates; ESLint uses 16 workers, coverage uses at most 12, and snapshot replay uses at most 8. Build starts as soon as the first short gates release scheduler slots, while snapshot replay and publication consumers retain explicit dependencies on emitted `lib/` output. Pull requests restore pnpm and ESLint caches without saving them, so cache compression and upload do not extend the required job; the master serial references refresh those caches outside the pull-request critical path. The read-only job does not persist checkout credentials.
- Node 22.19 and Node 26 use the 4- and 32-core Linux pools for their runtime compatibility smokes. Python 3.10 uses the 8-core Linux pool for the complete keyless SDK suite. These are environment contracts, not slices of the primary Node gate inventory.
- `windows node 24 / complete` uses one 32-core Windows runner. One setup feeds the required package build, required production site build, and complete observational portability inventory. Required failures fail the job; observational failures are reported as non-blocking. ESLint stays single-threaded because 16 ESLint workers took 174.54 seconds, coverage uses at most 12 workers, and the outer scheduler retains 16 slots. The job restores only the small master-refreshed ESLint cache and performs a clean pnpm install instead of restoring or saving the many-file package store.
@@ -42,7 +42,7 @@ The larger client package graph makes cache mechanics and scheduler pressure par
Two host effects remain part of the decision. A standard Node 26 job once spent 36 of its 67 seconds in `Set up job`, which is why environment contracts use distinct larger-runner pools instead of standard capacity. A Linux candidate spent 18 seconds registering a 50 KB Bubblewrap package because the hosted image scanned 202,507 package-database files. [`scripts/prepare-ci-bubblewrap.sh`](../../../../scripts/prepare-ci-bubblewrap.sh) instead verifies and extracts the pinned payload into the ephemeral runner directory, runs a functional confinement probe, and overlaps that preparation with dependency installation.
Coverage remains capped at 12 forks. Thirty-two forks crashed Node 24's CJS lexer twice, and a later 16-fork run reproduced the worker loss and invalid coverage result. Twelve forks preserve process headroom without becoming the single-host critical path.
Each coverage project is capped at 12 workers, and the process-bound project contains exactly four suite files. Thirty-two forks crashed Node 24's CJS lexer twice, and a later 16-fork run reproduced the worker loss and invalid coverage result. The single Vitest invocation therefore uses threads for the broad inventory and reserves forks for suites that exercise process-global state or `process` APIs. That narrow fork inventory includes the local bash process-plumbing suite: under aggregate gate contention its thread worker completed every test but intermittently missed the stdin-error callback needed for per-file function coverage. This preserves the suites' isolation contracts and deterministic coverage while avoiding forked execution for ordinary test files.
The workflow retains two manual measurement suites. `suite=larger-runner-benchmark` compares isolated critical lanes across every size, and `suite=consolidated-runner-benchmark` compares whole aggregates. Complete serial Linux, macOS, and Windows references run only when `master` moves; pull requests run only the optimized jobs.