Repository navigation
feat(cache): add tier probes and selective entry reads - #9829
Conversation
793e278 to
eb0525a
Compare
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
There was a problem hiding this comment.
The selective-read hooks and optional priority admission can land independently of the layered index implementation. Recursive sizing can deadlock priority-enabled QuickCache when cached values traverse another wrapper sharing the backend; taking the snapshot before invoking sizing callbacks avoids that risk.
The built-in LanceIndexStore already excludes its shared cache from sizing (#7574), limiting the demonstrated exposure to custom or nested cache values.
| .into_iter() | ||
| .map(|(key, _, value)| { | ||
| key_footprint(&key) | ||
| + size_of_entry(&value.entry, context).unwrap_or(value.size_bytes) |
There was a problem hiding this comment.
Recursive sizing of priority entries can deadlock the backend. The MutexGuard temporary remains alive until .sum() completes, so size_of_entry runs with the priority mutex held. If the value traverses another LanceCache wrapper sharing this backend, it tries to acquire the same mutex and hangs sizing and subsequent cache operations.
Bind snapshot() to a separate local before iterating, so the guard drops before invoking sizing callbacks. This is an adoption risk for recursive cache values; the ordinary-cache control completes.
Reproducer
Add this integration test to lance-core. On this head, the 500 ms completion assertion fails with priority 3; the priority-0 control completes.
use std::{borrow::Cow, sync::Arc, time::Duration};
use lance_core::cache::{CacheCodec, CacheKey, DeepSizeOf, LanceCache, QuickCacheBackend};
struct NestedCacheKey;
impl CacheKey for NestedCacheKey {
type ValueType = LanceCache;
fn key(&self) -> Cow<'_, str> { "nested".into() }
fn type_name() -> &'static str { "gate.nested.cache" }
fn codec() -> Option<CacheCodec> {
Some(CacheCodec::new("gate.priority", 1,
|_, _| Ok(()), |_| Ok(Arc::new(())))
.with_memory_priority(3))
}
}
#[test]
fn priority_sizing_can_reenter_shared_backend() {
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let runtime = tokio::runtime::Builder::new_current_thread().build().unwrap();
runtime.block_on(async {
let backend = Arc::new(QuickCacheBackend::with_capacity(4096));
let outer = LanceCache::with_backend(backend.clone());
let inner = LanceCache::with_backend(backend);
outer.insert_with_key(&NestedCacheKey, Arc::new(inner)).await;
outer.deep_size_of();
tx.send(()).unwrap();
});
});
assert!(rx.recv_timeout(Duration::from_millis(500)).is_ok());
}Run cargo test --locked -p lance-core --test gate_priority_probe priority_sizing_can_reenter_shared_backend after saving the test as tests/gate_priority_probe.rs.
Add backend tier and residency probes, reads that leave promotion to the caller, and codec hooks for selected rows. Add plane metadata and shared/strict admission controls used by layered RaBitQ readers.
Dependencies
Independent PR targeting main; no other Layered RQ PR is a prerequisite.
Integrated main
ec857edd1d15, preserving its cache capacity reporting and explicit shard policy together with selective reads and priority admission. The existing priority-cache test covers both shard policies and verifies that configured capacity remains visible during priority admission and after clear.The rest of the Layered RQ stack and internal-fork integration remain tracked in OSS-2559. Standalone benchmark/POC tools remain excluded.
Validation
On macOS arm64 with Rust 1.97.0, after main integration:
cargo +1.97.0 fmt --all -- --checkcargo +1.97.0 test --locked -p lance-core cache::: 99 passedcargo +1.97.0 clippy --locked --all --tests --benches -- -D warningsNo new performance measurement is claimed. CI is not awaited.
Tracking
OSS-2557