Repository navigation
fix: resolve vector prewarm name collisions from segment metadata - #97
Conversation
| for metadata in indices { | ||
| let Some(&field_id) = metadata.fields.first() else { | ||
| sdk_segments.push(metadata.uuid); | ||
| continue; | ||
| }; | ||
| if field_id == named_field.id { | ||
| sdk_segments.push(metadata.uuid); | ||
| continue; | ||
| } | ||
| // The pinned SDK interprets an index name as a schema column first. | ||
| // Open colliding vector segments by their indexed field, even on a cold cache. | ||
| let field_path = dataset.schema().field_path(field_id)?; | ||
| let index = dataset | ||
| .open_generic_index(&field_path, &metadata.uuid, &NoOpMetricsCollector) | ||
| .await?; | ||
| if index.index_type().is_vector() { | ||
| index.prewarm().await?; | ||
| } else { | ||
| sdk_segments.push(metadata.uuid); | ||
| } | ||
| } |
There was a problem hiding this comment.
This makes the collision path prewarm every vector segment sequentially. The SDK implementation uses try_join_all, so a logical index with many distributed-build segments currently warms concurrently. Since open_generic_index() and prewarm() can both perform substantial object-store I/O, the new path changes warmup latency from roughly the slowest segment to the sum of all segments. The two-segment local test cannot expose that regression. Please collect the colliding-vector futures and await them concurrently—ideally preserving the SDK path’s error/concurrency semantics—and add a deterministic multi-segment concurrency test.
Run each segment open and prewarm operation together under try_join_all, matching the SDK concurrency and error handling. Keep scalar and unresolved segments on the SDK validation path. Add deterministic gate-based tests for concurrent opens and prewarms, error cancellation, and SDK fallback routing.
There was a problem hiding this comment.
✅ Gate recommendation: approve.
Resolving colliding vector segments by their stored field IDs remains a reasonable workaround for the pinned SDK. Scalar/FTS validation and snapshot/session behavior are preserved.
The revised path runs segment opens and prewarms concurrently and cancels pending work on error, addressing the concurrency concern. All three scheduling tests and all six C API prewarm tests pass, including cold-session collision handling and reuse of both warmed vector segments without partition reloads.
…69332) ### What problem does this PR solve? Related PRs: lance-format/lance-c#97, lance-format/lance-c#96 Update the pinned lance-c revision from `8e9e09ecc795c91ce7f97513db94c2d91eec799d` to `4fafcd64525b274b113208390bbcca86bbbc093b`, the latest upstream `main` commit when this PR was prepared, and update the archive checksum. The new revision includes the upstream fix for cold-session vector prewarm when an index name matches a different schema column. It resolves the indexed field from segment metadata while preserving concurrent segment opening/prewarming. The revision also includes the row-address allowlist/blocklist prefilter APIs from lance-c #96. Refresh one C++ test context in the existing Foyer patch to accommodate the test added by #96. The patch's added/deleted code lines are unchanged; no local prewarm patch is introduced. ### Release note Fix Lance index prewarm failures when a vector index name matches a different schema column. ### Check List (For Author) - Test - [x] Manual test - Downloaded the exact pinned archive and verified its checksum through the Doris downloader. - `bash thirdparty/test/lance-prefilter-patch-test.sh thirdparty/src`: passed all scenarios, including fresh extraction, idempotence, cache invalidation, and patch-failure handling. - `cargo metadata --locked --offline --format-version 1` on the patched source: passed dependency resolution, including Foyer. - Compiled the patched upstream C and C++ test sources with `-fsyntax-only`: passed. - `bash -n` on the dependency scripts and `git diff --check`: passed. - Full Doris build and runtime regression tests were not run locally; requested through `run buildall`. - Behavior changed: - [x] Yes. Includes the upstream prewarm fix and new prefilter APIs. - Does this need documentation? - [x] No. ### Check List (For Reviewer who merge this PR) - [ ] Confirm the release note - [ ] Confirm test cases - [ ] Confirm document - [ ] Add branch pick label
lance_dataset_prewarm_index()can fail for a valid vector index when its name matches another schema column. The C++Dataset::prewarm_index()wrapper consequently throwslance::Error. Resolve the indexed field from segment metadata for this collision so prewarming succeeds on a fresh session and subsequent queries reuse the warmed partitions.Trigger and impact
A concrete reproducer is:
id: Int32andembedding: FixedSizeList<Float32, 8>.IVF_FLATindex onembedding, but name the indexid. The regression uses two fragments and two index segments.lance_dataset_prewarm_index(dataset, "id").Before this fix, the call returns
-1with an error containingData type is not a vector ... but Int32. Index construction and nearest-neighbor queries onembeddingsucceed for the same dataset. Consequently, callers cannot complete an explicit warmup before serving queries; applications that continue after the error still incur cold index reads. Applications that require warmup to succeed can reject an otherwise usable dataset. The demonstrated failure is in prewarm, without changing persisted data or index contents.The name collision matters: an index name that is not a schema column already resolves through index metadata. The test uses a separate session for the cold control query, so that query cannot populate the session used to reproduce prewarm.
Cause and fix
Reproduced against lance-c
2089f6f(currentmain), which pins Lance SDK68c12df. This is a correctness follow-up to #94.The pinned Lance SDK passes the logical index name to
open_generic_index(). Its vector field resolver checks for a schema column with that name before consulting the segment's field ID, soidincorrectly selects the scalar column instead ofembedding.For a name that matches a different schema field, the binding loads the named index's segment metadata and opens each affected vector segment using the indexed field path and segment UUID. It prewarms that opened index on the same dataset snapshot and session. The complete open-and-prewarm operation for each affected segment runs concurrently via
try_join_all, preserving the SDK’s fail-fast error propagation and cancellation of pending segment futures. Other paths continue through the SDK, including missing-index errors and scalar/full-text prewarm with its aggregate residency validation.This keeps the existing C/C++ API and dependency revision. It does not include the Doris-specific Foyer patch. Found while integrating upstream prewarm into apache/doris#69277.
Validation
Linux, Rust 1.97.0, using the unchanged lockfile and default features:
-1and theInt32/vector type error, after its independent cold-query control succeeded.cargo test --locked --test c_api_test test_prewarm_index: all 6 prewarm tests passed. The vector cases cover non-colliding names, names matching the indexed field, and names matching a different scalar field. Both segments return correct nearest neighbors with zero partition reloads after prewarm and a shared-session reopen.cargo test --locked --lib index::tests::: all 3 scheduling tests passed. Deterministic gates verify that all 8 segment opens start before any may finish, and all 8 prewarms start before any may finish. Separate checks verify immediate error propagation/cancellation during open or prewarm, and scalar/unresolved-segment fallback to the SDK. The concurrency tests failed against the serial implementation (1segment started instead of8); no timing thresholds or sleeps are used.cargo test --locked: 492 passed, 0 failed; the 3 ignored C/C++ cases were run separately. Doc-tests also completed successfully.cargo test --locked --test compile_and_run_test -- --ignored --test-threads=1: all 3 C/C++ compilation/runtime cases passed.cargo clippy --locked --all-targets -- -D warnings: passed.cargo fmt -- --checkandgit diff --check: passed.