Skip to content

fix(index): rebind vector readers to current object store#7944

Open
u70b3 wants to merge 5 commits into
lance-format:mainfrom
u70b3:fix-vector-index-cache-credential-rotation
Open

fix(index): rebind vector readers to current object store#7944
u70b3 wants to merge 5 commits into
lance-format:mainfrom
u70b3:fix-vector-index-cache-credential-rotation

Conversation

@u70b3

@u70b3 u70b3 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • cache only portable IVF state and file metadata, rebuilding readers with the object store supplied by each dataset open
  • stop sharing legacy live vector indices across dataset opens because their readers are object-store-bound
  • use the actual index object store throughout vector index construction while preserving reusable IVF state and partition entries

Testing

  • cargo fmt --all -- --check
  • cargo clippy --all --tests --benches -- -D warnings
  • cargo test -p lance --lib test_vector_cache_uses_current_object_store -- --nocapture
  • cargo test -p lance --lib test_prewarm_ivf_pq -- --nocapture
  • cargo test -p lance --lib test_prewarm_and_query_with_serializing_backend -- --nocapture

Closes #7904

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Vector index caching now accounts for object-store identity. Legacy live-index caching is removed, IVF partition namespaces use IVF state keys, and reconstructed IVF readers bind to the current object store while reusable metadata remains cached.

Changes

Object-store cache identity

Layer / File(s) Summary
Index opening and cache namespace updates
rust/lance/src/index.rs
Legacy live-index cache lookup and insertion are removed; IVF partition entries use the IVF state key namespace; scheduler and IVF construction use the active object store.
IVF reader cache isolation
rust/lance/src/index/vector/ivf/v2.rs
Cached reader keys include object-store identity, while reconstruction opens fresh readers through the supplied store and reuses portable metadata.
Cross-object-store validation
rust/lance/src/index/vector/ivf/v2.rs
Tests add selective partition-cache bypassing and validate queries, reader origins, cache-key contents, and IVF state reuse across store identities.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Dataset
  participant IVFIndex
  participant MetadataCache
  participant ObjectStore
  Dataset->>IVFIndex: reconstruct_typed(object_store)
  IVFIndex->>MetadataCache: reuse portable file metadata
  IVFIndex->>ObjectStore: open readers for current store
  ObjectStore-->>IVFIndex: return current-store readers
  IVFIndex-->>Dataset: return reconstructed index
Loading

Possibly related issues

Possibly related PRs

Suggested labels: A-index

Suggested reviewers: wjones127, bubblecal, xuanwo

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: vector readers are rebound to the current object store.
Description check ✅ Passed The description accurately summarizes the cache and reader rebinding changes in this PR.
Linked Issues check ✅ Passed The changes address #7904 by rebuilding bound readers with the current object store and avoiding stale legacy index reuse.
Out of Scope Changes check ✅ Passed The added cache key and test updates are in scope for fixing object-store-bound vector index reuse.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@u70b3
u70b3 force-pushed the fix-vector-index-cache-credential-rotation branch from d65ca56 to b649e77 Compare July 23, 2026 16:35

@wjones127 wjones127 left a comment

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.

This seems like the wrong solution to me. Keep in mind, some cache backends might spill these entries to disk, and reload them on restart. Or others might serialize them to another service like redis and share the entries amongst servers. So think an in-process atomic counter is too granular and could even create issues for the re-use on restart case. See my comment in this thread:

#7904 (comment)

@u70b3

u70b3 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, I understand the intended invariant now: cached index state should be portable, and the current object store should be injected during reconstruction rather than represented in the cache key.

The runtime identity in this PR is currently used only for codec-less, in-memory entries (CachedIndexReaders and the legacy live index), so persistent backends cannot serialize those entries today. However, I agree that a process-local counter is not an appropriate cache namespace and that it preserves connection-bound readers instead of removing that dependency.

I will rework the V2+ path to cache only portable IVF state/file metadata and always construct readers from the current object store during reconstruction. For legacy indices, since the cached value is inherently store-bound, I will bypass the shared live-index cache rather than add store identity to its key.

@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.35669% with 12 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
rust/lance/src/index/vector/ivf/v2.rs 90.83% 10 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@u70b3
u70b3 force-pushed the fix-vector-index-cache-credential-rotation branch from d77ffca to dd3204f Compare July 23, 2026 17:58
@u70b3 u70b3 changed the title fix(index): isolate vector caches by object store fix(index): rebind vector readers to current object store Jul 23, 2026
@u70b3

u70b3 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Updated in dd3204fd9 following the portable-state approach:

  • removed the process-local object store identity and all provider plumbing
  • removed cached open FileReaders; V2+ reconstruction now reuses only portable IVF state/file metadata and binds fresh readers to the current object store
  • stopped sharing legacy live vector indices across dataset opens because they cannot be detached from their readers
  • retained portable IVF state and partition reuse
  • extended the regression test to cover A → B → A object-store usage, V3 state reuse, legacy indices, multi-fragment recall, and credential-key leak checks

The targeted rotation, prewarm, serializing-backend tests, full fmt, and full clippy all pass.

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rust/lance/src/index.rs (1)

