ci: run every PostgreSQL shard on cargo-nextest, not just the pilot - #853
Conversation
#840 put ONE shard on nextest to measure it. Measured on merge_group run 32402124838, the pilot against its nearest neighbour: domain adapters B 45 targets nextest 6m domain adapters A 40 targets cargo 15m More tests, less than half the wall clock. That is the 2.35x from the original single-target benchmark, reproduced on real CI against a bigger shard, so the pilot has done its job. The remaining four shards move to the same hash-pinned installer and runner. WHAT THIS DOES NOT DO, and the measurement matters more than the change. The critical path of a merge_group CI run is not the shards: Backend — fmt / clippy / test / gates 17m <- critical path Test PostgreSQL — platform 15m Test PostgreSQL — domain adapters A 15m Test PostgreSQL — app 15m So taking the shards to ~6-7m leaves wall clock pinned near 17m by Backend, whose own 17m is an accumulation rather than a hot spot: 5m dev-auth PostgreSQL suites, 3m clippy, 3m console-app unit, ~4m setup, and 24 further steps at or under a minute. Splitting it is the next lever and a much larger ci.yml change; this one is proven and mechanical. A second measurement, taken locally while checking isolation: the platform shard runs 154 tests in 263s, and 263s of that is ONE test — `console-gate-writer-ownership::census_executes_against_postgres`. No runner change takes that shard below ~4.5m. Shards are not uniformly runner-bound, and the remaining gains after this are not evenly distributed. ISOLATION, which is the actual risk of this change and why it was piloted: nextest runs test binaries in parallel where cargo ran them serially, so a suite that shared state would newly fail. Verified locally before pushing — the platform shard passes 154/154 under nextest, with the cluster-global serial group in `.config/nextest.toml` doing its job. Ratchets moved UP: run-step coverage 126 -> 130 and the bypass matrix 378 -> 390, because each new install step goes through the same three mutations as every other run step. 61 preflight contract tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Executive Overview & Adversarial Quality AssessmentPR #853 rolls out The PR is exceptionally well-structured and disciplined: workflow steps use hash-pinned binary installations via 16-Lens Canonical Framework Analysis
Key Findings & Polish
Findings not addressable in the diff
🤖 [Reviewed] by Oyatie Anvil |
|
❌ Blocked — 12 finding(s) across 72 gates; 9 gate(s) produced no measurement.
🤖 [Blocked] by Oyatie Anvil |
The first run with every shard on nextest failed ONE test:
TIMEOUT [ 360.005s] console-gate-writer-ownership::
census_executes_against_postgres census_binds_to_an_executed_database
Not an isolation bug. That test is EIGHT sequential migrate+enforce cycles
against a Docker PostgreSQL in a single test function -- the mutation matrix
for the canonical-enforcement census -- at ~33s a cycle. I measured it at
263s locally and noted it in #853's own message; a hosted runner took it past
nextest's 120s x 3 = 360s terminate ceiling. cargo never enforced a ceiling,
which is why the shard was green before and is how a hang would also have
passed.
The ceiling is raised for THIS binary only, via a profile.ci override on
`binary(census_executes_against_postgres)`. The global 360s stays: it is what
catches a genuinely hung test, and widening it everywhere to fit one
known-slow suite would hide hangs in the other 153.
Proven:
- the filter selects the real binary (23 tests); a misspelt filter selects
zero, so a typo cannot silently leave the old ceiling in force;
- `cargo nextest run --profile ci` on that test: PASS in 174.9s, flagged
slow, not terminated;
- check-nextest-config and its tests pass; gate sweep 13/13.
The other four shards were already green: on the same run domain-a 6m,
app 7m, ontology 5m, domain-b 5m -- every shard now 5-8m, down from 13-15m.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Executive OverviewThis incremental update to PR #853 introduces a targeted During the rollout of 16-Lens Canonical Adversarial Review Matrix
Critical Risks & Recommendations
Findings not addressable in the diff
🤖 [Reviewed] by Oyatie Anvil |
Measured on merge_group run 32402124838: CI wall clock was ~19m and this one job was the 17m critical path. After #853 moved every PostgreSQL shard to nextest (now 4-8m each), Backend is the only thing left holding the number up. Its 17m is not a hot spot but an accumulation -- three steps are 757s of ~1,070s of work (dev-auth PG suites 355s, clippy 217s, console-app unit 185s) and the other 25 steps are all under 82s. So the lever is parallelism, not speed. The job becomes a three-leg strategy.matrix: cargo rustfmt, clippy, the eleven cargo gates, mutation suites, PR 473 contract tests, boot smoke (16 steps) buck-app platform-authz unit, console-app unit, OpenAPI drift, console-app inline PG (4 steps) buck-dev-auth the dev-auth feature PostgreSQL suites (1 step) Setup, the topology reconcile and collect-failures run on every leg (7 steps). Every one of the 28 steps lands on EXACTLY one leg or on all three -- verified by parsing the workflow, not by reading it. fail-fast is off so one red leg never hides the others, the same fail-slow rule the step layer already follows. WHY A MATRIX AND NOT SEPARATE JOBS. Splitting into new jobs was designed and refuted: it touches every pinned contract structure and creates two false-green surfaces -- a new job not wired into required-ci's `needs` runs and gates nothing, and a `steps.topology.outcome` condition carried into a job with no topology step skips forever while the job stays green. A matrix keeps the job id, its membership in required-ci, and every step's place in the bypass-mutation matrix exactly where they were. Ratchets do not move: run-step count and bypass-mutation count are unchanged (130 / 390 after #853), because no step was added, removed, or renamed. THE CACHE, and a pre-existing hole this closes. rust-cache has ONE writer by contract. It had two: migration-expand-contract carried `save-if: main` under a comment reading "The ONLY writer" -- copied from `backend` when that job was split out in #817 and false in place ever since -- and was absent from `cargoRustCacheJobs`, so the one-writer check never saw it. It is now restore-only and listed. With a matrix, "one writer" also stops being enough: three legs saving the same key race on every main push, and the textual `save-if: false` test cannot tell one leg saving from three. The writer is now exactly the cargo leg, and a new assertion requires the writer's save-if to name exactly one leg. TWO NEW ASSERTIONS, both mutation-proven, both from the refutation: a stale leg literal (rename a leg in the matrix, leave a step's `if`) -> "names matrix leg 'buck-app', which is not in strategy.matrix.leg -- that step would run on ZERO legs" the writer's save-if no longer leg-scoped -> "backend's rust-cache save-if must name exactly ONE matrix leg" restored -> preflight exit 0 Without the first, a coordinated matrix rename plus hash recompute could leave a proof step running on no leg at all while every leg, required-ci, and check-executed-tests stayed green. Preflight: exit 0. Contract suite: 61/61. Gate sweep 13/13. check-workflow-hardening and check-executed-tests exit 0. clippy and the first gate remain adjacent on the cargo leg, so the gate-order lock holds. WHAT IS PREDICTED, NOT MEASURED. Per-leg setup is ~55s. The design puts the legs at cargo ~6.7m, buck-dev-auth ~7.0m, buck-app ~8-10m -- the last because app-unit's 185s was measured WARM after dev-auth had built the platform crates in the same daemon, and buck-out does not cross runners. If that holds, Backend drops from 17m to ~8-10m and the shards (4-8m) and Backend become comparable, putting wall clock near 10m. That is a prediction until the first merge_group run measures it. 5m is not reachable by this change alone: the platform shard carries one 263s test, and the buck-app leg pays a cold platform build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Measured outcome. This PR predicted the shards would reach ~6–7m and — explicitly — that wall clock would not move until Backend was split. Both held. Shards across merge_group runs since: app 7–8m, platform 8–15m (noisy), ontology 5m, domain-a 4–6m, domain-b 5–6m — from 13–15m before. Wall clock stayed ~19m until #855 split Backend, exactly as stated. It is now 14m after #855 and #857. One correction to the reasoning here, for anyone reading this as precedent: the 2.35× runner ratio is real for these PostgreSQL suites, but it does not generalise to unit-test jobs. I measured a 27× cargo-vs-nextest ratio on the domain-unit lib sweep locally and nearly shipped a PR on it — that turned out to be a macOS process-spawn artifact applied to ~1s of real execution on the runner, where that job's 714s was 685s of compilation. See #857: the fix there was a rust-cache key, not a runner. |
…853) #840 put **one** shard on nextest to measure it. Measured on merge_group run 32402124838, the pilot against its nearest neighbour: | shard | targets | runner | wall | |---|---|---|---| | domain adapters **B** | 45 | **nextest** | **6m** | | domain adapters A | 40 | cargo | 15m | More tests, less than half the wall clock — the 2.35× benchmark reproduced on real CI against a bigger shard. The pilot has done its job, so the remaining four shards move to the same hash-pinned installer and runner. ## What this does not do — and the measurement matters more than the change The critical path of a merge_group run is **not** the shards: ``` Backend — fmt / clippy / test / gates 17m <- critical path Test PostgreSQL — platform 15m Test PostgreSQL — domain adapters A 15m Test PostgreSQL — app 15m ``` Taking the shards to ~6–7m leaves wall clock pinned near **17m by Backend**, whose own 17m is an accumulation rather than a hot spot: 5m dev-auth PG suites, 3m clippy, 3m console-app unit, ~4m setup, and 24 further steps at or under a minute. Splitting it is the next lever and a much larger ci.yml change. A second measurement, taken locally while checking isolation: the **platform shard runs 154 tests in 263s, and 263s of that is one test** — `console-gate-writer-ownership::census_executes_against_postgres`. No runner change takes that shard below ~4.5m. Shards are not uniformly runner-bound, and the remaining gains are not evenly distributed. ## Isolation — the actual risk, and why it was piloted nextest runs test binaries in **parallel** where cargo ran them serially, so a suite sharing state would newly fail. Verified locally before pushing: the platform shard passes **154/154** under nextest, with the cluster-global serial group in `.config/nextest.toml` doing its job. Ratchets moved **up**: run-step coverage 126 → 130, bypass matrix 378 → 390, because each new install step goes through the same three mutations as every other. 61 preflight contract tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Measured on merge_group run 32402124838: CI wall clock ~19m, and this one job was the **17m critical path**. After #853 moved every PostgreSQL shard to nextest (now 4–8m each), Backend is the only thing holding the number up. Its 17m isn't a hot spot — it's an accumulation. Three steps are **757s of ~1,070s** (dev-auth PG suites 355s, clippy 217s, console-app unit 185s); the other 25 steps are all under 82s. So the lever is parallelism, not speed. ## Three legs | leg | steps | |---|---| | `cargo` | rustfmt, clippy, the 11 cargo gates, mutation suites, PR 473 tests, boot smoke (16) | | `buck-app` | platform-authz unit, console-app unit, OpenAPI drift, console-app inline PG (4) | | `buck-dev-auth` | the dev-auth feature PostgreSQL suites (1) | Setup, topology reconcile and collect-failures run on every leg (7). **Every one of the 28 steps lands on exactly one leg or on all three** — verified by parsing the workflow, not by reading it. `fail-fast: false`, so one red leg never hides the others. ## Why a matrix and not separate jobs Separate jobs were designed and **refuted**: they touch every pinned contract structure and create two false-green surfaces — a new job not wired into `required-ci`'s `needs` runs and gates nothing; a `steps.topology.outcome` condition carried into a job with no topology step skips forever while the job stays green. A matrix keeps the job id, its `required-ci` membership, and every step's place in the bypass-mutation matrix exactly where they were. **Ratchets do not move** (130 / 390 after #853): no step was added, removed, or renamed. ## The cache — and a pre-existing hole this closes rust-cache has *one writer* by contract. **It had two.** `migration-expand-contract` carried `save-if: main` under a comment reading *"The ONLY writer"* — copied from `backend` when that job was split out in #817, and false in place ever since — and was absent from `cargoRustCacheJobs`, so the one-writer check never saw it. Now restore-only and listed. With a matrix, "one writer" also stops being enough: three legs saving the same key race on every main push, and the textual `save-if: false` test can't tell one leg from three. The writer is now exactly the `cargo` leg, enforced by a new assertion. ## Two new assertions, both mutation-proven, both from the refutation | mutation | result | |---|---| | rename a leg in the matrix, leave a step's `if:` stale | *"names matrix leg 'buck-app', which is not in strategy.matrix.leg — that step would run on ZERO legs"* | | writer's `save-if` no longer leg-scoped | *"save-if must name exactly ONE matrix leg"* | | restored | preflight exit 0 | Without the first, a coordinated matrix rename plus hash recompute could leave a proof step running on **no leg at all** while every leg, `required-ci`, and `check-executed-tests` stayed green. Preflight exit 0 · contract suite **61/61** · gate sweep 13/13 · workflow-hardening and executed-tests exit 0 · clippy→first-gate adjacency holds on the cargo leg. ## Predicted, not measured Per-leg setup ~55s. Design estimate: cargo ~6.7m, buck-dev-auth ~7.0m, buck-app ~8–10m — the last because app-unit's 185s was measured **warm** after dev-auth had built the platform crates in the same daemon, and buck-out doesn't cross runners. If that holds, Backend drops 17m → ~8–10m and wall clock lands near **10m**. That is a prediction until this PR's own CI run measures it. **5m is not reachable by this change alone**: the platform shard carries one 263s test, and the buck-app leg pays a cold platform build. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
#840 put one shard on nextest to measure it. Measured on merge_group run 32402124838, the pilot against its nearest neighbour:
More tests, less than half the wall clock — the 2.35× benchmark reproduced on real CI against a bigger shard. The pilot has done its job, so the remaining four shards move to the same hash-pinned installer and runner.
What this does not do — and the measurement matters more than the change
The critical path of a merge_group run is not the shards:
Taking the shards to ~6–7m leaves wall clock pinned near 17m by Backend, whose own 17m is an accumulation rather than a hot spot: 5m dev-auth PG suites, 3m clippy, 3m console-app unit, ~4m setup, and 24 further steps at or under a minute. Splitting it is the next lever and a much larger ci.yml change.
A second measurement, taken locally while checking isolation: the platform shard runs 154 tests in 263s, and 263s of that is one test —
console-gate-writer-ownership::census_executes_against_postgres. No runner change takes that shard below ~4.5m. Shards are not uniformly runner-bound, and the remaining gains are not evenly distributed.Isolation — the actual risk, and why it was piloted
nextest runs test binaries in parallel where cargo ran them serially, so a suite sharing state would newly fail. Verified locally before pushing: the platform shard passes 154/154 under nextest, with the cluster-global serial group in
.config/nextest.tomldoing its job.Ratchets moved up: run-step coverage 126 → 130, bypass matrix 378 → 390, because each new install step goes through the same three mutations as every other. 61 preflight contract tests pass.
🤖 Generated with Claude Code