Skip to content

fix(index): replace scalar cache entries on store rotation - #7962

Open
u70b3 wants to merge 2 commits into
lance-format:mainfrom
u70b3:fix-session-cache-store-lifecycle
Open

fix(index): replace scalar cache entries on store rotation#7962
u70b3 wants to merge 2 commits into
lance-format:mainfrom
u70b3:fix-session-cache-store-lifecycle

Conversation

@u70b3

@u70b3 u70b3 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • cache each default live scalar index together with the IndexStore binding that opened it
  • let LanceIndexStore reuse a live index only when both stores share the same Arc and index directory; a rotation reloads the index and overwrites the single in-memory entry
  • keep the existing portable BTree, Bitmap, and LabelList state caches unchanged and reusable across store bindings
  • preserve normal scalar/FTS cache hits and the existing FTS prewarm zero-I/O contract without introducing serialized FTS runtime state
  • cover a two-fragment A -> B -> A rotation, same-binding reuse, result correctness, and per-store index I/O ownership

Scope

This PR fixes only store-bound live scalar cache entries. It does not add TTL/TTI, change vector or legacy-index behavior, or introduce a new portable FTS state format. #7958 described an intermediate #7944 design that was not merged and is being corrected separately.

Custom IndexStore implementations are conservative by default: they do not reuse a live index unless they explicitly establish storage-binding equivalence.

Testing

  • cargo fmt --all -- --check
  • cargo clippy --all --tests --benches -- -D warnings
  • cargo test -p lance-index --lib
  • cargo test -p lance --lib test_scalar_cache_uses_current_object_store -- --nocapture
  • cargo test -p lance --lib test_fts_prewarm_with_position_controls_phrase_query_cache -- --nocapture
  • make build from python/
  • uv run pytest python/tests/test_scalar_index.py::test_index_prewarm from python/

Closes #7959

@coderabbitai

coderabbitai Bot commented Jul 24, 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

Index caching now avoids retaining store-bound scalar and vector objects across object-store generations. Portable IVF state remains cacheable, while readers are reopened against the current store. Regression tests verify cache isolation, current-store IO, and prewarm behavior.

Changes

Index cache isolation

Layer / File(s) Summary
Scalar cache eligibility
rust/lance-index-core/src/scalar.rs, rust/lance-index/src/scalar/..., rust/lance/src/index.rs
IndexStore reports whether bound indices can be cached; LanceIndexStore disables this path, and scalar cache behavior plus object-store isolation are tested.
Portable IVF state and vector opening
rust/lance/src/index.rs
Legacy live vector caching is removed. IVF state remains codec-backed, while vector construction, bandwidth configuration, cache namespacing, and insertion use the selected object store.
Fresh readers and cache validation
rust/lance/src/index/vector/ivf/..., rust/lance/src/dataset/tests/..., python/python/tests/test_scalar_index.py
IVF reconstruction reopens readers using cached metadata, and tests verify current-store reads, portable cache reuse, position prewarming, and legacy prewarm cache statistics.

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

Sequence Diagram(s)

sequenceDiagram
  participant Dataset
  participant IndexCache
  participant ObjectStore
  participant VectorIndex
  Dataset->>IndexCache: open index
  IndexCache-->>Dataset: portable IVF state or cache miss
  Dataset->>ObjectStore: open readers for current store
  ObjectStore-->>VectorIndex: index data
  VectorIndex-->>Dataset: reconstructed index
  Dataset->>IndexCache: store portable IVF state
Loading

Possibly related PRs

Suggested reviewers: westonpace, bubblecal

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: avoiding caching of store-bound scalar indices.
Description check ✅ Passed The description matches the patch and explains the scalar cache isolation, portable-state reuse, and regression coverage.
Linked Issues check ✅ Passed The changes address #7958 and #7959 by removing store-bound vector caching and making scalar/FTS caches reopen against the current store.
Out of Scope Changes check ✅ Passed The modified files stay focused on cache isolation, vector reconstruction, and regression tests for the linked store-rotation behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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-session-cache-store-lifecycle branch from f1a2cca to f6fdf31 Compare July 24, 2026 05:05

