Skip to content

fix(index): compare store logical identity for scalar cache reuse - #9086

Closed
LuQQiu wants to merge 1 commit into
lance-format:mainfrom
LuQQiu:lu/fix-store-binding-clone
Closed

fix(index): compare store logical identity for scalar cache reuse#9086
LuQQiu wants to merge 1 commit into
lance-format:mainfrom
LuQQiu:lu/fix-store-binding-clone

Conversation

@LuQQiu

@LuQQiu LuQQiu commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Problem

LanceIndexStore::is_same_storage_binding decides whether a cached scalar index can be reused for a given store. It compared the store by Arc pointer identity:

Arc::ptr_eq(&self.object_store, &other.object_store) && self.index_dir == other.index_dir

This only returns true when both stores are the exact same Arc instance. When a fresh ObjectStore is constructed per index access — which happens whenever the index is reached through an indirection rather than a single shared handle — the two Arcs differ even though they point at the same underlying storage. index_for_store (registry.rs) then returns None, so every access is treated as a store rotation: the cached scalar index is dropped and reloaded, and any per-query state built on top of it (e.g. a prepared FTS scorer) is rebuilt from scratch on every query.

The effect is severe for FTS: a warm single-token query that should take a few ms instead spends seconds rebuilding the scorer, because the resident/prewarmed cache is never actually hit.

Fix

Compare the store's logical identity instead of the Arc pointer:

self.object_store.store_prefix == other.object_store.store_prefix && self.index_dir == other.index_dir

store_prefix is documented as uniquely identifying the underlying object store (it encodes the scheme, bucket/account, and any wrapping — information not always present in the URL). Two stores reopened for the same location share it even as distinct Arc instances, so the cache is reused; different locations still differ, so the rotation behavior is preserved.

Test

Adds test_same_storage_binding_ignores_arc_identity: opens two ObjectStores separately for the same path (asserting they are distinct Arcs), and asserts is_same_storage_binding returns true; a different location returns false. Verified the test fails against the old Arc::ptr_eq implementation and passes with the fix. Full lance_format suite (16 tests) green; fmt/clippy clean.

`LanceIndexStore::is_same_storage_binding` gated cache reuse on
`Arc::ptr_eq(object_store)`, so it only returned true when both stores were the
exact same Arc instance. When a fresh `ObjectStore` is constructed per index
access (which happens whenever the index is reached through an indirection
rather than a shared handle), the pointers differ, so every access is treated
as a store rotation: the cached scalar index is dropped and reloaded, and any
per-query preparation built on top of it (e.g. an FTS scorer) is rebuilt from
scratch on every query.

Compare the store's logical identity instead: `store_prefix` uniquely
identifies the underlying store (scheme, bucket/account, and any wrapping), so
two stores reopened for the same location share it even as distinct Arc
instances, while different locations still differ.

Adds a regression test asserting that two stores opened separately for the same
location share a binding (and different locations do not).
@github-actions github-actions Bot added A-index Vector index, linalg, tokenizer bug Something isn't working labels Sep 9, 2026
@LuQQiu

LuQQiu commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #9075. My change restored performance by loosening is_same_storage_binding to compare store_prefix, but that would let one request reuse another request's live index and cross the credential/wrapper isolation boundary required by #7959. #9075 is the correct fix: it keeps storage handles request-bound while sharing the reader-free FTS state (stats/scorer/metadata) across requests, and avoids reopening partition files on rebind. Verified #9075 restores single-token QPS to ~3884 (baseline ~3965) with scorer_build_ms back to 0.

@LuQQiu LuQQiu closed this Sep 9, 2026
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.

1 participant