ci: bound the cargo cache to master, and stop the npm cache from crowding it out - #574
Conversation
…re times #572 added the cargo cache and named two things it could not settle. Both turned out to matter, and the measurement is now available: the repository holds 13.22 GB of Actions cache against a 10 GB limit -- 8.76 GB in ~113 npm caches spread over 50 refs, and 4.45 GB in the four cargo caches a single run of #572 wrote. So the limit binds today, and over it GitHub evicts least-recently-used. Cargo caches are the largest entries in the repository, which makes them the ones that vanish: without a bound, two pull requests in flight overflow the budget and the cache becomes a slower way of not caching. `save-if` bounds it. Pull requests restore and save nothing, so the total is one set rather than one set per branch in flight. That only works if something writes on master, and #572 recorded that nothing did: test_build.yml ran on `pull_request` alone, so every cache it wrote was scoped to a pull request branch and no other branch could read it. Every new pull request paid a full cold compile regardless. It now also runs on push to master, carrying the same paths-ignore, and cancel-in-progress is restricted to pull requests there for the reason test.yml already gives -- cancelling a master run because a second merge landed would discard the compile that was going to fill the cache. build.yml loses its cache entirely. Four more entries is another ~4.5 GB of a budget that is already over, spent on the least valuable minutes here: it is workflow_dispatch-only, runs about weekly, and nobody waits on a release build the way they wait on a pull request check. First place to add one back if the limit stops binding. 965 tests pass; svelte-check clean; all three workflows re-parsed as YAML. The one thing not knowable before merging: the first master run is a cold compile that writes the caches every later pull request reads. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… needs The 10 GB per-repository cache limit is being exceeded, and `save-if` in the previous commit bounds the cargo half of it. This is the other half, and it was the larger one: 113 npm cache entries, 8.76 GB, across 50 refs, most of them pull requests closed weeks earlier. Nothing can bound them. GitHub scopes caches by ref, so setup-node writes one per branch per platform, and there is no `save-if` equivalent -- deleting them by hand, as was done alongside this branch, buys a few weeks before they grow back. What they bought, measured on runs that hit them: `npm ci` in 6s on Linux, 9s on macOS, 15-17s on Windows. `cache: npm` caches `~/.npm`, the download directory, not `node_modules`; `npm ci` still runs and still links all 190 packages, so the saving is the registry fetch alone and the runners sit close to the registry. Seconds per job, in exchange for the budget that makes an eight-minute saving possible. Two assertions in scripts/ciCacheBudget.test.ts hold both halves: every rust-cache use restricts its save to master, and no workflow re-enables the npm cache. Both were mutation-checked -- removing `save-if` fails the first and not the second, re-adding `cache: npm` fails the second and not the first. Neither is a law of nature and the comments say so; the point is that the next change to either has to re-do the measurement rather than discover the ceiling again. 965 tests pass; svelte-check clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Measured. #575 is the first pull request that could read the caches this seeded, so the self-revert clause above can be settled. It works. Wall-clock time for a pull request is roughly halved. Same workflow configuration in both columns — the only difference is whether master had a cache to restore.
Wall clock is set by the slowest job, so what a contributor actually waits for went 17m30s → 8m51s. Step level, on Linux:
The So: no revert. The extra three-platform run per merge is buying about nine minutes off every pull request that follows it. |
Follow-up to #572, which added the cargo cache and named two things it could not settle. Both turned out to matter, and there is now a measurement.
The repository was already over its cache limit
Over the limit GitHub evicts least-recently-used, and the cargo caches are the largest entries here — so they are what disappears. Two pull requests in flight overflow the budget on their own.
#572 saved nothing, and that is measured, not predicted. #573's Linux job ran in 12m12s against the 12m14s baseline #572 was written from. A branch can only restore caches written on itself or its base,
test_build.ymlhad no push trigger, so every cache it wrote was scoped to a pull request branch that no other branch could read.Four changes
save-if: github.ref == 'refs/heads/master'in test.yml and test_build.yml. Pull requests restore and save nothing, so the total is one set rather than one set per branch in flight.test_build.yml now runs on push to master. This is not a separate optimisation — it is what
save-ifrequires. Restricting writes to master means master has to write, and it was not running there at all. Samepaths-ignore, andcancel-in-progressrestricted to pull requests for the reason test.yml already gives: cancelling a master run because a second merge landed would discard the compile that was going to fill the cache.build.yml loses its cache. Four more entries is another ~4.5 GB, spent on the least valuable minutes here —
workflow_dispatch-only, roughly weekly, and nobody waits on a release build the way they wait on a pull request check. Its jobs already finish in 12–16 minutes. The comment names it as the first place to add one back if the limit stops binding.cache: npmis removed. It was the larger consumer and nothing can bound it: caches are scoped by ref, so setup-node writes one per branch per platform with nosave-ifequivalent. Measured on runs that hit it,npm citook 6s on Linux, 9s on macOS, 15–17s on Windows — and it caches~/.npm, notnode_modules, sonpm cistill runs and still links all 190 packages. The saving is the registry fetch alone, from runners that sit close to the registry. Seconds per job, for the budget that makes an eight-minute saving possible.Stale caches from closed pull requests were also deleted by hand: 117 entries / 13.22 GB → 31 / 3.15 GB.
Cost, stated plainly
One extra three-platform run per merge to master. On a public repository the minutes are free; what it spends is macOS runner availability, which test_build.yml's own comment notes is the scarce pool. Against that, every pull request opened afterwards starts warm, and pull requests are pushed to far more often than master is merged into. Only the first master run is a full cold compile.
Plus 10–20 seconds per job for the npm fetch.
Tests
scripts/ciCacheBudget.test.tsholds both halves of the budget: every rust-cache use restricts its save to master, and no workflow re-enables the npm cache. Mutation-checked — removingsave-iffails the first and not the second; re-addingcache: npmfails the second and not the first.Neither is a law of nature, and the comments say so. The point is that the next change to either has to re-do the measurement rather than rediscover the ceiling.
npm testnpm run checkWhat still is not verified, and how to settle it
The first master run after this merges is a cold compile that writes the caches. Then open a trivial pull request and compare its Linux job against 12m14s.
If it has not moved meaningfully, the honest response is to revert the caching entirely rather than keep it as decoration — it costs budget, a new trigger, and four blocks of comment. #572 is already one example of caching that looked right and saved nothing.
🤖 Generated with Claude Code