-
Notifications
You must be signed in to change notification settings - Fork 638
feat: pluggable index cache via CacheBackend trait #6222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
wjones127
merged 27 commits into
lance-format:main
from
wjones127:feat/pluggable-index-cache
Mar 29, 2026
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
1a9eddd
feat: make index cache pluggable via CacheBackend trait
wjones127 8ad33f5
feat: pipe type_id through CacheKey to backend
wjones127 3d299f8
fix: address review feedback on pluggable cache
wjones127 00867ad
refactor: move get_or_insert dedup into CacheBackend
wjones127 376a2f7
refactor: remove non-CacheKey methods, type_tag, approx_size_bytes
wjones127 74fdc2c
cleanup
wjones127 1ba4ac3
cleanup
wjones127 1357409
refactor: replace type_id u64 with &'static str on CacheKey
wjones127 2e7602e
feat: add partition serde for all quantizer types (PR #6223)
wjones127 f1ed934
chore: make index_caches module public for downstream codec registration
wjones127 a38be57
feat: add cacheable_state() for VectorIndex disk caching
wjones127 a575f18
feat: add cache_key_prefix to IvfIndexState for reconstruction
wjones127 9c9f7b8
merge
wjones127 ddc3f77
refactor: move VectorIndex reconstruction from cache to call site
wjones127 33429e9
est
wjones127 4fdbe51
fix
wjones127 9ff1ab9
refactor: address PR review feedback
wjones127 a1fb0ba
refactor: move serialization/reconstruction code to PR #6223
wjones127 bc02d58
Merge remote-tracking branch 'upstream/main' into feat/pluggable-inde…
wjones127 c85f9b5
fix: update commit_existing_index to commit_existing_index_segments
wjones127 32d8c62
fix: restore file_sizes optimization and vector cache check
wjones127 8c3ef35
refactor: presize cache keys, extract size helper, document type_name…
wjones127 19aa338
refactor: inline single-caller private cache methods into public API
wjones127 c3f2cb6
refactor: replace opaque byte keys with InternalCacheKey, add was_cached
wjones127 1494cd7
fix: revert approx_size_bytes to iterate entries, fix rustdoc link
wjones127 ae4783a
refactor: remove unused `&self` from CacheKey::type_name and UnsizedC…
wjones127 8b69671
refactor: clarify cache module API surfaces, address review feedback
wjones127 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| // SPDX-FileCopyrightText: Copyright The Lance Authors | ||
|
|
||
| //! Backend interface for cache implementors. | ||
| //! | ||
| //! This module defines the trait that custom cache backends must implement, | ||
| //! along with the key and entry types they operate on. Most callers should | ||
| //! use [`LanceCache`](super::LanceCache) instead of interacting with | ||
| //! backends directly. | ||
|
|
||
| use std::any::Any; | ||
| use std::pin::Pin; | ||
| use std::sync::Arc; | ||
|
|
||
| use async_trait::async_trait; | ||
| use futures::Future; | ||
|
|
||
| use crate::Result; | ||
|
|
||
| /// A type-erased cache entry. | ||
| pub type CacheEntry = Arc<dyn Any + Send + Sync>; | ||
|
|
||
| /// Structured cache key passed to [`CacheBackend`] methods. | ||
| /// | ||
| /// Composed of three parts: | ||
| /// - **prefix**: scopes the key to a dataset or index (e.g. `"s3://bucket/dataset/"`) | ||
| /// - **key**: identifies the specific entry (e.g. `"42"` for a version number) | ||
| /// - **type_name**: distinguishes different value types stored under the same | ||
| /// user key (e.g. `"Vec<IndexMetadata>"`) | ||
| /// | ||
| /// [`LanceCache`](super::LanceCache) constructs these automatically from | ||
| /// [`CacheKey`](super::CacheKey) values; backend authors receive them | ||
| /// ready-made. | ||
| #[derive(Clone, Debug, Hash, PartialEq, Eq)] | ||
| pub struct InternalCacheKey { | ||
| prefix: Arc<str>, | ||
| key: Arc<str>, | ||
| type_name: &'static str, | ||
| } | ||
|
|
||
| impl InternalCacheKey { | ||
| pub fn new(prefix: Arc<str>, key: Arc<str>, type_name: &'static str) -> Self { | ||
| Self { | ||
| prefix, | ||
| key, | ||
| type_name, | ||
| } | ||
| } | ||
|
|
||
| pub fn prefix(&self) -> &str { | ||
| &self.prefix | ||
| } | ||
|
|
||
| pub fn key(&self) -> &str { | ||
| &self.key | ||
| } | ||
|
|
||
| pub fn type_name(&self) -> &'static str { | ||
| self.type_name | ||
| } | ||
|
|
||
| /// Returns true if this key's prefix starts with the given string. | ||
| pub fn starts_with(&self, prefix: &str) -> bool { | ||
| self.prefix.starts_with(prefix) | ||
| } | ||
| } | ||
|
|
||
| /// Low-level pluggable cache backend. | ||
| /// | ||
| /// Implementations store entries keyed by [`InternalCacheKey`] and return | ||
| /// type-erased [`CacheEntry`] values. | ||
| /// [`LanceCache`](super::LanceCache) handles key construction and type safety; | ||
| /// backend authors only need to implement storage and eviction. | ||
| #[async_trait] | ||
| pub trait CacheBackend: Send + Sync + std::fmt::Debug { | ||
| /// Look up an entry by its key. | ||
| async fn get(&self, key: &InternalCacheKey) -> Option<CacheEntry>; | ||
|
|
||
| /// Store an entry. `size_bytes` is used for eviction accounting. | ||
| async fn insert(&self, key: &InternalCacheKey, entry: CacheEntry, size_bytes: usize); | ||
|
|
||
| /// Get an existing entry or compute it from `loader`. | ||
| /// | ||
| /// Implementations should deduplicate concurrent loads for the same key | ||
| /// so the loader runs at most once. | ||
| /// | ||
| /// Returns `(entry, was_cached)` where `was_cached` is `true` if the entry | ||
| /// was already present in the cache (the loader was not invoked). | ||
| async fn get_or_insert<'a>( | ||
| &self, | ||
| key: &InternalCacheKey, | ||
| loader: Pin<Box<dyn Future<Output = Result<(CacheEntry, usize)>> + Send + 'a>>, | ||
| ) -> Result<(CacheEntry, bool)>; | ||
|
|
||
| /// Remove all entries whose prefix starts with the given string. | ||
| async fn invalidate_prefix(&self, prefix: &str); | ||
|
|
||
| /// Remove all entries. | ||
| async fn clear(&self); | ||
|
|
||
| /// Number of entries currently stored (may flush pending operations). | ||
| async fn num_entries(&self) -> usize; | ||
|
|
||
| /// Total weighted size in bytes of all stored entries (may flush pending operations). | ||
| async fn size_bytes(&self) -> usize; | ||
|
|
||
| /// Approximate number of entries, callable from synchronous contexts. | ||
| /// Backends that cannot provide this cheaply should return 0. | ||
| fn approx_num_entries(&self) -> usize { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be better for these to return Option with default of None? |
||
| 0 | ||
| } | ||
|
|
||
| /// Approximate weighted size in bytes, callable from synchronous contexts. | ||
| /// Used by `DeepSizeOf` to report cache memory usage. | ||
| /// Backends that cannot provide this cheaply should return 0. | ||
| /// | ||
| /// Assumes entries do not share underlying buffers; if they do, the | ||
| /// returned total may overcount. | ||
| fn approx_size_bytes(&self) -> usize { | ||
| 0 | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should assume here that no two entries share buffers? It's probably the best we can do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we do. There are ways we can detect this in the future. You may be interested in this PR: #6229