Repository navigation
Cap hung rust-cache restores so they cannot stall the merge queue - #1730
Conversation
On 2026-10-26 the yarn-classic job of merge-queue head #1719 sat in its `Cache cargo` (Swatinem/rust-cache) restore for 14+ minutes. The job normally takes 2-3 minutes, and the 12 entries queued behind it were blocked until the run was cancelled by hand. Without that, the job would have run to its 40-minute timeout and then evicted #1719. A healthy restore takes under 30 s, and a cache miss only costs a cold build, so the restore is best-effort. Give every rust-cache step in ci.yml a 5-minute step timeout and continue-on-error. A hung restore now fails only that step, and the job builds without the cache instead of stalling the queue. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZ8V19imL1WHdq6hZdoums
|
bugbot run Generated by Claude Code |
The fail-fast contract test rejected any `continue-on-error` in ci.yml. Its concern is a job-level key, which would let ci-ok pass despite a failed job. The capped `Cache cargo` steps set it at step level, which leaves the job's conclusion to its other steps, so narrow the check to job-level keys (four-space indent). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZ8V19imL1WHdq6hZdoums
|
Burn-down agent: Ready for review at
Generated by Claude Code |
Review follow-ups: - The contract test allowed continue-on-error on any step, so a test or build step tolerating failure would slip past it. Allow it only on rust-cache / actions/cache/restore steps that also carry a timeout, and require every such restore in ci.yml to be capped. - Cap the macOS vexctl actions/cache/restore the same way. If that restore times out on main, skip the save step, which would otherwise run with an empty key and fail the job. - Correct the comment: the hang was in yarn-classic-matrix, and an uncapped restore runs to that job's own timeout. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZ8V19imL1WHdq6hZdoums
Final review briefWhat it does. Gives every Risk: low. CI-only. No test step, no required check name and no job-level key changes. The worst case is a slower cold build when a restore times out. Look here
Verified
Changes I made: none. Open questions (non-blocking)
Auto-merge is armed, so approving sends this straight to the merge queue. Generated by Claude Code |
The invariant said the job has no continue-on-error, but its capped Cache cargo restore now carries one. Say what still holds: none on the job or its e2e steps. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZ8V19imL1WHdq6hZdoums
Problem
At 15:24 UTC on 2026-10-26, the merge_group run for queue head #1719 (run 40348796795) hung in
yarn-classic 1.22.22(job). The job sat in theCache cargostep (Swatinem/rust-cache restore) for over 14 minutes. Every earlier step had finished in under 30 s.Cache cargosteps finished in 24 s or less (median 1 s).timeout-minutes: 40. That would have evicted Fix Deno global npm tools missed by -g (#444) #1719 and thrown away every group behind it.Root cause
The restore is a transient infrastructure call to the cache backend on Depot runners. It has no timeout of its own. rust-cache already treats a restore error as a cache miss, but a restore that never returns blocks the job until the job-level timeout.
Fix
Every
Swatinem/rust-cachestep inci.yml(11 steps), plus the macOS vexctlactions/cache/restore, now hastimeout-minutes: 5andcontinue-on-error: true. The vexctl save step is skipped when that restore did not succeed, so it never saves under an empty key on main. A hung restore fails only that step after 5 min, and the job carries on with a cold build. The 5-minute cap is more than 10x the slowest healthy restore seen. No other step changes behaviour, andsave-ifis untouched.scripts/tests/test_merge_queue_fail_fast.pyhad a contract test that rejected anycontinue-on-errorinci.yml. The test exists because a job-level key would letci-okpass despite a failed job. It still rejects job-level keys. It now also allowscontinue-on-erroronly on rust-cache oractions/cache/restoresteps that carry a timeout, and requires every such restore inci.ymlto be capped. I checked that addingcontinue-on-errortoRun clippy, or removing a cap, fails the test.merge-queue-fail-fast.pyreads job conclusions, so its fail-fast logic is unaffected. The script's docstring now says "job-level".The compatibility workflows (
*-compatibility.yml, 12 more rust-cache steps) are not required checks and are left for a follow-up.Proof
ci.ymlcarries both keys.actionlintreports 170 findings before and after the change, all pre-existing matrix-property warnings, so none are new.zizmor --offlinegives 59 findings both before and after.python3 -B -m unittest discover -s scripts/tests: 373 tests OK, which is the suite thelint-ecosystemsjob runs.Required check names (
ci-ok,clippy) are unchanged. A step that times out is reported as failed, but because ofcontinue-on-errorthe job (and soci-ok) still passes./code-review highfindings not fixedSEGMENT_DOWNLOAD_TIMEOUT_MINSinstead. Its default is already 10 min, and the 2026-10-26 hang ran past 14 min, so that setting didn't catch it. A step timeout covers every phase of the restore.Cache cargoon Windows or macOS took 19 s, and Windows took 24 s in the merge queue.*-compatibility.ymlworkflows. They are not required checks and are out of scope here.https://claude.ai/code/session_01LZ8V19imL1WHdq6hZdoums
Generated by Claude Code