Skip to content

ci: bound the cargo cache to master, and stop the npm cache from crowding it out - #574

Merged
PathGao merged 2 commits into
masterfrom
ci/bound-the-cargo-cache
Aug 10, 2026
Merged

ci: bound the cargo cache to master, and stop the npm cache from crowding it out#574
PathGao merged 2 commits into
masterfrom
ci/bound-the-cargo-cache

Conversation

@PathGao

@PathGao PathGao commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

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

active:  13.22 GB / 117 entries        limit: 10 GB

  npm    113 entries   8.76 GB    across 50 refs — mostly closed PRs
  rust     4 entries   4.45 GB    written by one run of #572

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.yml had 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-if requires. Restricting writes to master means master has to write, and it was not running there at all. Same paths-ignore, and cancel-in-progress restricted 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: npm is 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 no save-if equivalent. Measured on runs that hit it, npm ci took 6s on Linux, 9s on macOS, 15–17s on Windows — and it caches ~/.npm, not node_modules, so npm ci still 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.ts holds both halves of the budget: every rust-cache use restricts its save to master, and no workflow re-enables the npm cache. 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 rediscover the ceiling.

npm test 965 pass
npm run check 675 files, 0 errors
YAML re-parse triggers confirmed on all changed workflows

What 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

PathGao and others added 2 commits August 10, 2026 20:45
…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>
@PathGao PathGao changed the title ci: write the cargo cache on master only, and stop writing it four more times ci: bound the cargo cache to master, and stop the npm cache from crowding it out Aug 10, 2026
@PathGao
PathGao merged commit b100697 into master Aug 10, 2026
4 checks passed
@PathGao
PathGao deleted the ci/bound-the-cargo-cache branch August 10, 2026 13:17
@PathGao

PathGao commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator Author

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.

job cold (master seeding, this PR) warm (#575)
build-test linux 12m49s 7m08s −44%
build-test windows 16m03s 8m16s −48%
build-test macos 17m30s 8m51s −49%
test ~4m50s 2m43s −44%

Wall clock is set by the slowest job, so what a contributor actually waits for went 17m30s → 8m51s.

Step level, on Linux:

cache rust dependencies    26s     ← the cost
run cargo test           151s →  15s    −90%
Build app                477s → 291s    −39%
Post cache rust deps       0s     ← save-if working: the PR wrote nothing

Post cache rust dependencies: 0s on all three platforms is the part worth noting separately: pull requests restore and save nothing, which is what keeps the total at one set. Cache usage is 4.45 GB against the 10 GB limit, down from 13.22 GB before this.

The cargo test and Build app figures also answer the open question about extrapolating between profiles. The debug-profile step fell 90% and the release-profile step 39% — the ratio does not carry across, which is why this was measured rather than predicted.

So: no revert. The extra three-platform run per merge is buying about nine minutes off every pull request that follows it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant