Skip to content

fix(index): exclude shared metadata cache from LanceIndexStore deep size#7574

Merged
wjones127 merged 2 commits into
lance-format:mainfrom
Ali2Arslan:fix/index-store-deepsize-exclude-metadata-cache
Jul 2, 2026
Merged

fix(index): exclude shared metadata cache from LanceIndexStore deep size#7574
wjones127 merged 2 commits into
lance-format:mainfrom
Ali2Arslan:fix/index-store-deepsize-exclude-metadata-cache

Conversation

@Ali2Arslan

Copy link
Copy Markdown
Contributor

Summary

LanceIndexStore::deep_size_of_children counted its metadata_cache:

self.object_store.deep_size_of_children(context)
    + self.index_dir.as_ref().deep_size_of_children(context)
    + self.metadata_cache.deep_size_of_children(context)   // <-- shared, session-scoped

But that cache is session-scoped and shared. Session::deep_size_of_children already accounts for it once (rust/lance/src/session.rs), and LanceCache::deep_size_of_children returns approx_size_bytes() while ignoring the dedup Context. So the shared cache isn't deduplicated: every opened index that holds a store re-counts the entire cache as its own bytes, inflating and N-times double-counting reported memory (N = number of open index stores).

Change

Count only the store's own footprint — the object store and index dir. The shared metadata cache is left to Session to report once.

Test plan

  • test_store_deep_size_excludes_metadata_cache — inserting a 4 MB blob into the shared cache must not change store.deep_size_of(). Verified it fails before the fix (before: 276, after: 4,194,620) and passes after.
  • cargo fmt --all and cargo clippy -p lance-index --all-targets -- -D warnings clean.

Made with Cursor

`LanceIndexStore::deep_size_of_children` summed its `metadata_cache`, but
that cache is session-scoped and shared: `Session::deep_size_of_children`
already accounts for it once, and `LanceCache`'s size ignores the dedup
`Context`. So every opened index holding a store re-counted the entire
cache as its own bytes, inflating and N-times double-counting reported
memory. Count only the store's own fields (object store + index dir).

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions github-actions Bot added A-index Vector index, linalg, tokenizer bug Something isn't working labels Jul 2, 2026
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@wjones127 wjones127 self-assigned this Jul 2, 2026
@wjones127 wjones127 self-requested a review July 2, 2026 15:51
Comment on lines +54 to +57
// Exclude the shared, session-scoped `metadata_cache` (accounted once by
// `Session::deep_size_of_children`): it is not this store's own footprint, and
// sizing it here makes every opened index that holds a store report the whole
// cache as its own bytes — inflating and N-times double-counting it.

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.

issue(non-blocking): Removing metadata_cache seems correct, but object store is another shared object here. I wonder if the real fix is making LanceIndexStore not part of the cache entries themselves, instead just making it cheap to reconstruct LanceIndexStore on load. You can see how we did this for Vector indices with IvfStateEntry:

pub(crate) trait IvfStateEntry: DeepSizeOf + Send + Sync + 'static {
fn serialize_state(&self, w: &mut CacheEntryWriter<'_>) -> Result<()>;
fn reconstruct<'a>(
&'a self,
object_store: Arc<ObjectStore>,
file_metadata_cache: &'a LanceCache,
index_cache: LanceCache,
frag_reuse_index: Option<Arc<FragReuseIndex>>,
) -> BoxFuture<'a, Result<Arc<dyn VectorIndex>>>;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks Will! I'll take a look and follow up

@Ali2Arslan Ali2Arslan Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

hmm, I took a look, so you're saying we should remove LanceIndexStore from all the scalar indexes so that we're not caching it and instead it's always reconstructed? Similar to what some are already doing with their *State structs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I can enumerate the scalar indexes which don't currently have a *State + reconstruct impls

@Ali2Arslan Ali2Arslan Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is this general direction good with you Will? #7652
If so, I can create PR's for the other indexes as well and then drop the LanceIndexStore.

@wjones127 wjones127 merged commit 92535a5 into lance-format:main Jul 2, 2026
30 checks passed
@Ali2Arslan Ali2Arslan deleted the fix/index-store-deepsize-exclude-metadata-cache branch July 7, 2026 04:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-index Vector index, linalg, tokenizer bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants