Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9406110
feat(serverless): add VolumeCache skeleton with availability gating
deanq Jul 6, 2026
83c3790
feat(serverless): VolumeCache.sync writes collision-free delta shards
deanq Jul 6, 2026
1d9a037
fix(serverless): tolerate coarse network-fs mtime granularity in Volu…
deanq Jul 6, 2026
709b24b
feat(serverless): VolumeCache.hydrate extracts shards with idempotent…
deanq Jul 6, 2026
0c7a99a
fix(serverless): drop 3.12-only tarfile filter kwarg to honor py3.10 …
deanq Jul 6, 2026
cf5e634
fix(serverless): apply tar data filter when available, honoring py3.1…
deanq Jul 6, 2026
757fb1a
feat(serverless): harden VolumeCache extract against traversal and sy…
deanq Jul 6, 2026
1c7edf0
fix(serverless): realpath-normalize cache dirs so symlinked mounts match
deanq Jul 6, 2026
b6477f5
feat(serverless): add size-capped retention to VolumeCache
deanq Jul 6, 2026
c507959
feat(serverless): add best-effort guard and warm() context manager
deanq Jul 6, 2026
d1957cc
feat(serverless): export VolumeCache from runpod.serverless
deanq Jul 6, 2026
280e142
feat(serverless): auto-hydrate model cache from network volume in wor…
deanq Jul 6, 2026
1310330
fix(serverless): guard build_default_cache and isolate worker-wiring …
deanq Jul 6, 2026
75394fc
fix(serverless): guard cache sync dispatch, sweep temp shards, cover …
deanq Jul 6, 2026
7047644
fix(serverless): address review - streaming sync, retention resilienc…
deanq Jul 7, 2026
ee7d536
feat(serverless): make network-volume warm cache opt-in; add usage docs
deanq Jul 7, 2026
1aff13c
refactor(serverless): VolumeCache mirror+reconcile closure; drop env-…
deanq Jul 7, 2026
17ef5e1
fix(serverless): bound atexit sync join, unique temp names, exclude t…
deanq Jul 7, 2026
544b979
fix(serverless): clear CodeQL alerts (empty-except, unreachable test,…
deanq Jul 8, 2026
5f5570d
docs(serverless): add VolumeCache warm-cache handler example
deanq Jul 8, 2026
a8f0afc
feat(volumecache): add size-partition, max_workers, and packed-format…
deanq Jul 10, 2026
e612661
feat(volumecache): add versioned manifest read/write and change detec…
deanq Jul 10, 2026
5b332dd
feat(volumecache): pack small-file bucket via tar with tarfile fallback
deanq Jul 10, 2026
c34dd22
feat(volumecache): extract small bucket via tarfile with per-member s…
deanq Jul 10, 2026
3a2d36d
fix(volumecache): harden _extract_small against corrupt archives
deanq Jul 10, 2026
f76773b
test(volume-cache): target inner getmembers guard in _extract_small
deanq Jul 10, 2026
f17840a
feat(volumecache): add thread-pool parallel copy helper
deanq Jul 10, 2026
ddbaa9e
feat(volumecache): mirror and hydrate via size-bucketed tar + paralle…
deanq Jul 10, 2026
cc64103
fix(volumecache): gate small-archive extract on manifest + prune stal…
deanq Jul 10, 2026
ca51c75
docs(volumecache): document size-bucketed adaptive transport
deanq Jul 10, 2026
c242dd2
fix(volume-cache): harden manifest reads and pack_small cleanup
deanq Jul 10, 2026
996cb1f
fix(volumecache): repack small archive when a small file is deleted
deanq Jul 11, 2026
2f38d64
merge: sync adaptive-transport branch with #531 (scalar-dirs fix)
deanq Jul 11, 2026
3284bc8
refactor(volumecache): stream copy count and confine hydrate source t…
deanq Jul 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 34 additions & 13 deletions docs/serverless/volume_cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,41 @@ vc.sync(background=False) # persist new files back to the volume, inli
| `namespace` | `RUNPOD_ENDPOINT_ID` | Isolation key for the on-volume mirror. Must be a single safe path component. |
| `volume_path` | `/runpod-volume` | Network-volume mount point. |
| `best_effort` | `True` | Swallow and log errors instead of raising. Set `False` while debugging. |
| `max_workers` | `min(32, (os.cpu_count() or 4) * 4)` | Thread count for parallel copy of large files (I/O-bound). |

## How it works

- **Directory mirror.** Cached files live at `{volume_path}/.cache/{namespace}`,
laid out at the same absolute path they occupy in the container (so the
mirror is directly browsable — no archive format to unpack).
- **Per-file, atomic reconcile.** Each direction (`hydrate`/`sync`) walks the
source tree and copies any file whose size differs or whose mtime is newer
than the destination's (beyond a small tolerance for coarse network-filesystem
mtimes). Copies are written to a temp file and atomically renamed into place
(`os.replace`), so a crash mid-copy never leaves a half-written file visible.
- **Idempotent.** Copies preserve mtime (`shutil.copy2`), so re-running
`hydrate`/`sync` after nothing has changed copies zero files.
- **Size-bucketed mirror.** Cached files live at
`{volume_path}/.cache/{namespace}`, split by size: files below 256 KiB are
packed into a single `small.tar` archive (collapsing per-file metadata
round-trips on the network volume), and larger files are copied unpacked
into a `big/` subdirectory, preserving their original relative path. A
versioned `manifest.json` — written last — records file metadata (size,
mtime) for every cached file and is the commit marker for a complete
mirror; a mirror without a valid, current-version manifest is treated as
absent.
- **Incremental large files, whole-archive small files.** `big/` transfers
are diffed per file by size/mtime against the manifest, so unchanged large
files are skipped. The `small.tar` archive is re-packed as a whole whenever
any small file has changed, since unpacking and re-diffing many tiny files
individually is slower than the network volume's per-file overhead.
- **Parallel copy.** Large-file transfers run across a thread pool sized by
`max_workers` (default `min(32, (os.cpu_count() or 4) * 4)`), since the
work is I/O-bound.
- **Packing/extraction.** Packing the small-file archive uses the `tar`
binary when available (falling back to the stdlib `tarfile` module
otherwise). Extraction always goes through `tarfile`, validating every
member's resolved path against the configured `dirs` before writing.
- **Idempotent.** Re-running `hydrate`/`sync` after nothing has changed
copies zero files and does not repack `small.tar`. `manifest.json` is still
refreshed on every `sync` call — a small atomic write that also drops
entries for files deleted locally since the last sync.
- **Last-writer-wins.** Under concurrent workers, the mirror simply reflects
whichever worker synced most recently — there's no locking or merge.
- **Safety.** Symlinked sources are never followed/copied. Hydration destinations
are checked to resolve inside one of the configured `dirs` before any write,
so a mirror entry can't be used to write outside the cached directories.
- **Safety.** Symlinked sources are never followed/copied. Every archive
member and every big-file destination is checked to resolve inside one of
the configured `dirs` before any write, so a mirror entry can't be used to
write outside the cached directories.

## Limitations

Expand All @@ -87,3 +104,7 @@ vc.sync(background=False) # persist new files back to the volume, inli
a daemon thread; if the process exits without going through normal
interpreter shutdown (e.g. `os._exit`, `SIGKILL`), the atexit hook never
runs and the sync may not complete.
- **Orphaned big files are never pruned.** If a large file is deleted or
renamed locally, its `big/<relpath>` copy stays on the volume — hydrate
is manifest-driven and simply ignores it, but volume space grows across
model-version swaps. Same behavior as the prior flat-mirror design.
Loading