Repository navigation
fix(serverless): VolumeCache copies files on volumes that reject utime/chmod - #620
Merged
Merged
Conversation
shutil.copy2 runs copystat after the data copy, and FUSE object stores such as Global Store reject utime/chmod with EPERM. The whole copy then failed and was logged at debug level, so every file >= 256 KiB silently dropped out of the mirror and each cold start re-downloaded the model. Copy the data with copyfile and carry mtime and mode over best-effort. A copy-time mtime is never older than the source, so both diff directions still converge. Log real copy failures as warnings.
There was a problem hiding this comment.
馃煛 Changes recommended
Copy-time timestamps can hide source updates and cause hydration to restore stale bytes or overwrite newer local data.
1 open finding
What changed in this PR
Updates VolumeCache to copy files on volumes that reject timestamp or permission changes.
Changes:
- Separates data copying from best-effort metadata preservation.
- Logs copy failures as warnings.
- Adds metadata-rejection, convergence, and preservation tests.
| File | Description |
|---|---|
| tests/鈥媡est_serverless/鈥媡est_utils/鈥媡est_rp_volume_cache.py | Tests metadata failures, round trips, convergence, and warning logs. |
| runpod/鈥媠erverless/鈥媢tils/鈥媟p_volume_cache.py | Makes metadata copying best-effort and raises copy-failure log visibility. |
馃 Review effort: Balanced
馃挕 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
When the volume rejects utime, mirror files carry copy-time mtimes, so comparing physical mtimes could skip a same-size update on sync or overwrite a newer container file on hydrate. Compare big files against the manifest's recorded source metadata in both directions, keep a size/existence check on the mirror, and stamp the source mtime on restore so the next sync is a no-op.
_sync_needs_copy compared source and manifest mtimes with abs(), so a source older than the recorded entry also triggered a recopy. That made every big file rewrite across workers with separate downloads (same bytes, different mtimes) and let a stale worker overwrite newer mirror bytes. Recopy only when the source is strictly newer than the manifest.
jasonwang-runpod
approved these changes
Oct 15, 2026
Promptless documentation updates
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Bug
VolumeCache._copy_fileusedshutil.copy2, which callscopystat(utime, thenchmod) after copying the data. FUSE object stores such as Global Store reject both withEPERM, so the entire copy raised. Theexcept OSErrorlogged it at debug level and returnedFalse.As a result, every file of 256 KiB or more, which covers all model weights, silently dropped out of the mirror. The manifest correctly left them out, so each cold start re-downloaded the model and nothing ever reported an error. I found this while benchmarking
VolumeCacheon Global Store (deanq/flash-model-cache-bench, finding 4).Fix
shutil.copyfile, then carry the mtime (os.utime) and mode (shutil.copymode) over separately, each best-effort with a debug log.utime, mirror files carry copy-time mtimes, and comparing those could skip a same-size update on sync or overwrite a newer container file on hydrate (review feedback)._meta_satisfied_by_local), and stamp the manifest's source mtime on the restored file so the next sync is a no-op.chmod.log.warnrather thanlog.debug, so a cache that is silently not caching shows up in worker logs.Tests
The new tests failed before the fix and pass after it:
test_big_file_round_trips_when_volume_rejects_metadata_ops: sync, delete, hydrate withos.utime/os.chmodraisingEPERM. Before the fix, 0 files were restored.test_sync_converges_when_volume_rejects_metadata_ops: a second sync re-copies nothing.test_copy_failure_is_logged_as_warning.Two more tests:
test_copy_file_preserves_mtime_and_modeguards the normal-filesystem behavior (mtime and0o700preserved).test_copy_file_returns_false_and_cleans_tmp_on_failurenow patchescopyfile.Review regression tests, simulating a volume that rejects
utime/chmodonly under the volume path:test_sync_recopies_same_size_update_when_volume_rejects_utime(failed before)test_hydrate_keeps_newer_local_file_when_volume_rejects_utime(failed before)test_hydrate_restores_source_mtime_so_next_sync_is_noop(failed before)test_sync_recopies_when_mirror_file_is_missing(guard)Full suite:
uv run pytest, 1478 passed.Not addressed
On Global Store, the benchmark also saw 7B hydrates come back about half-complete even with this copy fix. The cause wasn't identified, so this PR doesn't claim to fix it. Symlinks under the HF cache's
snapshots/aren't mirrored either, by design.