Skip to content

Let only Test save the shared uv cache - #592

Merged
ChristianGeng merged 2 commits into
mainfrom
fix/cache-single-saver
Aug 6, 2026
Merged

Let only Test save the shared uv cache#592
ChristianGeng merged 2 commits into
mainfrom
fix/cache-single-saver

Conversation

@ChristianGeng

@ChristianGeng ChristianGeng commented Aug 6, 2026

Copy link
Copy Markdown
Member

Problem

Every workflow installs uv with astral-sh/setup-uv.
The action derives its cache key from architecture, runner image, Python version
and a hash of the dependency files:

setup-uv-2-x86_64-unknown-linux-gnu-ubuntu-24.04-3.12.13-ed955f29...

Nothing in that key identifies the workflow or the job, so every job on the same
OS with the same Python version derives the same key.
When several such jobs run from one push they all try to reserve that key at save
time, the first to finish wins, and the rest log:

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

It is save-time only and harmless — the losing job's content would have been
identical — but it surfaces as a yellow warning annotation on the check run.

main has it. The most recent push (d1f07f7)
produced two colliding key groups:

Workflow · job key Python Outcome
Linter · build 3.12.13 saved the key
Documentation · build (ubuntu-latest, 3.12) 3.12.13 restore miss, never reached save (setup-uv is post-if: success() and this job failed on an unrelated linkcheck timeout)
Test · build (ubuntu-latest, 3.12) 3.12.13 ⚠️ Unable to reserve cache
Test · build (ubuntu-latest, 3.10) 3.10.20 saved the key
Test · build (ubuntu-latest, 3.10, pandas==2.1.4) 3.10.20 ⚠️ Unable to reserve cache

Measured frequency: 2 of the 61 uv-installing jobs across the last six commits on
main, and both of them on that one push.
The five commits before it ran setup-uv@v5, whose key
(setup-uv-1-…-no-dependency-glob) was already populated, so every job got a
cache hit and logged not saving cache — no save attempt, no race.
The collision is therefore structural but only visible when the key is fresh:
it returns on every key rotation (uv major version, runner image, Python patch
release, dependency change), which is exactly what d1f07f7 did by moving to v9.

Fix

Single-saver. Keep the one shared key and make exactly one writer:
Documentation, Linter and Publish pass save-cache: false
(an input of setup-uv v9.0.0, default true), which leaves Test as the only
workflow that writes.

That alone was not enough here, and the second commit says why.
Test in this repository declares ubuntu-latest + Python 3.10 twice — once
plain and once with pandas==2.1.4 — so those two legs share a key with each
other
, inside one workflow.
Measured on this PR with a cold key: both legs missed, the pandas leg saved,
and the plain leg emitted the one remaining warning.
So the pandas leg now skips the save as well, via
save-cache: ${{ !matrix.pandas }}true for every leg that does not set
matrix.pandas, false for the one that does — leaving exactly one writer in
Test.

Measuring this needed care: main had already populated the v9 keys, so the
first run of this PR got a cache hit on every job, nothing tried to save,
and zero warnings proved nothing.
The numbers above come from re-running all three workflows after deleting the
ubuntu-24.04 3.10.20 and 3.12.13 uv caches, which reproduces the cold-key
conditions of d1f07f7.

actions/cache steps (the emodb test data) are untouched; this is only about
the uv cache.

Trade-off

After a cache-key rotation the non-saving workflows miss the cache once, until
the next Test run populates the new key.
That costs a second or two per job, for one run.

Context

Ports audeering/audeer#207 (merged, reviewer-approved).
opensmile-python and audformat get the same treatment.
main here never got the fix because
d1f07f7
merged before the warning was diagnosed.

Giving each workflow its own cache via cache-suffix was considered and rejected
in audeer#207: it multiplies near-identical caches for no benefit.

Test plan

  • CI green - all 11 checks pass on
    8d3b87d
  • Zero Unable to reserve cache annotations on the head commit, verified
    with the uv caches deleted first so that every job really attempts a save

For the record, on the colliding keys of that run:

Job save-cache Restore Post step
Documentation - build (ubuntu-latest, 3.12) false miss save-cache is false. Skipping save cache step.
Linter - build false miss save-cache is false. Skipping save cache step.
Test - build (ubuntu-latest, 3.12) true miss uv cache saved with key: setup-uv-2-...-3.12.13-...
Test - build (ubuntu-latest, 3.10) true miss uv cache saved with key: setup-uv-2-...-3.10.20-...
Test - build (ubuntu-latest, 3.10, pandas==2.1.4) false miss save-cache is false. Skipping save cache step.

One writer per key, no reservation contest.

An earlier attempt on the first commit alone, with the same cold key, still
warned once - the two 3.10 legs - which is what the second commit fixes.

astral-sh/setup-uv derives its cache key from architecture, runner
image, Python version and a hash of the dependency files, so every job
that runs the same OS and the same Python version derives the same key.
When such jobs run from one push, they all try to reserve that key at
save time; the first to finish wins and the rest log

  Failed to save: Unable to reserve cache with key setup-uv-2-...,
  another job may be creating this cache.

The warning is save-time only and harmless -- the losing job would have
written identical content -- but it is log noise.

