Skip to content

feat(serverless): network-volume warm cache (VolumeCache)#531

Open
deanq wants to merge 23 commits into
mainfrom
deanquinanola/sls-367-network-volume-warm-cache-for-serverless-volumecache
Open

feat(serverless): network-volume warm cache (VolumeCache)#531
deanq wants to merge 23 commits into
mainfrom
deanquinanola/sls-367-network-volume-warm-cache-for-serverless-volumecache

Conversation

@deanq

@deanq deanq commented Jul 6, 2026

Copy link
Copy Markdown
Member

Summary

Adds VolumeCache, a small explicit primitive that warms local directories (e.g. a model cache) across serverless workers using a mounted network volume. It turns a repeated multi-GB model download on every cold start into a one-time cost per endpoint. Stdlib-only, best-effort: any failure degrades to a cold worker and never raises into your handler.

Interface — a closure

from runpod.serverless import VolumeCache

with VolumeCache(dirs=["/root/.cache/huggingface"]):
    model = load_model()
  • __enter__hydrate(): reconcile volume → container (copy files missing/newer in the container).
  • __exit__sync(background=True): reconcile container → volume on a daemon thread so your code returns immediately; an atexit join (bounded by a timeout) completes it before the process ends.

How it works

  • The volume holds a browsable mirror of the cached directories at {volume}/.cache/{namespace}/… (namespace defaults to RUNPOD_ENDPOINT_ID). No opaque archives, no accumulation.
  • Reconcile transfers only the diff (files missing or newer by size/mtime), via per-file atomic copy (copy2 to a unique temp + os.replace). Concurrent workers are last-writer-wins with no torn files.
  • Safety: skips symlinks; hydrate only writes destinations that resolve under a configured directory; namespace must be a single safe path component.

Notes

  • No new runtime dependencies; honors the >=3.10 floor.
  • Explicit, opt-in by design: users add the with block where it fits. There is no automatic worker-loop wiring or environment-variable toggle — an earlier draft had a built-in; it was removed in favor of the single explicit primitive. Auto-wiring can be layered on later if desired.

Test plan

  • Unit suite tests/test_serverless/test_utils/test_rp_volume_cache.py: availability gating, namespace validation, sync→hydrate round-trip, reconcile idempotence (unchanged files not recopied), newer-file protection, path exclusions, symlink skip, hydrate destination-containment (escape attempt blocked), context-manager enter/exit (+ exception propagation), background completion via the atexit join (bounded), temp-file exclusion, and best-effort swallow vs re-raise.
  • Full tests/test_serverless suite green; repo make quality-check passes (557 tests, ~94% overall, ~98% on the new module).
  • Tests are stdlib + tmp_path only — no Docker, no network.

Docs

  • docs/serverless/volume_cache.md and a short README section under Serverless.

deanq added 14 commits July 6, 2026 01:54
…test state

Wrap build_default_cache() parse+construct in try/except to prevent malformed
RUNPOD_VOLUME_CACHE_MAX_GB from crashing worker startup; degrades to cache-disabled
instead. Add sync() no-op to fake cache in test_run_worker_hydrates_registered_cache
and wrap test body with reset_builtin_state_for_test() to prevent module-state
leakage to subsequent tests (sync_after_job could fire and hit AttributeError).
@capy-ai

capy-ai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Capy auto-review is paused for this organization because the usage-cycle auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

Comment thread tests/test_serverless/test_utils/test_rp_volume_cache.py Fixed
Comment thread runpod/serverless/utils/rp_volume_cache.py Fixed
Comment thread runpod/serverless/utils/rp_volume_cache.py Fixed
Comment thread tests/test_serverless/test_utils/test_rp_volume_cache.py Fixed
Comment thread tests/test_serverless/test_utils/test_rp_volume_cache.py Fixed
Comment thread tests/test_serverless/test_utils/test_rp_volume_cache.py Fixed
Comment thread runpod/serverless/utils/rp_volume_cache.py Fixed
Comment thread tests/test_serverless/test_utils/test_rp_volume_cache.py Fixed
Comment thread tests/test_serverless/test_utils/test_rp_volume_cache.py Fixed
Comment thread runpod/serverless/utils/rp_volume_cache.py Fixed
@promptless

promptless Bot commented Jul 6, 2026

Copy link
Copy Markdown

Promptless prepared a documentation update related to this change.

Triggered by runpod-python PR #531

