Skip to content

fix(index): reject incompatible vector segments at segment commit - #9432

Open
u70b3 wants to merge 3 commits into
lance-format:release/v11.0from
u70b3:fix/vector-segment-commit-compat-v11
Open

u70b3 wants to merge 3 commits into
lance-format:release/v11.0from
u70b3:fix/vector-segment-commit-compat-v11

Conversation

@u70b3

@u70b3 u70b3 commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Problem

Dataset::commit_existing_index_segments validated only index_details.type_url across the segment set, so vector segments built with different distance metrics (or dimensions, sub-index types, or quantizer kinds) could be committed as one logical index. Scan planning derives the metric from the first segment and applies it to the whole logical index, so the remaining segments would be searched and ranked under the wrong metric — silently returning incorrect nearest-neighbor results.

Reproduced via the distributed build loop (the path lance-c's lance_dataset_commit_index_segments binds): build fragment 0 as IVF_FLAT/L2 and fragment 1 as IVF_FLAT/Cosine, then commit both metadata blobs — the commit succeeds at ab6b5bbe.

Surfaced during review of lance-format/lance-c#84.

Fix

After replacement selection, validate the coexisting segment set — incoming segments plus retained existing segments — with the same validate_vector_query_compatibility check the query/optimize paths already require (metric, dimension, sub-index type, quantizer kind; independently trained IVF centroids and PQ codebooks may still differ). Incompatible sets are rejected with InvalidInput before any manifest change, so the dataset version and existing index are untouched.

Because incoming segments are not in the manifest yet, the validation opens each segment through a new Dataset::open_vector_index_from_metadata helper — DatasetIndexInternalExt::open_vector_index minus the manifest load_index lookup, extracted mechanically with no behavior change for committed segments.

A complete replacement may still change the metric: replaced segments are no longer part of the coexisting set, so they impose no constraint.

Tests

Three new tests in rust/lance/src/index/vector/ivf/v2.rs:

  • two incoming segments with L2 + Cosine metrics over disjoint fragments are rejected (error names the metric; version unchanged, no index created);
  • an incoming Cosine segment conflicting with a retained L2 segment is rejected (retained index untouched);
  • a full-coverage replacement switching L2 → Cosine is accepted.

test_commit_existing_index_segments_commits_multiple_segments previously committed metadata-only fake segments (payload b"seg0"); since compatibility validation now opens coexisting segments, it builds real IVF segments via execute_uncommitted instead — same assertions otherwise.

  • cargo test -p lance --lib: 3069 passed, 0 failed
  • cargo clippy -p lance --lib --tests: clean

Targeting release/v11.0 so lance-c can pin a v11-line revision containing the fix (its current pin ab6b5bbe = v11.0.0). Happy to forward-port to main if you'd like it there first.


Supersedes #9351 (same commits): that PR picked up a stale format-change label while it briefly targeted main, so the format-spec-vote gate blocks it until a PMC member relabels or votes — this diff touches no format files.

commit_existing_index_segments compared only index_details.type_url
across segments, so vector segments built with different distance
metrics (or dimensions, sub-index types, quantizer kinds) could be
committed as one logical index. Scan planning derives the metric from
the first segment and applies it to the whole logical index, silently
ranking the rest under the wrong metric.

Validate the coexisting segment set — incoming segments plus retained
existing segments, after replacement selection — with the same
validate_vector_query_compatibility check the query path requires,
opening each segment via a new open_vector_index_from_metadata helper
that does not require the segment to be committed yet. A complete
replacement may still change the metric because replaced segments no
longer constrain the set.
rustls 0.23.40 is flagged by RUSTSEC-2026-0285 (TLS 1.3 handshake
messages accepted across encryption level boundaries); 0.23.45 is the
fix release. Refresh all three lockfiles; the root lockfile also picks
up the required aws-lc-rs 1.18.1 / aws-lc-sys 0.45.0 bump.
Backport of the compat-harness change from lance-format#8903 (1391e6c).

pylance 12.0.0 (released 2026-09-17) and the 13.0.0 betas depend on
lance-namespace>=0.11.1,<0.12, which conflicts with the harness's
lance-namespace>=0.8.0,<0.9 pin: venv creation for those versions fails
pip resolution, failing every [12.0.0]/[13.0.0b4] compat case and every
index-maintenance sequence shard (its newer ref resolved to 12.0.0 as
soon as it hit PyPI), which alone burns the 60-minute Compatibility
Tests budget.

Not caused by the index fix in this PR; backported to unblock compat
CI on release/v11.0 (the fix already lives on main).
@github-actions github-actions Bot added A-python Python bindings A-java Java bindings + JNI A-deps Dependency updates labels Sep 19, 2026
@github-actions github-actions Bot added the bug Something isn't working label Sep 19, 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 commit boundary now validates the authoritative query contract across incoming and retained vector segments after replacement selection, so incompatible logical indices are rejected before publication while full replacements may still change metrics. The synchronized rustls backport matches #9212, and the compatibility-harness change is the release-line backport of #8903. No blocking issues remain.

@lance-gatekeeper lance-gatekeeper Bot added the K-approved Latest Gatekeeper recommendation permits acceptance. label Sep 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-deps Dependency updates A-java Java bindings + JNI 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.

1 participant