Skip to content

gw#597+gw#598: multi-writer CAS safety + digest-verified reuse for volume-backed roots#339

Merged
PaulFidika merged 2 commits into
masterfrom
agent/597-cas-multiwriter-volume
Jul 20, 2026
Merged

gw#597+gw#598: multi-writer CAS safety + digest-verified reuse for volume-backed roots#339
PaulFidika merged 2 commits into
masterfrom
agent/597-cas-multiwriter-volume

Conversation

@PaulFidika

@PaulFidika PaulFidika commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Two tightly-coupled fixes for th#850's CAS-root-on-volume design (one wave, same files):

gw#597 — multi-writer safety + shared-tier removal

  • th#850 collapses the endpoint-volume design from a separate shared-read-tier (gw#277) to pointing the worker's existing CAS root (TENSORHUB_CACHE_DIR) directly at the mounted RunPod volume — removes ENDPOINT_SHARED_CACHE_MOUNT/endpoint_shared_cas_dir()/shared_base_dir plumbing.
  • Fixes the multi-writer race that collapse exposes: _download_one_file_sync's temp path and _materialize_snapshot's .building dir were not writer-unique (safe only with one writer per CAS root). Both now stage to writer-unique names + atomic replace/rename.
  • Boot-time sweep for orphaned writer temp artifacts (persistent volumes keep a crashed writer's temps forever); sweep_orphan_blobs hardened to skip in-flight dotfile temps.

gw#598 — digest-verified reuse (BLAKE3 gap)

  • The fresh-materialization path trusted REUSED bytes it never hashed: an existing snap_dir short-circuited with no check, and _blob_usable reused blobs on size alone. On a shared volume those are another pod's bytes of any age.
  • Fix at the CAS layer: reused blobs get one full BLAKE3 per (root, digest) per process; reused materialized trees are verified once per (root, key) per process, with quarantine (tree + bad blobs) and clean rebuild on corruption. Fresh downloads are never double-hashed; cold boots pay nothing extra; a warm-volume boot pays exactly one hash pass (the intended verify-on-first-use defense).

Test plan

  • tests/test_shared_cas_root_multiwriter.py — 4-process spawn races on the real download and materialization paths; correct final bytes, no temp litter
  • tests/test_snapshot_corruption.py::test_preseeded_unverified_cas_bytes_are_verified_before_trust — corrupt right-size pre-seeded blob+tree for an untracked ref: detected, quarantined, re-downloaded, shared blob repaired
  • tests/test_disk_gc.py::test_rescan_sweeps_stale_writer_temp_artifacts
  • Full suite: 1519 passed / 35 skipped / 0 failed; ruff + mypy clean

Cross-refs: th#850 + gw#597 + gw#598 in the trackers; tensorhub counterpart PR: https://github.com/cozy-creator/tensorhub/pull/489

th#850 moves the endpoint-volume design from a separate shared-read-tier
(gw#277) to simply pointing the worker's existing CAS root
(TENSORHUB_CACHE_DIR) at the mounted RunPod volume when one is attached.
The first pod's ordinary R2 prefetch warms it for later same-endpoint
pods -- no seed/publish/read-through split needed, so that plumbing is
removed from cache_paths.py/download.py/cozy_snapshot.py.

That collapse exposed a real bug: the primary blob-download temp path
(_download_one_file_sync) staged to a fixed ".part" name, safe only
because a pod-local CAS root has exactly one writer. Once the CAS root
is a volume shared by concurrent same-endpoint pods, two pods racing on
the same missing digest would collide on that name. Same gap in
_materialize_snapshot's ".building" tmp dir. Both now stage to a
writer-unique temp name + atomic replace/rename, matching the pattern
the (now-removed) shared-tier helper already used correctly.

Since a persistent volume (unlike ephemeral pod-local /tmp) keeps a
crashed writer's orphaned temp forever, added a boot-time sweep
(disk_gc.sweep_stale_writer_temp, wired into ModelStore.rescan_disk) and
hardened sweep_orphan_blobs to skip dotfile temp artifacts instead of
treating an in-flight writer's temp as an orphaned blob.

Proof: 4-process spawn tests racing the real download and materialization
paths against one shared destination (test_shared_cas_root_multiwriter.py).
executor.py's existing gc_disk/_ensure_disk_headroom LRU disk-GC needed no
changes -- it already operates on whatever cache_dir is mounted, satisfying
th#850's per-volume size-cap design as-is.

Full suite: 1518 passed, 35 skipped, 0 failed. ruff + mypy clean.
…t per boot

The fresh-materialization path trusted bytes it did not download: an
already-materialized snap_dir short-circuited with no check at all, and
_blob_usable reused existing blobs on a size-only check. Safe when the
CAS root's only writer was this pod; on a th#850 shared persistent
volume those are another pod's bytes of any age.

Fix lives in the CAS layer (cozy_snapshot), not the executor -- the
reuse decision and its verification belong together, stubbed-download
tests are untouched, and genuinely fresh downloads are never hashed
twice. Mechanics: process-local trust sets keyed by (resolved root,
digest/key). Reused blobs get a full BLAKE3 once per root+digest per
process (_blob_trusted; corrupt -> unlink -> re-download); a reused
materialized tree is verified once per root+key per process
(_verify_materialized_tree: manifest size+blake3 + structural
safetensors for reassembled originals; corrupt -> quarantine tree + bad
blobs -> rebuild). A freshly built tree is trusted without a second
pass since every input was verified this process. The executor's
existing first-use-per-boot verify for residency-tracked refs is
unchanged and now backed by the same guarantee underneath.

Chosen tradeoff (recorded in gw#598): verify-on-first-use-per-process,
NOT re-hash-every-boot -- warm-volume boot pays one hash pass over the
model (the intended defense), cold boots pay nothing extra.

Test: test_preseeded_unverified_cas_bytes_are_verified_before_trust --
corrupt right-size blob hardlinked into a materialized tree for a ref
residency never saw; ensure_local must detect, quarantine, re-download,
and repair the shared blob. test_disk_gc fixtures now declare truthful
digests of their fake bytes. Full suite 1519 passed / 0 failed.
@PaulFidika PaulFidika changed the title gw#597: fix multi-writer CAS races; remove now-superseded shared-tier gw#597+gw#598: multi-writer CAS safety + digest-verified reuse for volume-backed roots Jul 20, 2026
@PaulFidika
PaulFidika merged commit 61ec363 into master Jul 20, 2026
1 check passed
@PaulFidika
PaulFidika deleted the agent/597-cas-multiwriter-volume branch July 20, 2026 05:16
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.

1 participant