Documents the new Serverless network-volume warm cache: a new page covering the VolumeCache API and its warm()/hydrate()/sync() lifecycle, the RUNPOD_VOLUME_CACHE / RUNPOD_CACHE_DIRS / RUNPOD_VOLUME_CACHE_MAX_GB environment variables, and auto-discovered cache directories (HF_HOME / HF_HUB_CACHE / TORCH_HOME), plus a cold-start optimization cross-link. Since this PR is still open, the draft is phrased around the env-var toggle rather than asserting default-on behavior — worth confirming the default-on vs opt-in decision and target SDK version before merge.

Review: Document Serverless network-volume warm cache (VolumeCache)

@deanq deanq requested a review from Copilot July 6, 2026 23:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a reusable serverless SDK primitive (VolumeCache) for warming local model/cache directories from a mounted network volume and syncing back deltas as per-worker tar “shards”, then wires it into the serverless worker lifecycle to persist model weights across worker recycling (SLS-367).

Changes:

  • Added runpod.serverless.utils.rp_volume_cache.VolumeCache with hydrate(), sync(), and warm() plus built-in helpers (build_default_cache, sync_after_job).
  • Auto-integrated the cache into the worker loop (run_worker hydrates/registers) and job execution (run_job dispatches a one-time async sync after a successful handler run).
  • Expanded public exports (runpod.serverless.VolumeCache, runpod.serverless.utils.VolumeCache) and added a comprehensive unit test suite.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/test_serverless/test_utils/test_rp_volume_cache.py New unit test suite covering VolumeCache behavior, safety, retention, and worker/job wiring.
tests/test_serverless/test_utils_init.py Updates runpod.serverless.utils.__all__ expectations to include VolumeCache.
tests/test_serverless/test_init.py Updates runpod.serverless.__all__ expectations to include VolumeCache.
runpod/serverless/worker.py Hydrates and registers the default cache before starting job scaling.
runpod/serverless/utils/rp_volume_cache.py New implementation of VolumeCache plus built-in env-driven wiring helpers.
runpod/serverless/utils/init.py Re-exports VolumeCache from runpod.serverless.utils.
runpod/serverless/modules/rp_job.py Dispatches a one-time background cache sync after non-streaming jobs.
runpod/serverless/init.py Re-exports VolumeCache from runpod.serverless.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread runpod/serverless/utils/rp_volume_cache.py
Comment thread runpod/serverless/utils/rp_volume_cache.py Outdated
Comment thread runpod/serverless/modules/rp_job.py Outdated
@deanq

deanq commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

Addressed review feedback in ede2d5c:

Copilot (behavioral):

  • run_job_generator now dispatches the one-time sync_after_job() on its success path — streaming/generator handlers previously never warmed the cache.
  • _enforce_retention now tolerates a shard vanishing under concurrent prune (getsize no longer raises FileNotFoundError out of best-effort sync()).
  • VolumeCache now rejects an unsafe namespace (absolute / path separators / ./..) to prevent shard/marker paths escaping the volume.

CodeQL / code-quality:

  • Documented the best-effort empty except in the temp-shard sweep; removed the unreachable-code pattern in the warm-on-exception test; consolidated mixed import styles in the test module.
  • The "unused global _SYNCED" alert is a false positive (_SYNCED is read/written in sync_after_job and reset in reset_builtin_state_for_test) — dismissed.

Tests added for streaming sync, retention resilience, and namespace validation. Full suite green (566 tests, 94% coverage).

Still open for a maintainer decision before merge: default-on vs opt-in for the serverless built-in when a volume is mounted (currently on, disabled via RUNPOD_VOLUME_CACHE=0).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Comment thread runpod/serverless/utils/rp_volume_cache.py Outdated
Comment thread runpod/serverless/utils/rp_volume_cache.py Outdated
Comment thread tests/test_serverless/test_utils/test_rp_volume_cache.py Fixed
Comment thread tests/test_serverless/test_utils/test_rp_volume_cache.py Fixed
Comment thread tests/test_serverless/test_utils/test_rp_volume_cache.py Fixed
Comment thread runpod/serverless/utils/rp_volume_cache.py Fixed
Comment thread runpod/serverless/utils/rp_volume_cache.py Fixed
Comment thread runpod/serverless/utils/rp_volume_cache.py Fixed
Comment thread tests/test_serverless/test_utils/test_rp_volume_cache.py Fixed
Comment thread tests/test_serverless/test_utils/test_rp_volume_cache.py Fixed
Comment thread tests/test_serverless/test_utils/test_rp_volume_cache.py Fixed
Comment thread runpod/serverless/utils/rp_volume_cache.py Fixed
Comment thread runpod/serverless/utils/rp_volume_cache.py Fixed
Comment thread runpod/serverless/utils/rp_volume_cache.py Fixed
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.

3 participants