Skip to content

warm_cache: only delete the caches of the ref it runs on - #83

Open
oliviermattelaer wants to merge 1 commit into
mainfrom
claude/warm-cache-ref-scoped
Open

warm_cache: only delete the caches of the ref it runs on#83
oliviermattelaer wants to merge 1 commit into
mainfrom
claude/warm-cache-ref-scoped

Conversation

@oliviermattelaer

Copy link
Copy Markdown
Contributor

The problem

The cache-refresh jobs delete a cache before saving it, since actions/cache never overwrites an existing key. The deletes were written without --ref:

gh cache delete heptools-${{ env.CACHE_KEY }} --repo $GITHUB_REPOSITORY || true

gh cache delete <key> matches the key across every ref unless one is given — --ref exists precisely to narrow it, and the REST API documents ref as an optional restriction on an otherwise key-wide delete.

The save, by contrast, is ref-scoped. So a refresh deletes every branch's copy and puts back one visible only to the ref that ran it. On main that is harmless, because every branch can read a default-branch cache. Anywhere else it is destructive.

It already happened

A manual dispatch on madspin_branch left the shared caches held solely by that branch:

heptools-ubuntu24   ref=refs/heads/madspin_branch
delphes-ubuntu24    ref=refs/heads/madspin_branch
root-ubuntu24       ref=refs/heads/madspin_branch

With fail-on-cache-miss: true on the restore actions, every branch except that one then failed before running a single test:

##[error]Failed to restore cache entry. Exiting as fail-on-cache-miss is set.
         Input key: heptools-ubuntu24

On one branch that took out four workflows — acceptance (mg7), acceptance (madevent), acceptance (rest) and parallel — while madspin_branch itself stayed green, which is what makes the cause easy to misread as a code regression.

That the delete is responsible, rather than the save, is visible in the cache list: 12 other keys are currently held under several refs at once (one madspace-* key exists under refs/heads/main, refs/heads/madnis-paper and refs/pull/68/merge simultaneously). Saving on a branch does not displace another branch's copy — so heptools-ubuntu24 surviving under one ref only means the others were deleted.

The existing ref guard does not prevent this, and shouldn't have to:

if: (github.ref_name == 'main' || github.ref_name == 'test_ci'
     || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule')

workflow_dispatch and schedule are deliberately accepted from any branch, so a cache can be rebuilt without pushing to main.

The change

Pass --ref "${{ github.ref }}" to all thirteen live deletes. A refresh then replaces only what its own ref holds: on main it behaves exactly as before, and a dispatch on a feature branch gives that branch its own copy while leaving the main-scoped one intact.

warm_cache never runs on a pull_request event — only push, schedule and workflow_dispatch — so github.ref is always refs/heads/<branch> here. The two commented-out deletes are left alone. YAML re-parses; 13 insertions, 13 deletions.

Alternative considered

Tightening the guard so workflow_dispatch/schedule also require main. Rejected: it removes the ability to rebuild a cache from a branch, and it leaves the sharp edge in place for whoever re-adds that ability later. Scoping the delete makes the damage structurally impossible instead.

Not fixed by this PR

The caches are still in the broken state. This change prevents recurrence; it does not repair what the earlier dispatch removed. A warm_cache run on main is needed to recreate the main-scoped copies, after which the failed jobs on other branches will pass on re-run.

The refresh jobs delete a cache before saving it, because actions/cache never
overwrites an existing key. The delete was written without --ref, and
`gh cache delete <key>` matches the key across every ref unless one is given,
so a refresh deletes the copy held by every branch, not just its own.

The save is ref scoped, so what comes back exists only under the ref that ran
the workflow. That is fine on main, whose caches every branch can read, and
destructive anywhere else: the shared copy is gone and the replacement is
visible only to the branch that made it. With fail-on-cache-miss set on the
restore actions, every other branch then fails before running a test.

That is not hypothetical. A manual dispatch on madspin_branch left
heptools-ubuntu24, delphes-ubuntu24 and root-ubuntu24 held solely by
refs/heads/madspin_branch, and every branch except that one failed on

    Failed to restore cache entry. Exiting as fail-on-cache-miss is set.
    Input key: heptools-ubuntu24

The ref guard on the refresh job does not prevent it: workflow_dispatch and
schedule are accepted from any branch, by design, so that a cache can be
rebuilt without pushing to main.

Pass --ref "${{ github.ref }}" to all thirteen live deletes. A refresh now
replaces only what its own ref holds, and a dispatch on a feature branch gives
that branch its own copy while leaving the main-scoped one intact. The two
commented-out deletes are left as they are.

warm_cache never runs on a pull_request event -- only push, schedule and
workflow_dispatch -- so github.ref is always refs/heads/<branch> here.

Note this does not repair the current state: a run on main is still needed to
recreate the main-scoped caches that the madspin_branch dispatch removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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