@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.

Actionable comments posted: 2

🧹 Nitpick comments (1)
rust/lance/src/index/vector/ivf/v2.rs (1)

6570-6585: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add #[cfg_attr(coverage, coverage(off))] to this test utility.

NoPartitionCacheBackend and its CacheBackend impl are test-only scaffolding and should be excluded from coverage instrumentation.

♻️ Suggested annotations
+#[cfg_attr(coverage, coverage(off))]
 #[derive(Debug)]
 struct NoPartitionCacheBackend(lance_core::cache::MokaCacheBackend);
 #[async_trait::async_trait]
+#[cfg_attr(coverage, coverage(off))]
 impl lance_core::cache::CacheBackend for NoPartitionCacheBackend {

As per coding guidelines: "disable coverage for test utilities with #[cfg_attr(coverage, coverage(off))]".

🤖 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/vector/ivf/v2.rs` around lines 6570 - 6585, Add
#[cfg_attr(coverage, coverage(off))] to the NoPartitionCacheBackend test utility
and its CacheBackend implementation, excluding both scaffolding components from
coverage instrumentation.

Source: Coding guidelines

🤖 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.

Inline comments:
In `@rust/lance-index-core/src/scalar.rs`:
- Around line 249-257: Expand the documentation for
can_cache_store_bound_indices with a compiling usage example that demonstrates
both true and false implementations, and link to the relevant cache and
index-opening APIs using their actual symbols and signatures. Keep the example
synchronized with the public contract and clarify when implementers should
override the default.

In `@rust/lance/src/index.rs`:
- Around line 2942-2975: Replace the manually constructed test fixtures in the
batch setup around RecordBatchIterator with arrow_array’s record_batch!() macro.
Remove the redundant Schema, Arc, and RecordBatch::try_new boilerplate while
preserving the existing id and text values and resulting RecordBatchIterator
behavior.

---

Nitpick comments:
In `@rust/lance/src/index/vector/ivf/v2.rs`:
- Around line 6570-6585: Add #[cfg_attr(coverage, coverage(off))] to the
NoPartitionCacheBackend test utility and its CacheBackend implementation,
excluding both scaffolding components from coverage instrumentation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 6335ac2e-99de-4a65-815f-d9354fafa37f

📥 Commits

Reviewing files that changed from the base of the PR and between f1a2cca and f6fdf31.

📒 Files selected for processing (5)
  • rust/lance-index-core/src/scalar.rs
  • rust/lance-index/src/scalar/lance_format.rs
  • rust/lance-index/src/scalar/registry.rs
  • rust/lance/src/index.rs
  • rust/lance/src/index/vector/ivf/v2.rs

Comment thread rust/lance-index-core/src/scalar.rs Outdated
Comment thread rust/lance/src/index.rs

@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)

2400-2557: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

IVF_RQ opens against the wrong directory.

Every other IVF variant branch (IVF_FLAT, IVF_PQ, IVF_SQ, IVF_HNSW_FLAT, IVF_HNSW_SQ, IVF_HNSW_PQ) passes the locally-resolved index_dir (from self.indice_files_dir(&index_meta), which honors index_meta.base_id for e.g. shallow-cloned datasets). IVF_RQ instead passes self.indices_dir(), ignoring any base redirection. For an IVF_RQ index whose metadata points at a different base (cloned dataset), this will resolve to the wrong location and fail to open.

🐛 Proposed fix
                     "IVF_RQ" => {
                         let ivf = IVFIndex::<FlatIndex, RabitQuantizer>::try_new(
                             object_store.clone(),
-                            self.indices_dir(),
+                            index_dir,
                             uuid.to_owned(),
                             frag_reuse_index,
                             self.metadata_cache.as_ref(),
                             index_cache,
                             file_sizes,
                         )
                         .await?;
                         Ok(wrap_ivf(ivf))
                     }
🤖 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 2400 - 2557, Update the IVF_RQ branch
in the index-opening match to pass the locally resolved index_dir to
IVFIndex::try_new, matching every other IVF variant. Do not use
self.indices_dir(), so index metadata base redirection remains honored.
🤖 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 2400-2557: Update the IVF_RQ branch in the index-opening match to
pass the locally resolved index_dir to IVFIndex::try_new, matching every other
IVF variant. Do not use self.indices_dir(), so index metadata base redirection
remains honored.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 5c7e1498-9d16-4f7a-9f13-3d6df6327c64

📥 Commits

Reviewing files that changed from the base of the PR and between f6fdf31 and 9404ed7.

📒 Files selected for processing (1)
  • rust/lance/src/index.rs

@u70b3
u70b3 force-pushed the fix-session-cache-store-lifecycle branch from 9404ed7 to e140b65 Compare July 27, 2026 10:39

@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.

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

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)

2515-2527: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Pass index_dir to IVF_RQ construction

IVF_RQ currently constructs with self.indices_dir(), which always uses the dataset root and ignores the index metadata’s base_id. Like the other IVF branches, use the already resolved index_dir from indice_files_dir(&index_meta).

🐛 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 2515 - 2527, Update the IVF_RQ branch’s
IVFIndex::<FlatIndex, RabitQuantizer>::try_new call to pass the already resolved
index_dir from indice_files_dir(&index_meta) instead of self.indices_dir(). Keep
the remaining construction arguments and wrap_ivf flow unchanged.
🟡 Other comments (2)
rust/lance/src/index.rs-3277-3287 (1)

3277-3287: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

This assertion passes vacuously if the cache key type name ever changes.

The regression check filters on the literal "ScalarIndex". If that type name is renamed, the filter matches nothing, scalar_cache_entries is empty, and the assertion succeeds while store-bound scalar indices are once again being retained — the exact regression this test exists to catch.

Assert the filter is meaningful, e.g. capture all key type names and additionally assert that the expected FTS/scalar-related name is absent, or derive the literal from the key type rather than hard-coding it.

🤖 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 3277 - 3287, Update the cache assertion
in the test around index_cache_keys so it cannot pass vacuously when the scalar
index key type name changes. Replace the hard-coded "ScalarIndex" filter with a
type-name value derived from the relevant key type, or separately validate that
the expected scalar/FTS key name exists before asserting no matching entries
remain.
rust/lance/src/index.rs-1629-1635 (1)

1629-1635: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Error message omits which segment failed and which condition tripped.

With a multi-segment batch the caller cannot tell whether the problem is retired fragment coverage or a fragment-reuse mapping, nor which segment uuid to rebuild. Include the offending uuid(s) and the triggering condition.

As per coding guidelines, "Include full context in error messages such as variable names, values, sizes, types, and indices; avoid generic messages."

🛠️ Sketch
-            if has_retired_coverage || requires_rebuild {
-                return Err(Error::invalid_input(
-                    "CreateIndex: NGram segments built before compaction must be rebuilt or merged \
-                     with merge_existing_index_segments before commit"
-                        .to_string(),
-                ));
-            }
+            if has_retired_coverage || requires_rebuild {
+                let reason = if has_retired_coverage {
+                    "cover fragments no longer in the dataset"
+                } else {
+                    "are affected by a fragment-reuse mapping"
+                };
+                let uuids = segments
+                    .iter()
+                    .map(|segment| segment.uuid().to_string())
+                    .collect::<Vec<_>>()
+                    .join(", ");
+                return Err(Error::invalid_input(format!(
+                    "CreateIndex: NGram segments [{uuids}] {reason}; rebuild them or merge with \
+                     merge_existing_index_segments before commit"
+                )));
+            }
🤖 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 1629 - 1635, Update the invalid-input
error in the has_retired_coverage/requires_rebuild branch to include the
offending segment UUID(s) and identify whether retired coverage or
fragment-reuse mapping triggered the failure. Use the relevant variables
available in the surrounding index-creation flow, while preserving the existing
rebuild-or-merge guidance.

Source: Coding guidelines

🧹 Nitpick comments (3)
rust/lance/src/index.rs (1)

1586-1588: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Explain why NGram and FMIndex opt out of the merged_dataset_version overwrite.

ngram::merge_segments already sets dataset_version itself (current manifest version on the rebuild path, source_dataset_version otherwise), so overwriting it here would mislabel a rebuilt segment as stale. That reasoning is invisible at this call site, and the negative list will rot as other merge paths start owning their own version.

As per coding guidelines, "Comment fallback or guard code paths with when they trigger and why they exist."

♻️ Suggested comment
+        // NGram and FMIndex merges set `dataset_version` themselves (an NGram
+        // rebuild is stamped with the current manifest version), so overwriting
+        // it here would mark a freshly rebuilt segment as stale.
         if !all_ngram && !all_fmindex {
             merged_segment.dataset_version = merged_dataset_version;
         }
🤖 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 1586 - 1588, Add an explanatory comment
alongside the `if !all_ngram && !all_fmindex` guard in the merge flow, stating
that NGram and FMIndex merge paths assign `dataset_version` themselves and must
not be overwritten because rebuilt segments require the current manifest version
while other paths may use `source_dataset_version`. Keep the existing condition
and assignment unchanged.

Source: Coding guidelines

rust/lance/src/index/vector/ivf/v2.rs (2)

6739-6741: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use named rstest cases.

♻️ Proposed rename
 #[rstest]
-#[case(IndexFileVersion::V3)]
-#[case(IndexFileVersion::Legacy)]
+#[case::v3(IndexFileVersion::V3)]
+#[case::legacy(IndexFileVersion::Legacy)]
 #[tokio::test]

As per coding guidelines: "Use rstest for Rust parameterized tests, use readable #[case::{name}(...)] names".

🤖 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/vector/ivf/v2.rs` around lines 6739 - 6741, Update the
parameterized test cases in the visible rstest declaration to use readable named
cases, such as case names identifying IndexFileVersion::V3 and
IndexFileVersion::Legacy, while preserving their existing values and test
coverage.

Source: Coding guidelines


6587-6589: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Type-name string matching can silently stop intercepting partition entries.

is_partition depends on the exact type_name() of the IVF partition cache keys. If those names change, this backend silently caches partitions again and test_vector_cache_uses_current_object_store degrades into a vacuous pass (no per-query partition reads, so the store-routing assertions lose their teeth). Consider tracking intercepted keys in the backend and asserting a non-zero count in the test, so a rename fails loudly.

🤖 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/vector/ivf/v2.rs` around lines 6587 - 6589, Update the
backend cache interception around is_partition to track how many partition-entry
keys are intercepted, and expose that count for test verification. In
test_vector_cache_uses_current_object_store, assert the count is non-zero so
renamed type names fail the test instead of making it vacuously pass; preserve
the existing routing assertions.
🤖 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 2515-2527: Update the IVF_RQ branch’s IVFIndex::<FlatIndex,
RabitQuantizer>::try_new call to pass the already resolved index_dir from
indice_files_dir(&index_meta) instead of self.indices_dir(). Keep the remaining
construction arguments and wrap_ivf flow unchanged.

---

Other comments:
In `@rust/lance/src/index.rs`:
- Around line 3277-3287: Update the cache assertion in the test around
index_cache_keys so it cannot pass vacuously when the scalar index key type name
changes. Replace the hard-coded "ScalarIndex" filter with a type-name value
derived from the relevant key type, or separately validate that the expected
scalar/FTS key name exists before asserting no matching entries remain.
- Around line 1629-1635: Update the invalid-input error in the
has_retired_coverage/requires_rebuild branch to include the offending segment
UUID(s) and identify whether retired coverage or fragment-reuse mapping
triggered the failure. Use the relevant variables available in the surrounding
index-creation flow, while preserving the existing rebuild-or-merge guidance.

---

Nitpick comments:
In `@rust/lance/src/index.rs`:
- Around line 1586-1588: Add an explanatory comment alongside the `if !all_ngram
&& !all_fmindex` guard in the merge flow, stating that NGram and FMIndex merge
paths assign `dataset_version` themselves and must not be overwritten because
rebuilt segments require the current manifest version while other paths may use
`source_dataset_version`. Keep the existing condition and assignment unchanged.

In `@rust/lance/src/index/vector/ivf/v2.rs`:
- Around line 6739-6741: Update the parameterized test cases in the visible
rstest declaration to use readable named cases, such as case names identifying
IndexFileVersion::V3 and IndexFileVersion::Legacy, while preserving their
existing values and test coverage.
- Around line 6587-6589: Update the backend cache interception around
is_partition to track how many partition-entry keys are intercepted, and expose
that count for test verification. In
test_vector_cache_uses_current_object_store, assert the count is non-zero so
renamed type names fail the test instead of making it vacuously pass; preserve
the existing routing assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 2e098483-deaa-4fde-a040-d7b82d187463

📥 Commits

Reviewing files that changed from the base of the PR and between 9404ed7 and e140b65.

📒 Files selected for processing (5)
  • rust/lance-index-core/src/scalar.rs
  • rust/lance-index/src/scalar/lance_format.rs
  • rust/lance-index/src/scalar/registry.rs
  • rust/lance/src/index.rs
  • rust/lance/src/index/vector/ivf/v2.rs

@github-actions github-actions Bot added the A-python Python bindings label Jul 27, 2026

@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.

🧹 Nitpick comments (1)
rust/lance/src/index.rs (1)

3139-3139: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use memory:// for this test, or document why a filesystem store is required.

This test creates a temporary directory despite the repository rule requiring plain memory:// URIs. Verify that the in-memory backend supports the configured credential accessor and I/O tracking; if so, switch the fixture to memory://.

As per coding guidelines, “Use plain "memory://" URIs in tests; no atomic counters or unique suffixes are needed.”

🤖 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` at line 3139, Update the test fixture around
TempStrDir::default to use the plain memory:// URI instead of a temporary
filesystem directory, after verifying the in-memory backend supports the
configured credential accessor and I/O tracking; if it does not, retain the
filesystem store and document that requirement in the test.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@rust/lance/src/index.rs`:
- Line 3139: Update the test fixture around TempStrDir::default to use the plain
memory:// URI instead of a temporary filesystem directory, after verifying the
in-memory backend supports the configured credential accessor and I/O tracking;
if it does not, retain the filesystem store and document that requirement in the
test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 91de701e-f553-4d62-9ea3-2b4b28d2fc8e

📥 Commits

Reviewing files that changed from the base of the PR and between e140b65 and 4d7ef1d.

📒 Files selected for processing (6)
  • python/python/tests/test_scalar_index.py
  • rust/lance-index-core/src/scalar.rs
  • rust/lance/src/dataset/tests/dataset_index.rs
  • rust/lance/src/index.rs
  • rust/lance/src/index/vector/ivf.rs
  • rust/lance/src/index/vector/ivf/v2.rs

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@u70b3
u70b3 force-pushed the fix-session-cache-store-lifecycle branch 7 times, most recently from 878987c to 58d1ac3 Compare July 31, 2026 11:55
@github-actions

Copy link
Copy Markdown
Contributor

Important

This PR touches the Lance format specification.

Substantive changes to the format specification — the .proto definitions
and the spec docs under docs/src/format/ — require a PMC vote before merge.
Minor edits such as typo fixes, wording, or formatting are excluded; use your
judgment.

If this is a meaningful format change:

  • Start a vote following the Lance community voting process.
    Format specification modifications need 3 binding +1 votes (excluding the
    proposer), held on GitHub Discussions, with a minimum voting period of 1 week.
  • Once the vote passes, link the completed vote in this PR. It should not be
    merged until the vote is linked.

@u70b3
u70b3 force-pushed the fix-session-cache-store-lifecycle branch 2 times, most recently from 7c0e630 to f3af492 Compare August 3, 2026 08:26
@u70b3 u70b3 changed the title fix(index): avoid caching store-bound scalar indices fix(index): replace scalar cache entries on store rotation Aug 3, 2026
@u70b3
u70b3 force-pushed the fix-session-cache-store-lifecycle branch 4 times, most recently from 048f7e0 to e5921b6 Compare August 3, 2026 10:39
@u70b3

u70b3 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Rebased the unchanged one-commit binding-replacement patch onto current main (new head e5921b6) to regenerate CI artifacts. The previous linux-build failure was infrastructure-only: nextest could not extract the coverage archive because the zstd stream ended with incomplete frame; all other substantive Rust, Python, Java, and compatibility jobs passed. Fresh isolated local validation passed cargo fmt, both targeted store-rotation/prewarm regressions, and full-workspace clippy. Please re-review the minimized patch. @coderabbitai review

@u70b3
u70b3 force-pushed the fix-session-cache-store-lifecycle branch from e5921b6 to 7691a7a Compare August 3, 2026 13:49
@u70b3
u70b3 force-pushed the fix-session-cache-store-lifecycle branch 5 times, most recently from 5f68074 to ff0a8b4 Compare August 7, 2026 05:40
@u70b3
u70b3 force-pushed the fix-session-cache-store-lifecycle branch from ff0a8b4 to db366a4 Compare August 11, 2026 04:09

u70b3 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

@Xuanwo could you take another look when you have a chance? I rebased this onto current main and fixed the interaction between the store-bound scalar cache entry and the newer FTS single-flight/prewarm residency path.

Local validation passed:

  • cargo fmt --all -- --check
  • cargo clippy --all --tests --benches -- -D warnings
  • cargo check -p lance --lib
  • cargo test -p lance-index --lib (1005 passed, 2 ignored)
  • store A → B → A scalar-cache rotation regression
  • FTS position-prewarm and best-effort residency regressions

@u70b3
u70b3 force-pushed the fix-session-cache-store-lifecycle branch 2 times, most recently from f3dc75e to bced32f Compare August 11, 2026 06:22
Live scalar cache entries retained readers from the ObjectStore used on first open. Cache each live index together with its storage binding, reuse it only for an equivalent binding, and overwrite it on rotation. Portable plugin state remains unchanged.

Update SingleScalarContainerCacheBackend to also reject store-bound cache entries so the partial-prewarm tests from lance-format#8298 keep simulating a single-container cache.
@u70b3
u70b3 force-pushed the fix-session-cache-store-lifecycle branch from bced32f to 022cca8 Compare August 21, 2026 12:13
lance-gatekeeper[bot]

This comment was marked as outdated.

@lance-gatekeeper lance-gatekeeper Bot added the K-changes Latest Gatekeeper recommendation requests changes. label Aug 21, 2026
Keep one stable scalar cache slot across store rotations and recheck the binding under a shared async guard so concurrent credential-generation opens reuse a single reload.
@lance-gatekeeper lance-gatekeeper Bot removed the K-changes Latest Gatekeeper recommendation requests changes. label Aug 21, 2026

@lance-gatekeeper lance-gatekeeper 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.

Gate recommendation: approve.

The stable per-index cache slot now serializes store-binding replacement and rechecks beneath the shared async guard, so equivalent concurrent rotations perform one reload while atomically switching readers to the current object store. The targeted current-store, prewarm, and eight-way concurrent cold-open regressions pass.

Please mark this PR with the breaking-change label.

@lance-gatekeeper lance-gatekeeper Bot added the K-approved Latest Gatekeeper recommendation permits acceptance. label Aug 21, 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 A-python Python bindings bug Something isn't working K-approved Latest Gatekeeper recommendation permits acceptance.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: scalar/FTS index caches are not isolated by object store and may reuse expired credentials after rotation

1 participant