feat: add file_clone (reflink/CoW) mode for the local disk cache#2739
Open
Skory wants to merge 1 commit into
Open
feat: add file_clone (reflink/CoW) mode for the local disk cache#2739Skory wants to merge 1 commit into
Skory wants to merge 1 commit into
Conversation
7bc864f to
7109e48
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2739 +/- ##
==========================================
+ Coverage 74.51% 75.62% +1.11%
==========================================
Files 70 71 +1
Lines 39654 42007 +2353
==========================================
+ Hits 29549 31769 +2220
- Misses 10105 10238 +133 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Opt-in file_clone stores disk-cache entries uncompressed and restores them with filesystem reflinks (FICLONE on Linux, clonefile on macOS/APFS, ReFS on Windows), falling back to plain copies on non-CoW filesystems. The default compressed (zip+zstd) cache is unchanged. - config: file_clone in [cache.disk] and SCCACHE_FILE_CLONE (default false) - reflink-on-write via Storage::put_objects (no zip/zstd); fd-level FICLONE fast path on Linux, in-place overwrite fallback for read-only dirs on non-Linux - uncompressed directory entries in LruDiskCache (gated; default path unchanged), atomic staging+rename, out-of-band mode manifest, 0600 cache files / 0700 dirs - new --show-stats counters: objects_reflinked / objects_copied_fallback - docs/FileClone.md, scripts/bench-file-clone.sh, integration + system + unit tests Refs mozilla#1053, mozilla#1174; reimplements mozilla#2640 (credit @quake).
7109e48 to
f59d7d8
Compare
Collaborator
|
in the future, please keep only the most important information in comment #0 |
Author
|
@sylvestre do you know who is the best PoC to review? |
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.
Summary
Adds an opt-in
file_clonemode to sccache's local disk cache. Instead of storingentries compressed (zip + zstd) and decompressing a fresh, fully-allocated copy on every
cache hit,
file_clonestores entries uncompressed and restores them with filesystemreflinks (copy-on-write):
FICLONEon Linux (Btrfs/XFS/ZFS/…),clonefileon macOS(APFS), block-cloning on Windows (ReFS), with a transparent copy fallback on non-CoW
filesystems.
This is opt-in and off by default; the existing compressed cache path is byte-for-byte
unchanged.
This is a fresh implementation of the idea in #2640 (credit @quake) with the open gaps
closed and some performance optimisations. Refs #1053, #1174.
Motivation
On a cache hit the disk cache decompresses a freshly-allocated copy of every artifact
into the build tree. When the same outputs are restored over and over, that physical
duplication adds up:
cargo clean/ branch-switch rebuilds re-extract the same artifacts againand again (the original ask in Support reflinks & hardlinks #1053 / Disk cache and clean rebuild speed #1174).
increasingly common: each worktree builds the same dependencies and restores the same
outputs from the cache, so the identical bytes get written to disk once per worktree —
N worktrees means N full copies of the same artifacts. On multi-worktree machines this
is the dominant source of disk pressure.
With
file_clone, restored files share underlying storage blocks with the cache entryon a CoW filesystem, so:
cached bytes (blocks are shared, not duplicated);
The trade-off is a larger, uncompressed cache (compression is given up to enable block
sharing). Because it is opt-in, nobody pays that cost unless they ask for it.
What's included
file_cloneunder[cache.disk]andSCCACHE_FILE_CLONE(defaultfalse).Storage::put_objects(the default impl preserves today'szip+zstd behaviour for every backend;
DiskCacheoverrides it to reflink the compiler'soriginal outputs straight into the cache — no compress-then-decompress round trip).
FICLONEfast path; non-Linux uses thereflink-copycrate, with anin-place-overwrite fallback for read-only destination dirs.
LruDiskCache(gated behind a flag — the defaultcache and the preprocessor cache keep the existing single-file path unchanged), with
atomic staging + rename, an out-of-band mode manifest, and
0600cache files /0700dirs.
--show-statscounters:objects_reflinked/objects_copied_fallback, so thereflink-vs-copy ratio is visible.
docs/FileClone.md,docs/Local.md,docs/Configuration.md), a manual multi-repobenchmark (
scripts/bench-file-clone.sh, not wired into CI; usescompsizeon btrfs tomeasure actual on-disk block sharing), plus integration, system, and unit tests.
Benchmarks
Two environments. Absolute times are not comparable across the two tables (different
machines); compare only within each table. In both,
file_cloneis comparable in buildtime to the compressed cache and the win is disk sharing, not speed.
Linux / btrfs
On-disk usage measured with
compsize(theauthoritative btrfs tool — it counts shared/reflinked extents once).
restore marginal disk= compsize disk of (cache + restored tree) − compsize disk of the cache alone, i.e.the new disk a warm restore actually consumes. It is ~0 only when the restored artifacts
reflink the cache, and it cancels out btrfs transparent compression (which affects both
terms equally).
The same
restore marginal diskmeasured against the compressed cache is much higher —local-c 1.9M, ripgrep 148.5M, fd 116.2M, bat 306.6M — because a compressed restore writes
fresh, unshared blocks. The gap is the disk the reflink sharing saves on every restore
(e.g. bat 151.6M vs 306.6M ≈ 155M saved per restore;
local-c, a pure-compile workloadwhere every object is cached, shares 100% → 0 marginal).
reflink/copy = N/0confirmsevery cached object was reflinked. (For the cargo targets the non-zero marginal is mostly
the freshly linked binary and cargo's incremental/fingerprint files, which sccache does not
cache.)
macOS / APFS
APFS has no
compsizeequivalent, so sharing is measured with the volume free-space delta(
df) during the warm restore —clone Δdiskwell belowzip Δdiskmeans blocks wereshared. Δdisk is noisy and includes non-cached build outputs; the reflink/copy counter is
the authoritative proof.
Takeaways
reflink/copy = N/0on every target on both filesystems —100% of cached objects restored via reflink, zero copy fallbacks.
touch faster from skipping decompression, sometimes a touch slower from per-file clone
syscalls). This is not a build-speed feature.
storage with the cache (btrfs
restore marginal disk:bat151.6M vs 306.6M for thecompressed cache,
local-c0M; APFSclone Δdisk:fd35.8M vs 173.0M,bat183.0M vs561.0M). Multiplied across many worktrees this is the headline benefit.
traded for block sharing).
Notes / requirements
SCCACHE_DIRand the build tree are on thesame CoW filesystem; otherwise reflink transparently falls back to a full copy (and the
objects_copied_fallbackstat goes up).touch the cache entry.