Skip to content

Implement memory cache V2 in safe Rust - #7110

Open
danlapid wants to merge 1 commit into
mainfrom
dlapid/memoryCacheUpdate
Open

Implement memory cache V2 in safe Rust#7110
danlapid wants to merge 1 commit into
mainfrom
dlapid/memoryCacheUpdate

Conversation

@danlapid

Copy link
Copy Markdown
Collaborator

Introduce a Rust memory-cache core behind the MEMORY_CACHE_V2 autogate, while retaining the existing C++ implementation as the rollback path. Keep the handwritten CXX bridge thin and isolate generated unsafe code from the core, which forbids unsafe code.

Store cache entries in a LinkedHashMap for LRU ordering and maintain a separate expiration index for efficient cleanup. Use a per-key Tokio mutex to coalesce concurrent misses without a custom waiter queue. Represent registry membership with weak flight references so abandoned requests do not retain cache state, while flight destruction safely removes only its own registry entry.

Make leader handoff cancellation-safe through mutex ownership. Promotion follows waiter poll order rather than request creation order, avoiding eager polling and custom scheduling machinery. Track waiter cardinality with lock-free, best-effort counters so metrics do not add contention to the cache-state lock.

Expose the implementation through the existing memory-cache API and add coverage for eviction, expiration, coalescing, cancellation, abandoned reads, leader promotion, weak lifetime cleanup, and the autogated C++ integration.

Introduce a Rust memory-cache core behind the MEMORY_CACHE_V2 autogate,
while retaining the existing C++ implementation as the rollback path.
Keep the handwritten CXX bridge thin and isolate generated unsafe code
from the core, which forbids unsafe code.

Store cache entries in a LinkedHashMap for LRU ordering and maintain a
separate expiration index for efficient cleanup. Use a per-key Tokio
mutex to coalesce concurrent misses without a custom waiter queue.
Represent registry membership with weak flight references so abandoned
requests do not retain cache state, while flight destruction safely
removes only its own registry entry.

Make leader handoff cancellation-safe through mutex ownership. Promotion
follows waiter poll order rather than request creation order, avoiding
eager polling and custom scheduling machinery. Track waiter cardinality
with lock-free, best-effort counters so metrics do not add contention to
the cache-state lock.

Expose the implementation through the existing memory-cache API and add
coverage for eviction, expiration, coalescing, cancellation, abandoned
reads, leader promotion, weak lifetime cleanup, and the autogated C++
integration.
@danlapid
danlapid requested review from a team as code owners August 24, 2026 23:46
@ask-bonk

ask-bonk Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

LGTM

github run

expiration: f64,
now_ms: f64,
) -> Result<ffi::WriteTrace, memory_cache::CacheError> {
let permit = self

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The current head fails mandatory Clippy here with significant_drop_tightening; the crate-level boxed_local expectation is also unfulfilled. Please shorten permit's lifetime and remove the obsolete expectation so the lint build passes.

Comment thread src/workerd/api/memory-cache-v2.c++
let removed = state.bindings.remove(&id);
debug_assert!(removed.is_some());
recompute_limits(&mut state, &self.namespace);
state.resize(now_ms);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

release() always calls resize(), which scans every entry for oversized values while holding the cache mutex, even when removing this binding leaves the effective limits unchanged. With a large shared cache and many equivalent live bindings, each isolate teardown therefore blocks all cache operations for O(entries). Could we skip resizing unless effective limits shrink, and only run the oversized-value scan when max_value_size decreases?

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.82024% with 449 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.93%. Comparing base (099fec0) to head (1331199).

Files with missing lines Patch % Lines
src/rust/memory-cache/lib.rs 59.45% 223 Missing ⚠️
src/workerd/api/memory-cache-v2.c++ 51.93% 102 Missing and 10 partials ⚠️
src/rust/memory-cache/ffi/lib.rs 54.33% 79 Missing ⚠️
src/workerd/api/memory-cache-test.c++ 82.46% 0 Missing and 27 partials ⚠️
src/workerd/api/memory-cache.h 60.00% 3 Missing and 3 partials ⚠️
src/workerd/api/memory-cache.c++ 81.81% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7110      +/-   ##
==========================================
- Coverage   67.95%   67.93%   -0.02%     
==========================================
  Files         464      467       +3     
  Lines      130735   131860    +1125     
  Branches    21389    21452      +63     
==========================================
+ Hits        88840    89579     +739     
- Misses      28923    29269     +346     
- Partials    12972    13012      +40     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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