Let only Test save the shared uv cache - #134
Conversation
astral-sh/setup-uv derives its cache key from the runner architecture, the runner image, the Python version and a hash of the dependency files. Documentation, Linter and the ubuntu-latest/3.10 leg of Test therefore all derive the same key, and when one push triggers them together they race to reserve it at save time. Only the first to finish wins; the losers log Failed to save: Unable to reserve cache with key setup-uv-2-..., another job may be creating this cache. The race is save-time only and harmless, since every loser would have written identical content, but it is noise in an otherwise green log. Giving each workflow its own cache-suffix would remove the collision by storing several copies of the same cache, which was rejected as wasteful in audeering/audeer#207. Keep the single shared key instead and make exactly one workflow the writer: Documentation, Linter and Publish set save-cache: false, so they still restore the cache but never save it, and Test remains the sole saver. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reviewer's guide (collapsed on small PRs)Reviewer's GuideConfigure GitHub Actions so that only the Test workflow writes the shared uv cache, while Documentation, Linter, and Publish continue to restore but no longer save, eliminating cache reservation races and warnings. File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- Since the design now relies on Test being the sole cache writer, consider making this explicit in
test.yml(e.g., a short comment and/or an explicitsave-cache: true) so future workflow edits don’t accidentally reintroduce multiple writers or disable saving entirely.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Since the design now relies on Test being the sole cache writer, consider making this explicit in `test.yml` (e.g., a short comment and/or an explicit `save-cache: true`) so future workflow edits don’t accidentally reintroduce multiple writers or disable saving entirely.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
@sourcery-ai Thanks — the concern is fair, but I'd rather not act on it in this PR. This is a straight port of audeering/audeer#207, which is merged and reviewer-approved. That PR touches exactly On the mechanics: There is also a working guard already in place. The three non-saving workflows each carry an explicit If @hagenw wants a comment in |
CI verification on bfabecc
19 of 20 checks pass. The one failure is Documentation, which fails identically on Linter — restores, then declines to saveSame shared key as before, restore still works, and the save step is gone — which is exactly the job that used to win the reservation and starve the others. Test — still the writerRun 31098242982,
Documentation shows |
There was a problem hiding this comment.
Pull request overview
This PR updates the GitHub Actions CI workflows to eliminate astral-sh/setup-uv cache save races across concurrently-started workflows by making only the Test workflow the cache writer (this PR disables saving in Documentation, Linter, and Publish while preserving restore behavior).
Changes:
- Disable
setup-uvcache saving in.github/workflows/doc.yml. - Disable
setup-uvcache saving in.github/workflows/linter.yml. - Disable
setup-uvcache saving in bothbuildanddeployjobs in.github/workflows/publish.yml.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| .github/workflows/doc.yml | Sets save-cache: false for setup-uv to avoid competing cache saves. |
| .github/workflows/linter.yml | Sets save-cache: false for setup-uv to avoid competing cache saves. |
| .github/workflows/publish.yml | Sets save-cache: false for both setup-uv steps to prevent save collisions (including future release-time collisions). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Problem
astral-sh/setup-uvderives its cache key from the runner architecture,the runner image, the Python version and a hash of the dependency files:
Nothing in that key identifies the workflow. Documentation
(ubuntu-latest, 3.10), Linter (ubuntu-latest, 3.10) and the
ubuntu-latest/3.10 leg of Test therefore derive a byte-identical key, and
one push starts all three at once. At save time they race to reserve it;
only the first to finish wins, and the losers log:
It is save-time only and harmless — the loser's content would have been
identical to the winner's — but it is a yellow annotation on an
otherwise green run.
Measured on
main: 1 annotation across the last six pushes, all ofit on the one push that has run since
setup-uvwas bumped to v9.0.0.Push a41c0bd ("Fix CI caching; bump checkout/setup-python off Node.js
20"),
17 check-runs, 1 warning:
build…-ubuntu-24.04-3.10.20-69a2db3b…uv cache saved with key: …— won the reservationbuild (ubuntu-latest, 3.10)…-ubuntu-24.04-3.10.20-69a2db3b…Failed to save: Unable to reserve cache …build (ubuntu-latest, 3.10)…-ubuntu-24.04-3.10.20-69a2db3b…setup-uv's post step ispost-if: success()All three logged
No GitHub Actions cache found for key: …69a2db3b…at restore, so all three were candidates to save. Documentation is a
third contender that is only masked today: it fails at
Test building documentationwithRuntimeError: Cannot find version '1.1.1' for database 'emodb', a pre-existing failure onmainunrelated to this PR,which aborts the job before the save step runs. Fix that and the same
push produces two warnings instead of one.
The five earlier pushes show zero such annotations; they ran
astral-sh/setup-uv@v5and their logs are past GitHub's retentionwindow, so the key they derived can no longer be inspected.
Fix
Keep the single shared key and make exactly one workflow the writer.
save-cache: false(an input ofsetup-uvv9.0.0, defaulttrue) turnsoff saving while leaving restore intact:
doc.yml— stops savinglinter.yml— stops savingpublish.yml— stops saving, bothsetup-uvsteps (buildanddeploy)test.yml— untouched, sole saverTest's matrix is a pure 3-OS x 5-Python cross product, so every leg
derives a distinct key and there is no race inside it. Everyone still
restores the same shared cache; only Test writes it.
As a side effect this also removes a collision that would have hit
publish.ymlon the next release: itsbuildjob is a 6-wayplatformmatrix that all runs on
ubuntu-latestwith Python 3.10 and the samedependency hash, so all six legs derive one key. With
save-cache: falsenone of them tries to save.
Trade-off
After a cache-key rotation — a
pyproject.tomloruv.lockchange, arunner image bump, a Python patch bump — the non-saving workflows miss
once, because the new key does not exist until the next Test run writes
it. That is a fresh
uvdownload in Documentation, Linter and Publishfor a single run: a second or two per job, once. From the following run
on, they restore normally.
Context
Ports audeering/audeer#207
(merged, reviewer-approved), where the single-saver design was chosen
over per-workflow
cache-suffixvalues — the latter removes the warningby storing several identical copies of the same cache, which was rejected
as wasteful.
audformatandaudbget the same treatment.This repo's
mainnever received the fix: its caching PR merged beforethe warning was diagnosed, which is why a41c0bd is the push that shows
it.
Test plan
(
Cannot find version '1.1.1' for database 'emodb'), which alsofails on
mainat a41c0bdUnable to reserve cacheannotations across every check-runon the head SHA
save-cache is false. Skipping save cache step.after restoringbuild (ubuntu-latest, 3.10)saves or hits the shared key