Repository navigation
fix(cache): concurrency-safe downloads into the shared labelle home - #533
Merged
Merged
Conversation
Several labelle processes share one LABELLE_HOME (a CI shell runner runs jobs concurrently). The assembler download staged on a fixed `<dest>.part`, so two jobs fetching assembler v0.126.0 wrote the same file; one renamed it into place and the other failed "could not read the download to verify it" -> AssemblerNotCached (Flying Platform pipeline #57). New src/cli/cache_publish.zig: unique staging siblings (`<dest>.tmp-<pid>-<random>`), an exclusive per-item InstallLock, and publishFile / publishDir, which publish with one atomic rename and keep a concurrent winner's valid install instead of failing (including Windows, where a running executable cannot be replaced). A broken leftover directory is moved aside and replaced, never mixed. Applied to every download into the cache: - assembler: unique staging + manifest path, lock, chmod before publish, verified-digest check of a concurrent winner, curl timeouts - astcenc: download + extract into a unique staging dir, publish the tree with one rename (was: zip + unzip in place in the version dir) - managed Python: lock, unique archive + staging dir, publish (was: shared archive path and deleteTree + extract in place) - provider archives (cache read + `providers fetch`): publishFile - managed Zig: unique staging name from the shared helper - `labelle update`: stage next to the installed binary, unique name Tests: unit tests for the helpers; 8-thread races for publishFile and publishDir; 6 parallel `downloadAssembler` calls and 6 parallel astcenc `ensure` calls into one cache against file:// release fixtures, asserting all succeed, the result verifies and nothing is left behind. Claude-Session: https://claude.ai/code/session_01Hpt1haxcboZPznpG1KqDRS
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
apotema
added a commit
that referenced
this pull request
Oct 31, 2026
Ships #533: assembler, astcenc, managed Python, provider archive and self-update downloads into the shared labelle home are staged on unique paths and published atomically, so concurrent jobs on one CI runner no longer clobber each other (Flying Platform pipeline #57). Claude-Session: https://claude.ai/code/session_01Hpt1haxcboZPznpG1KqDRS
Open
10 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Every labelle process on a machine shares one
LABELLE_HOME, and a CI shell runner (Flying Platform'sfp-ubuntunow runs 3 jobs at once) shares it across concurrent jobs. The assembler download staged on a fixed<dest>.part. In FP pipeline #57,test-gameandtest-game-androidboth fetched assembler v0.126.0 into the same.part. One renamed it into place, and the other failed:astcenc (zip + unzip in place in the version dir) and managed Python (shared archive path,
deleteTreethen extract in place) had the same race, and theirs was worse.Fix:
src/cli/cache_publish.zigtempSibling(dest, tag)returns<dest>.tmp-<pid>-<16 hex>. It sits in the same directory, so the final rename is atomic, and the name is unique across processes and threads.InstallLockis an exclusive, blocking advisory lock on<dest>.lockthat the OS releases if the holder dies. Only one process downloads; the others wait, re-check, and reuse the result.publishFile(staged, dest, validator)publishes with one rename. Ifdestalready validates (before we start, or after a failed rename, e.g. Windows refusing to replace a running exe), ours is discarded and theirs is used.stagednever survives the call.publishDir(staged, dest, validator)does the same for a tree. A broken leftover atdestis moved aside in one rename, ours goes in, and the old tree is deleted, so the two are never mixed.Applied to every download into the cache:
downloadAssembler).part, fixed.sha256sums, chmod after publishastcenc_bin.ensure)<ver>/in placepython-download.tar.gz,deleteTree(py_dir)+ extract in placepublishDirproviders fetch)publishFilewith the pinned sha256 (keeps a concurrent winner, handles Windows rename-over failures)tempSiblinglabelle update$TEMP/labelle-update,mvacross filesystemsAlso: when
SystemRootis unset, astcenc's Windows extract now tries the stockC:\Windows\System32\tar.exebefore baretar.Tests
cache_publish:tempSiblingshape and uniqueness;publishFile(publish / keep valid / replace damaged);publishDir(publish / keep valid / replace partial leftover); 8-thread races for unlockedpublishFileand lockedpublishDir, asserting all succeed, the result verifies, and no staging leftovers remain.assembler.ConcurrentDownload: 6 paralleldownloadAssemblercalls into one cache against afile://release fixture (6 MiB asset + SHA256SUMS) through real curl. All succeed, the binary verifies, and only the binary and its.lockare left in the slot. A tampered asset leaves nothing at the final path.ensurecalls into one labelle home against afile://zip fixture. Every binary is complete, and nothing besides the version dir and its lock is left.zig build test -j2on Windows: 1070 pass, 100 skip, 4 fail. Those 4 are the same as onmainon this machine:packsymlink tests that need the symlink privilege.https://claude.ai/code/session_01Hpt1haxcboZPznpG1KqDRS