fix(ci): one writer to the buildcache ref #4
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix-buildcache-race"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes jlxq0/m365-mcp#4 for this repository.
The race
Merging a PR and pushing the release tag are two pushes, so two
dockerjobsrun concurrently. Both exported to the same unqualified
:buildcacheref:Observed on
m365-mcpruns 49 and 50, failing within half a second of eachother — after the image had already been pushed successfully.
The fix, and why it is not either option in the issue
The tag build now imports the cache and does not export it.
That works because a tag build is the same commit as the
mainbuild thatpreceded it. Reading the branch: the two paths differ only in
TAGSandPUSH,which are output settings rather than build inputs. So the two caches would be
byte-identical — exporting twice was pure duplication, and the duplication was
the race. One writer removes it by construction rather than by scheduling.
The issue offered two shapes and I took neither:
concurrency:group serialises two builds that produce the same bytes,which is paying a queue to do work twice. Worse, Forgejo job-level
concurrency is unverified here: nothing in the fleet uses it, this instance
is 15.0.2, and an unsupported key is ignored silently — a fix that looks
applied and does nothing, which is the failure mode worth avoiding above all
others tonight.
is the expensive path, and accumulates refs on a registry whose disk is
currently the fleet's bottleneck.
carddav-mcpalready did thisIt guards its export on
refs/heads/mainand has exactly one writer. So this isfour copies converging on the one that was right, not a new invention — the same
shape as the divergence in
oauth_redirect.rs, resolved in favour of the copythat already worked.
Worth correcting one line of the issue: it says all five export to the same ref,
which is true, but only four of them export from both paths.
carddav-mcpneeded no change.
Verification
Not verified by running, and it cannot be yet: the forge runner is out of
disk and every
cargojob fails before reaching a gate. A greendockerjobwould not be evidence either — buildkit does not use that volume.
Correction: the assertion I first wrote was the wrong one
I originally said the check was "exactly one
--export-cachesite remains".That does not distinguish fixed from broken, and
typst-mcpis the proof:before this change it had exactly one export site, guarded by
GITHUB_EVENT_NAME != pull_request— which fires on amainpush and on atag push. One site, two writers. A grep for the count would have reported it
already fixed.
The property is exactly one writer, which is about the guard on the
export, not the number of sites. For three of the four repos those coincide;
for typst they do not.
refs/tags/v*andrefs/heads/mainrefs/tags/v*andrefs/heads/main!= pull_requestrefs/tags/v*and theelserefs/heads/mainAfter this change every one of the four exports only under
refs/heads/main, stated explicitly rather than implied by "not a tag", so atrigger added on another branch later cannot quietly become a second writer.
What is checked
bash -n;--export-cacheis inside arefs/heads/mainguard — read,not counted.
Do not merge on a gate you cannot read. This wants one run after the runner
is back: a merge to
mainand a tag push landing together, both green.🤖 Generated with Claude Code
https://claude.ai/code/session_01SP7njJZ7ZtrMgULuXh8ddq
45b682518fbf1618e81aMerged as
14b7ee29. Merge builds: cargo16534, docker16536, both green.The
Closes jlxq0/m365-mcp#4in the body above did not fire. Cross-repository references do not close issues on Forgejo, and all four pull requests carried the same line, so the issue stayed open with nothing recording that the work was done. It is closed by hand now, and m365-mcp#4 holds the record for all four repositories: merge commits, what was verified, and two instruments that were proposed and withdrawn before anyone used them.This repository is the one with a writer path still open. Its
17 3 * * *cron runs withGITHUB_REFofrefs/heads/main, which is exactly the guard the export now sits behind, so the nightly run exports. Task16519is that cron on 2026-08-25, writingsha256:e269c2ea.... If it fires while a merge build is running, that is two writers again. Tracked in m365-mcp#7 with why aconcurrency:group was not reached for. None of the other four repositories has a schedule.Evidence for this repository specifically, stated plainly: there is none, and the change is unproven at runtime here.
16536wrote no log, and thebuildcachedigest is unchanged atsha256:e269c2ea...because the build was a full cache hit re-exporting identical bytes, which is precisely the case every available instrument is blind to. Three of the four repositories have a confirmed export on the merge build; this one does not. The static check in the body above is what it ships on.What is confirmed here is that the export path works at all:
16519, under the old code, returns2forgrep -c "exporting cache to registry". That is the positive control for the instrument, not evidence about this change.