Expand description
Convention for a remote forge step’s host-persistent build cache.
§The gap this closes
A remote subprocess gets image + argv + the forge_produced mount and
nothing else, so a step that compiles a source tree compiles it from scratch
every single run: the container’s writable layer (where CARGO_TARGET_DIR
lands by default) is thrown away when kamaji reaps the exited container, and
/yah/produced is per-run and reaped on destroy. mesofact-musl’s x86_64
leg measured 9m56s / 9m57s / 11m10s across its successful runs and every one
of those was a cold full release build.
This is the third mount under forge_state::HOST_ROOT, and — exactly as
R603-B6’s handoff promised — it needs neither new yubaba code nor a
yubaba.service edit: ensure_forge_state_dirs already mkdirs any forge
bind under that root.
§Why the key is derived, not caller-supplied
A shared target dir keyed by nothing is a correctness bug, not merely a
race. mesofact-musl carries concurrency_key = "mesofact-musl", but that
is camp-side scheduling: it does not constrain a second camp, or a
hand-rolled dispatch, aiming at the same worker. The key is therefore
derived by the dispatcher from pipeline + step + target triple
([key_from_parts]) rather than written in a TOML, so two different
pipelines — or the same pipeline’s two triples — cannot land on one target
dir however the run was started.
Two runs of the same pipeline+step+triple DO share, and that is the whole
point: cargo is designed for exactly that reuse, and its own .cargo-lock
in the target dir serializes two builds that overlap in time.
§Eviction is explicit
An unbounded cache on a worker rootfs is R702’s subject. Both holders of a
cache root — yubaba on the worker, qed for the local-container placement —
sweep it with [evict_plan]: anything idle past [RETENTION] goes, and if
the filesystem is below [FREE_FLOOR_BYTES] the least-recently-used dirs go
too, until it is not. The cache can therefore never consume the last few GB
of a build worker’s /var.
Constants§
- CACHE_
DIR_ ENV - Environment variable carrying
CONTAINER_DIRinto the step, so an argv never has to hardcode the convention. - CONTAINER_
DIR - Conventional container-side directory a cached forge step’s build scratch lives in. Bind-mounted onto a host-persistent, key-scoped dir.
- FREE_
FLOOR_ BYTES - Below this much free space on the filesystem holding a cache root,
least-recently-used cache dirs are evicted until it is above it again.
This is the bound that matters on a build worker: us-west-003’s
/varis a 60 GB LV, and a release target dir is multiple GB. - HOST_
ROOT - Host root under which each cache key gets a directory:
<HOST_ROOT>/<key>/. Undersuper::forge_state::HOST_ROOT, so yubaba’sensure_forge_state_dirscreates it andyubaba.servicealready grants write access to it. - MAX_
KEY_ LEN - Longest derived key kept verbatim; longer ones are truncated and
disambiguated with a digest by
key_from_parts. - RETENTION
- A cache dir untouched for this long is evicted. Long enough that a weekly release still hits a warm cache; short enough that a renamed step’s orphan does not sit on the disk forever.
Functions§
- cache_
dir_ under - The same derivation against an arbitrary root — for the local-container
placement, whose cache lives under the camp’s own
.yah/cacheand has noHOST_ROOTto hang off. Split out for the same reasonsuper::forge_produced::host_path_underis: the key validation is the whole safety content of both, and two copies is one copy that can be fixed alone. - durable_
mount - The build-cache bind mount for one key: host
<HOST_ROOT>/<key>→ containerCONTAINER_DIR, writable.Nonefor an invalid key — the caller must refuse rather than mount something else. - evict_
plan - Which cache dirs to delete, given every dir in a cache root with its last-modified time, the current free space on that filesystem, and the floor to hold.
- host_
dir - The host-persistent cache directory for one key, under
HOST_ROOT. - is_
valid_ key - Whether
keyis safe to use as a single path component underHOST_ROOT. Deliberately narrow: alphanumerics plus.,-,_, non-empty, length-capped, and never a bare./... Everything a dispatcher derives passes; nothing a hostile spec could write escapes. - key_
from_ parts - Derive the sharing key from the parts that must not collide: the pipeline, the step within it, and the target triple.