turbo-persistence: pin referenced BlockCache entries to prevent eviction#92361
Merged
turbo-persistence: pin referenced BlockCache entries to prevent eviction#92361
Conversation
Merging this PR will degrade performance by 3.5%
Performance Changes
Comparing Footnotes
|
Collaborator
Stats from current PR✅ No significant changes detected📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles
Server Middleware
Build DetailsBuild Manifests
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📎 Tarball URL |
lukesandberg
approved these changes
Apr 6, 2026
…d entries Implement `quick_cache::Lifecycle` for `BlockCacheLifecycle` with `is_pinned` that checks whether the `ArcBytes` backing `Arc` has strong_count > 1, preventing eviction of cache entries still referenced outside the cache. Co-Authored-By: Claude <noreply@anthropic.com>
…lias in tests Drop the Option-based eviction stash since we only need is_pinned. Remove the redundant TestBlockCache type alias in favor of the existing BlockCache alias. Co-Authored-By: Claude <noreply@anthropic.com>
6ce5dc5 to
c58552b
Compare
Co-Authored-By: Claude <noreply@anthropic.com>
eps1lon
pushed a commit
that referenced
this pull request
Apr 7, 2026
…ion (#92361) ### What? Implements [`quick_cache::Lifecycle`](https://docs.rs/quick_cache/latest/quick_cache/#lifecycle-hooks) hooks for the `BlockCache` in turbo-persistence. A new `BlockCacheLifecycle` struct overrides `is_pinned` to check whether the `ArcBytes` value's backing `Arc` has a strong count > 1, indicating it is still referenced outside the cache. When `is_pinned` returns `true`, the cache's eviction algorithm (Clock-PRO / S3-FIFO) skips the entry instead of evicting it. ### Why? Without pinning, the cache can evict blocks that are still in use — the caller holds an `ArcBytes` clone (which shares the `Arc` backing), so the data isn't lost, but the cache slot is wasted. When the caller finishes and the entry is looked up again, it results in an unnecessary cache miss and re-read/decompress from disk. By checking `Arc::strong_count > 1` in `is_pinned`, we avoid evicting entries that are actively referenced, improving cache hit rates under concurrent access. ### How? **`ArcBytes::is_shared()`** (`arc_bytes.rs`): - Returns `true` when the backing `Arc<[u8]>` has `strong_count > 1` (i.e., someone outside the cache holds a clone). - Mmap-backed entries always return `false` — the mmap `Arc` is shared across all slices from the same file, so checking its `strong_count` would make them permanently unevictable. This is fine because #92390 already drops mmap blocks from the cache entirely. **`BlockCacheLifecycle`** (`static_sorted_file.rs`): - Implements `quick_cache::Lifecycle<(u32, u16), ArcBytes>` with `RequestState = ()`. - `is_pinned` delegates to `val.is_shared()`. - `begin_request` and `on_evict` are no-ops since we only need the pinning behavior. **`BlockCache` type alias** updated to include `BlockCacheLifecycle` as the 5th generic parameter. All call sites use `Default::default()` for construction, so no call-site changes were needed. **Test cleanup**: Removed the redundant `TestBlockCache` type alias (and its unused imports) in the builder tests in favor of the existing `BlockCache` alias. <!-- NEXT_JS_LLM_PR --> --------- Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
eps1lon
pushed a commit
that referenced
this pull request
Apr 7, 2026
…ion (#92361) ### What? Implements [`quick_cache::Lifecycle`](https://docs.rs/quick_cache/latest/quick_cache/#lifecycle-hooks) hooks for the `BlockCache` in turbo-persistence. A new `BlockCacheLifecycle` struct overrides `is_pinned` to check whether the `ArcBytes` value's backing `Arc` has a strong count > 1, indicating it is still referenced outside the cache. When `is_pinned` returns `true`, the cache's eviction algorithm (Clock-PRO / S3-FIFO) skips the entry instead of evicting it. ### Why? Without pinning, the cache can evict blocks that are still in use — the caller holds an `ArcBytes` clone (which shares the `Arc` backing), so the data isn't lost, but the cache slot is wasted. When the caller finishes and the entry is looked up again, it results in an unnecessary cache miss and re-read/decompress from disk. By checking `Arc::strong_count > 1` in `is_pinned`, we avoid evicting entries that are actively referenced, improving cache hit rates under concurrent access. ### How? **`ArcBytes::is_shared()`** (`arc_bytes.rs`): - Returns `true` when the backing `Arc<[u8]>` has `strong_count > 1` (i.e., someone outside the cache holds a clone). - Mmap-backed entries always return `false` — the mmap `Arc` is shared across all slices from the same file, so checking its `strong_count` would make them permanently unevictable. This is fine because #92390 already drops mmap blocks from the cache entirely. **`BlockCacheLifecycle`** (`static_sorted_file.rs`): - Implements `quick_cache::Lifecycle<(u32, u16), ArcBytes>` with `RequestState = ()`. - `is_pinned` delegates to `val.is_shared()`. - `begin_request` and `on_evict` are no-ops since we only need the pinning behavior. **`BlockCache` type alias** updated to include `BlockCacheLifecycle` as the 5th generic parameter. All call sites use `Default::default()` for construction, so no call-site changes were needed. **Test cleanup**: Removed the redundant `TestBlockCache` type alias (and its unused imports) in the builder tests in favor of the existing `BlockCache` alias. <!-- NEXT_JS_LLM_PR --> --------- Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
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.
What?
Implements
quick_cache::Lifecyclehooks for theBlockCachein turbo-persistence. A newBlockCacheLifecyclestruct overridesis_pinnedto check whether theArcBytesvalue's backingArchas a strong count > 1, indicating it is still referenced outside the cache.When
is_pinnedreturnstrue, the cache's eviction algorithm (Clock-PRO / S3-FIFO) skips the entry instead of evicting it.Why?
Without pinning, the cache can evict blocks that are still in use — the caller holds an
ArcBytesclone (which shares theArcbacking), so the data isn't lost, but the cache slot is wasted. When the caller finishes and the entry is looked up again, it results in an unnecessary cache miss and re-read/decompress from disk.By checking
Arc::strong_count > 1inis_pinned, we avoid evicting entries that are actively referenced, improving cache hit rates under concurrent access.How?
ArcBytes::is_shared()(arc_bytes.rs):truewhen the backingArc<[u8]>hasstrong_count > 1(i.e., someone outside the cache holds a clone).false— the mmapArcis shared across all slices from the same file, so checking itsstrong_countwould make them permanently unevictable. This is fine because turbo-persistence: skip BlockCache for uncompressed (mmap-backed) blocks #92390 already drops mmap blocks from the cache entirely.BlockCacheLifecycle(static_sorted_file.rs):quick_cache::Lifecycle<(u32, u16), ArcBytes>withRequestState = ().is_pinneddelegates toval.is_shared().begin_requestandon_evictare no-ops since we only need the pinning behavior.BlockCachetype alias updated to includeBlockCacheLifecycleas the 5th generic parameter. All call sites useDefault::default()for construction, so no call-site changes were needed.Test cleanup: Removed the redundant
TestBlockCachetype alias (and its unused imports) in the builder tests in favor of the existingBlockCachealias.