Skip to content

Let only Test save the shared uv cache - #134

Merged
ChristianGeng merged 1 commit into
mainfrom
fix/cache-single-saver
Aug 6, 2026
Merged

Let only Test save the shared uv cache#134
ChristianGeng merged 1 commit into
mainfrom
fix/cache-single-saver

Conversation

@ChristianGeng

Copy link
Copy Markdown
Member

Problem

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:

setup-uv-2-x86_64-unknown-linux-gnu-ubuntu-24.04-3.10.20-69a2db3b0c51eec1981aed13262a263eaefdcc0830957d5848dfa77c059a7219

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:

Failed to save: Unable to reserve cache with key setup-uv-2-x86_64-unknown-linux-gnu-ubuntu-24.04-3.10.20-69a2db3b0c51eec1981aed13262a263eaefdcc0830957d5848dfa77c059a7219, another job may be creating this cache.

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 of
it on the one push that has run since setup-uv was bumped to v9.0.0.
Push a41c0bd ("Fix CI caching; bump checkout/setup-python off Node.js
20"
),
17 check-runs, 1 warning:

Workflow Job Derived key Outcome Run
Linter build …-ubuntu-24.04-3.10.20-69a2db3b… uv cache saved with key: … — won the reservation 30999400955
Test build (ubuntu-latest, 3.10) …-ubuntu-24.04-3.10.20-69a2db3b… Failed to save: Unable to reserve cache … 30999401311
Documentation build (ubuntu-latest, 3.10) …-ubuntu-24.04-3.10.20-69a2db3b… no save attempt — the job fails earlier for an unrelated reason (see below), and setup-uv's post step is post-if: success() 30999400946

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 documentation with RuntimeError: Cannot find version '1.1.1' for database 'emodb', a pre-existing failure on main unrelated 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@v5 and their logs are past GitHub's retention
window, 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 of setup-uv v9.0.0, default true) turns
off saving while leaving restore intact:

  • doc.yml — stops saving
  • linter.yml — stops saving
  • publish.yml — stops saving, both setup-uv steps (build and deploy)
  • test.yml — untouched, sole saver

Test'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.yml on the next release: its build job is a 6-way platform
matrix that all runs on ubuntu-latest with Python 3.10 and the same
dependency hash, so all six legs derive one key. With save-cache: false
none of them tries to save.

Trade-off

After a cache-key rotation — a pyproject.toml or uv.lock change, a
runner 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 uv download in Documentation, Linter and Publish
for 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-suffix values — the latter removes the warning
by storing several identical copies of the same cache, which was rejected
as wasteful.

audformat and audb get the same treatment.

This repo's main never received the fix: its caching PR merged before
the warning was diagnosed, which is why a41c0bd is the push that shows
it.

Test plan

  • All checks green, except the pre-existing Documentation failure
    (Cannot find version '1.1.1' for database 'emodb'), which also
    fails on main at a41c0bd
  • Zero Unable to reserve cache annotations across every check-run
    on the head SHA
  • Documentation, Linter show save-cache is false. Skipping save cache step. after restoring
  • Test's build (ubuntu-latest, 3.10) saves or hits the shared key

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>
@sourcery-ai

sourcery-ai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Configure 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

Change Details Files
Disable uv cache saving in non-Test workflows to avoid cache reservation races while retaining cache restore behavior.
  • Add save-cache: false input to the uv setup step in the Publish workflow build job
  • Add save-cache: false input to the uv setup step in the Publish workflow deploy job
  • Add save-cache: false input to the uv setup step in the Documentation workflow
  • Add save-cache: false input to the uv setup step in the Linter workflow
.github/workflows/publish.yml
.github/workflows/doc.yml
.github/workflows/linter.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 explicit save-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.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@ChristianGeng

Copy link
Copy Markdown
Member Author

@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 doc.yml, linter.yml and publish.yml, leaves test.yml untouched, and adds no marker comment. audformat and audb are getting the same change, so keeping the four diffs identical is a large part of the value here — a comment in this repo only would make the rollout inconsistent.

On the mechanics: save-cache: true is already the default in astral-sh/setup-uv v9.0.0, so writing it out changes no behaviour. It would read as if it were switching something on when it is not.

There is also a working guard already in place. The three non-saving workflows each carry an explicit save-cache: false, which is a visible marker at every site where the invariant could be broken; and the failure mode is loud rather than silent — drop the sole saver and the shared key is never written, so every job logs No GitHub Actions cache found for key: … on each run.

If @hagenw wants a comment in test.yml I'll add it here, but I'd then add the same one in audeer, audformat and audb so the four stay in step.

@ChristianGeng

Copy link
Copy Markdown
Member Author

CI verification on bfabecc

Unable to reserve cache annotations: 0, across all 20 check-runs on the head SHA. Was 1 on main at a41c0bd.

19 of 20 checks pass. The one failure is Documentation, which fails identically on main at a41c0bd and is unrelated to this PR:

FileNotFoundError: [Errno 2] No such file or directory: '/home/runner/audb/emodb/1.1.1/db.pkl'
RuntimeError: Cannot find version '1.1.1' for database 'emodb'.

Linter — restores, then declines to save

Run 31098243119:

  save-cache: false
Trying to restore cache from GitHub Actions cache with key: setup-uv-2-x86_64-unknown-linux-gnu-ubuntu-24.04-3.10.20-69a2db3b…
Cache restored successfully
cache restored from GitHub Actions cache with key: setup-uv-2-x86_64-unknown-linux-gnu-ubuntu-24.04-3.10.20-69a2db3b…
save-cache is false. Skipping save cache step.

Same 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 writer

Run 31098242982, build (ubuntu-latest, 3.10):

  save-cache: true
Cache hit for: setup-uv-2-x86_64-unknown-linux-gnu-ubuntu-24.04-3.10.20-69a2db3b…
Cache restored successfully
Cache hit occurred on key setup-uv-2-x86_64-unknown-linux-gnu-ubuntu-24.04-3.10.20-69a2db3b…, not saving cache.

save-cache is still true here. It does not write on this run because the key already exists — main populated it — and a hit means there is nothing new to store. On the next key rotation it is the one job that will write it.

Documentation shows save-cache: false and its restore attempt, but no Skipping save cache step. line, because it aborts at the emodb failure above and setup-uv's post step is post-if: success(). That is also why it produced no warning on main despite deriving the same key: it never reached its save step. Once the emodb failure is fixed it becomes a third contender for that key — and this PR is what keeps that from turning into a second warning.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-uv cache saving in .github/workflows/doc.yml.
  • Disable setup-uv cache saving in .github/workflows/linter.yml.
  • Disable setup-uv cache saving in both build and deploy jobs 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.

@ChristianGeng
ChristianGeng requested a review from hagenw August 6, 2026 11:46
@ChristianGeng
ChristianGeng merged commit 308bd9e into main Aug 6, 2026
20 of 21 checks passed
@ChristianGeng
ChristianGeng deleted the fix/cache-single-saver branch August 6, 2026 12:33
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.

3 participants