Giving each workflow its own cache via cache-suffix was considered and
rejected in audeering/audeer#207: it multiplies near-identical caches
for no benefit. Instead keep the one shared key and make exactly one
writer. Documentation, Linter and Publish set save-cache: false; Test
keeps the default and is the sole writer.

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

Centralizes uv cache writes in CI so only a single job (a specific Test matrix leg) saves the shared uv cache, eliminating Unable to reserve cache warnings caused by concurrent saves while preserving a shared cache key across workflows.

Sequence diagram for shared uv cache writes across workflows

sequenceDiagram
    participant Documentation
    participant Linter
    participant Publish
    participant Test_plain_3_10
    participant Test_pandas_3_10
    participant Test_3_12
    participant setup_uv
    participant GitHubActionsCache

    Documentation->>setup_uv: setup-uv v9.0.0 (save-cache: false)
    setup_uv->>GitHubActionsCache: restore cache

    Linter->>setup_uv: setup-uv v9.0.0 (save-cache: false)
    setup_uv->>GitHubActionsCache: restore cache

    Publish->>setup_uv: setup-uv v9.0.0 (save-cache: false)
    setup_uv->>GitHubActionsCache: restore cache

    Test_plain_3_10->>setup_uv: setup-uv v9.0.0 (save-cache: true)
    setup_uv->>GitHubActionsCache: restore cache
    alt [cold key]
        setup_uv->>GitHubActionsCache: save cache
    else [warm key]
        setup_uv->>GitHubActionsCache: not saving cache
    end

    Test_pandas_3_10->>setup_uv: setup-uv v9.0.0 (save-cache: false)
    setup_uv->>GitHubActionsCache: restore cache

    Test_3_12->>setup_uv: setup-uv v9.0.0 (save-cache: true)
    setup_uv->>GitHubActionsCache: restore cache
    alt [cold key]
        setup_uv->>GitHubActionsCache: save cache
    else [warm key]
        setup_uv->>GitHubActionsCache: not saving cache
    end
Loading

Flow diagram for conditional uv cache saving in Test workflow

flowchart TD
    A[Test job matrix leg] --> B[Check matrix.pandas]
    B -->|matrix.pandas set| C[save-cache: false]
    B -->|matrix.pandas not set| D[save-cache: true]
    C --> E[setup-uv restore only]
    D --> F[setup-uv restore]
    F --> G[setup-uv save cache on cold key]
    F --> H[setup-uv not saving cache on warm key]
Loading

File-Level Changes

Change Details Files
Disable uv cache saving in non-Test workflows to avoid cross-workflow cache write races.
  • Configure astral-sh/setup-uv@v9.0.0 in the Documentation workflow to set save-cache: false.
  • Configure astral-sh/setup-uv@v9.0.0 in the Linter workflow to set save-cache: false.
  • Configure astral-sh/setup-uv@v9.0.0 in the Publish workflow to set save-cache: false.
.github/workflows/doc.yml
.github/workflows/linter.yml
.github/workflows/publish.yml
Restrict uv cache saving within the Test workflow to a single matrix variant to prevent intra-workflow cache write races.
  • Update the Test workflow astral-sh/setup-uv@v9.0.0 step to set save-cache: ${{ !matrix.pandas }} so only legs without the pandas parameter write the cache, avoiding collisions between the plain and pandas==2.1.4 Python 3.10 jobs.
.github/workflows/test.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 reviewed your changes and they look great!


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.

The Test matrix declares ubuntu-latest + Python 3.10 twice, once plain
and once with pandas==2.1.4. Same OS and same Python means the same
setup-uv cache key, so those two legs race with each other and the
previous commit alone does not silence them.

Measured on this pull request with a cold key (run 31098374254,
attempt 2): both legs missed
setup-uv-2-x86_64-unknown-linux-gnu-ubuntu-24.04-3.10.20-ed955f29...,
the pandas leg saved it at 11:50:33 and the plain leg reported at
11:50:37

  Failed to save: Unable to reserve cache with key
  setup-uv-2-...-3.10.20-..., another job may be creating this cache.

That was the only warning left, and it came from this pair alone.

Making the variant leg skip the save leaves exactly one writer in Test:
matrix.pandas is unset for every other leg, so the expression is true
there and false for the pandas leg.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ChristianGeng

Copy link
Copy Markdown
Member Author

@sourcery-ai review

@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 reviewed your changes and they look great!


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.

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 reduces GitHub Actions warning annotations from astral-sh/setup-uv cache save-key collisions by ensuring only one job/workflow attempts to save the shared uv cache key.

Changes:

  • Disable uv cache saving in Documentation, Linter, and Publish workflows (save-cache: false).
  • In Test, disable cache saving for the pandas matrix leg so only one ubuntu-latest + 3.10 leg writes the cache.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
.github/workflows/test.yml Makes only the non-pandas matrix legs save the uv cache to avoid intra-workflow cache key races.
.github/workflows/publish.yml Disables uv cache saving to avoid cross-workflow save-key collisions.
.github/workflows/linter.yml Disables uv cache saving to avoid cross-workflow save-key collisions.
.github/workflows/doc.yml Disables uv cache saving to avoid cross-workflow save-key collisions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/test.yml
@ChristianGeng
ChristianGeng requested a review from hagenw August 6, 2026 12:01
@ChristianGeng
ChristianGeng merged commit 57390f1 into main Aug 6, 2026
12 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