fix(docker): isolate cargo cache per TARGETPLATFORM (multi-arch race)

The tunnel-docker job in v1.7.3 release failed with:

  error: failed to unpack package `serde_json v1.0.149`
  Caused by: failed to open `/usr/local/cargo/registry/src/.../serde_json-1.0.149/.cargo-ok`
  Caused by: File exists (os error 17)

Root cause: BuildKit's default cache-mount sharing is "shared" — both
linux/amd64 and linux/arm64 build stages mount the SAME on-disk cache
dir. Cargo's registry source extraction is non-atomic; both arches
race on `tar -xzf serde_json-1.0.149.crate` into the same destination,
and the loser hits EEXIST mid-unpack.

Fix: scope each cache mount with `id=cargo-registry-${TARGETPLATFORM}`
(and matching for cargo-git + target). BuildKit then keeps separate
on-disk caches per architecture — no race. Per-arch warm-build speedup
is preserved (each cache fills with that arch's pre-built deps); the
only loss is one cache miss per arch on the first build after this
change, which we already paid in v1.7.3.

The target/ mount is also platform-scoped since target/ holds compiled
object files for a single ABI; sharing across arches would either miss
or, worse, link wrong-ABI objects together.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
therealaleph
2026-04-26 22:04:19 +03:00
parent 0255123afd
commit 08efbc571d
+15 -3
View File
@@ -35,9 +35,21 @@ COPY src/ ./src/
# BuildKit cache mounts: cargo's registry/git caches and the target/
# directory persist across builds, dramatically speeding up rebuilds when
# only application code changes.
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
--mount=type=cache,target=/app/target \
#
# `id=...-$TARGETPLATFORM` is load-bearing on multi-arch builds. Without
# it, BuildKit defaults to a single shared cache across architectures
# and the `linux/amd64` + `linux/arm64` jobs race on the same on-disk
# `/usr/local/cargo/registry/src/.../<crate>/.cargo-ok` extraction. The
# second-arriving arch hits `File exists (os error 17)` mid-unpack and
# the whole multi-arch build fails. Per-platform cache id keeps each
# arch's cache isolated; warm-build speedup is preserved per-arch.
# `target` cache is also platform-scoped because target/ holds object
# files for one ABI and sharing them across arches would just produce
# misses or, worse, invalid linking.
ARG TARGETPLATFORM
RUN --mount=type=cache,target=/usr/local/cargo/registry,id=cargo-registry-${TARGETPLATFORM} \
--mount=type=cache,target=/usr/local/cargo/git,id=cargo-git-${TARGETPLATFORM} \
--mount=type=cache,target=/app/target,id=app-target-${TARGETPLATFORM} \
cargo build --release --bin tunnel-node && \
cp /app/target/release/tunnel-node /usr/local/bin/tunnel-node