2319-2331: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

IVF_RQ passes the default indices directory while using the index-specific object store.

IVF_RQ resolves a base-aware index_dir via indice_files_dir(&index_meta) in the same arm, but still passes self.indices_dir() into IVFIndex::try_new. For relocated index base paths, that stores IVF partition lookup paths under the dataset default while the store prefix is already bound to the index-specific path. Pass index_dir consistently with the other IVF arms.

🐛 Proposed fix
                     "IVF_RQ" => {
                         let ivf = IVFIndex::<FlatIndex, RabitQuantizer>::try_new(
                             object_store.clone(),
-                            self.indices_dir(),
+                            index_dir,
                             uuid.to_owned(),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance/src/index.rs` around lines 2319 - 2331, Update the IVF_RQ arm’s
IVFIndex::try_new call to pass the already-resolved index_dir from
indice_files_dir(&index_meta) instead of self.indices_dir(), keeping the
index-specific object store and directory prefix consistent for relocated index
bases.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@rust/lance/src/index.rs`:
- Around line 2319-2331: Update the IVF_RQ arm’s IVFIndex::try_new call to pass
the already-resolved index_dir from indice_files_dir(&index_meta) instead of
self.indices_dir(), keeping the index-specific object store and directory prefix
consistent for relocated index bases.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: f1c3b8b7-e1f0-4ea4-9eae-d9f90c4d3eb4

📥 Commits

Reviewing files that changed from the base of the PR and between d77ffca and dd3204f.

📒 Files selected for processing (2)
  • rust/lance/src/index.rs
  • rust/lance/src/index/vector/ivf/v2.rs

@u70b3
u70b3 force-pushed the fix-vector-index-cache-credential-rotation branch from 91436c2 to 1e9cc8a Compare July 24, 2026 05:06

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rust/lance/src/index.rs (1)

2319-2331: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the resolved index directory for IVF_RQ. This arm passes self.indices_dir() while the other v0.3+ arms pass index_dir, which is already resolved by self.indice_files_dir(&index_meta). For indices stored on external/base paths, IVF_RQ readers can be opened from the wrong location; pass index_dir to IVFIndex::<FlatIndex, RabitQuantizer>::try_new(...).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance/src/index.rs` around lines 2319 - 2331, Update the IVF_RQ arm in
the index-loading match to pass the resolved index_dir from
self.indice_files_dir(&index_meta) into IVFIndex::<FlatIndex,
RabitQuantizer>::try_new instead of self.indices_dir(), matching the other v0.3+
arms and preserving external/base-path resolution.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@rust/lance/src/index.rs`:
- Around line 2319-2331: Update the IVF_RQ arm in the index-loading match to
pass the resolved index_dir from self.indice_files_dir(&index_meta) into
IVFIndex::<FlatIndex, RabitQuantizer>::try_new instead of self.indices_dir(),
matching the other v0.3+ arms and preserving external/base-path resolution.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: a42bbf26-1e3b-4d4e-bcd6-17426fd6be3f

📥 Commits

Reviewing files that changed from the base of the PR and between 91436c2 and 1e9cc8a.

📒 Files selected for processing (2)
  • rust/lance/src/index.rs
  • rust/lance/src/index/vector/ivf/v2.rs

@u70b3

u70b3 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Updated the PR and synchronized it with current main.

Follow-up changes:

  • use the resolved index directory for IVF_RQ so the object store and path come from the same base
  • add a two-fragment shallow-clone IVF_RQ regression with a fresh Session and recall >= 0.5
  • strengthen the A -> B -> A credential-rotation test to verify partition cache reuse and stable cache namespaces, in addition to reader I/O ownership
  • exclude the test-only cache backend from coverage instrumentation

Validation:

  • cargo check -p lance-examples --example hnsw
  • cargo test -p lance --lib test_shallow_clone_ivf_rq_uses_resolved_index_directory -- --nocapture
  • cargo test -p lance --lib test_vector_cache_uses_current_object_store -- --nocapture
  • cargo test -p lance --lib test_prewarm_ivf_pq -- --nocapture
  • cargo test -p lance --lib test_prewarm_and_query_with_serializing_backend -- --nocapture
  • cargo fmt --all -- --check
  • cargo clippy --all --tests --benches -- -D warnings

The previous runtime-identity implementation is no longer present; the PR now caches only store-free IVF state/metadata/partitions and reconstructs readers with the current object store. @wjones127, could you please re-review the portable-state version?

@u70b3

u70b3 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up for the first CI run:

  • removed the coverage(off) annotations from the test backend because the lance crate does not enable that nightly attribute and coverage cannot be applied to a struct
  • updated the two legacy prewarm tests to match the portable-state design: legacy live indices reopen through the current store, while prewarmed store-free partitions are verified through increasing index-cache hit counts

The previously failing legacy single-delta and multi-delta tests now pass locally, along with the credential-rotation and IVF_RQ shallow-clone regressions. Full fmt and workspace Clippy also pass. Pushed as 9543f79.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-encoding Encoding, IO, file reader/writer bug Something isn't working

Projects

None yet

2 participants