[test](lance) Build the missing IVF_PQ regression fixture - #66779
Merged
Conversation
Contributor
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
### What problem does this PR solve? Issue Number: Part of apache#66495 Problem Summary: test_lance_vector_search documents its doris.vector_search fixture as carrying an IVF_PQ index, but the fixture SQL (run07_create_vector_types.sql) delegated index creation to a companion create_vector_search_index.py that was never committed, and lance-spark-bundle 0.4.0 cannot create vector indexes through SQL. The table therefore had no index at all, so every use_index / nprobes / refine_factor query in the suite silently executed a flat KNN scan while the goldens still looked correct. Nothing in the repository could observe the difference. Reproduction: build the table the old fixture built and probe it with nprobes=1. Lance ignores nprobes on an unindexed dataset and returns exactly the flat top-10 (rows 256,255,257,254,258,253,259,252,260,251 for the boundary query) - identical to the flat baseline, which is why the defect was invisible. Fix: replace the Spark-created table with an offline-generated Directory Namespace V2 catalog that carries a real IVF_PQ index, and add the evidence that the index is actually used. - lance_build_preinstalled_catalog.py rebuilds the committed fixture and self-checks it: exactly one IVF_PQ index named embedding_ivf_pq_f32 covering every fragment, ANNSubIndex and ANNIvfPartition present in the indexed plan, KNNVectorDistance and no ANN node in the flat plan, and the exact 16 * (n - r)^2 distance ladder that every golden and comment encodes, so a change to the data shape fails here instead of surfacing as an opaque golden diff. Index creation goes through the physical dataset because DirectoryNamespace.create_table_index raises UnsupportedOperationError. - doris.vs_ivf_pq_f32 replaces doris.vector_search: 1024 rows in two fragments, 16-dimensional Float32 embedding[j] = (row_id - 1) + j, so the exact squared L2 distance between rows r and n is 16 * (n - r)^2 and the head/tail queries have no distance ties. Columns are declared NOT NULL to match the fixture being replaced, keeping the only non-nullable Lance column mapping recorded by any Lance suite's DESC golden. The vs_<algorithm>_<element type> name encodes one cell of the algorithm x element type matrix, so a missing combination is visible from the table list alone. - The suite gains a silent-fallback discriminator. Row 256 sits on the first IVF partition boundary, so a genuine single-partition probe must miss true neighbours from the next partition. The suite asserts that the nprobes=1 distance sequence differs from flat search; on the previous unindexed fixture the two are identical and the assertion fails. Distances are compared rather than row ids because the boundary query is symmetric and rows r-d and r+d tie. top_k is 9 there, the last cut that lands on a complete tie pair: at 10 the pair at distance 400 is split, so the golden would pin an arbitrary winner that any change to Lance's top-k selection could flip. Which partition edge row 256 lands next to changes on every retrain, so no measured range is hardcoded; --check prints it instead. - IVF_PQ is lossy, so every indexed query uses refine_factor and the suite documents indexed/flat agreement as an observed property of this frozen fixture and pinned Lance version, not an algorithm guarantee. The fixture is generated with the pins in lance_fixture_requirements.txt. Its readers do not all run the same Lance version - a BE built from source uses lance-c v0.1.2 (lance-rs 4.0.1) per thirdparty/vars.sh, the BE in CI comes from the prebuilt doris-thirdparty package and is already on lance-c v0.1.6 (lance-rs 7.0.0-beta), and Spark writes into the same __manifest through lance-java 4.0.0. The writer is therefore pinned to the oldest Lance in that set, which every reader can read. Verified that this does not make the goldens version-dependent: pylance 7.0.0 reads the committed fixture with results identical to pylance 4.0.1 - same index, same refined top-5, same nprobes=1 boundary rows, same IVF partition ranges. Index training is not bit-reproducible, so regenerating the fixture changes the binary output; the reproducible properties are asserted by the generator self-check instead. IVF_FLAT, IVF_SQ, IVF_HNSW_* and the other vector element types are follow-up work for apache#66495. ### Release note None ### Check List (For Author) - Test: Regression test - Fixture generator self-check with the pinned dependencies - test_lance_vector_search regenerated with -forceGenOut, then passed the normal golden comparison - The whole external_table_p0/lance directory passed (6 suites, 0 failed), covering the pre-existing suites that share the regenerated __manifest - Cross-checked that the nprobes=1 golden row order matches what pylance records probing the same physical index directly - Behavior changed: No, test fixture and regression coverage only - Does this need documentation: No
FANNG1
force-pushed
the
lance-ivf-pq-fixture-66495
branch
from
August 14, 2026 10:13
eece8e6 to
e0a1fed
Compare
10 tasks
Contributor
|
run buildall |
Gabriel39
approved these changes
Aug 15, 2026
Contributor
|
PR approved by at least one committer and no changes requested. |
Contributor
|
PR approved by anyone and no changes requested. |
yiguolei
approved these changes
Aug 15, 2026
u70b3
added a commit
to u70b3/doris
that referenced
this pull request
Aug 16, 2026
branch-4.1 apache#66779 replaced the Spark-built vector fixture with the committed offline catalog: doris.vector_search is now doris.vs_ivf_pq_f32 with index embedding_ivf_pq_f32 (num_bits=4, num_sub_vectors=4), and the run07 SQL plus its companion Java builder are gone. The nested_index/BTree and __lance_frag_reuse coverage now lives in lance_build_preinstalled_catalog.py and the committed fixture. Golden regenerated with -forceGenOut and verified against a fresh local cluster plus a freshly rebuilt docker MinIO fixture; the full external_table_p0/lance directory passes (7/7). For apache#66497.
u70b3
added a commit
to u70b3/doris
that referenced
this pull request
Aug 16, 2026
Directory-only golden coverage for the physical entry TVF against the preinstalled fixture (apache#66779): doris.vs_ivf_pq_f32 yields exactly one user entry (embedding_ivf_pq_f32), doris.nested_index yields nested_label_btree while its reserved __lance_frag_reuse system entry is filtered, the unindexed table yields zero rows, an ordinary predicate filters the bounded result, REST catalogs are rejected with a fixed pre-initialization error, and a user without SHOW privilege is denied. For apache#66497 PR2 slice 2.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What problem does this PR solve?
Issue Number: Part of #66495
Problem Summary: test_lance_vector_search documents its doris.vector_search
fixture as carrying an IVF_PQ index, but the fixture SQL
(run07_create_vector_types.sql) delegated index creation to a companion
create_vector_search_index.py that was never committed, and lance-spark-bundle
0.4.0 cannot create vector indexes through SQL. The table therefore had no
index at all, so every use_index / nprobes / refine_factor query in the suite
silently executed a flat KNN scan while the goldens still looked correct.
Nothing in the repository could observe the difference.
Reproduction: build the table the old fixture built and probe it with
nprobes=1. Lance ignores nprobes on an unindexed dataset and returns exactly
the flat top-10 (rows 256,255,257,254,258,253,259,252,260,251 for the boundary
query) - identical to the flat baseline, which is why the defect was invisible.
Fix: replace the Spark-created table with an offline-generated Directory
Namespace V2 catalog that carries a real IVF_PQ index, and add the evidence
that the index is actually used.
self-checks it: exactly one IVF_PQ index named embedding_ivf_pq_f32 covering
every fragment, ANNSubIndex and ANNIvfPartition present in the indexed plan,
KNNVectorDistance and no ANN node in the flat plan, and the exact
16 * (n - r)^2 distance ladder that every golden and comment encodes, so a
change to the data shape fails here instead of surfacing as an opaque golden
diff. Index creation goes through the physical dataset because
DirectoryNamespace.create_table_index raises UnsupportedOperationError.
fragments, 16-dimensional Float32 embedding[j] = (row_id - 1) + j, so the
exact squared L2 distance between rows r and n is 16 * (n - r)^2 and the
head/tail queries have no distance ties. Columns are declared NOT NULL to
match the fixture being replaced, keeping the only non-nullable Lance column
mapping recorded by any Lance suite's DESC golden. The vs__
name encodes one cell of the algorithm x element type matrix, so a missing
combination is visible from the table list alone.
IVF partition boundary, so a genuine single-partition probe must miss true
neighbours from the next partition. The suite asserts that the nprobes=1
distance sequence differs from flat search; on the previous unindexed
fixture the two are identical and the assertion fails. Distances are
compared rather than row ids because the boundary query is symmetric and
rows r-d and r+d tie. top_k is 9 there, the last cut that lands on a
complete tie pair: at 10 the pair at distance 400 is split, so the golden
would pin an arbitrary winner that any change to Lance's top-k selection
could flip. Which partition edge row 256 lands next to changes on every
retrain, so no measured range is hardcoded; --check prints it instead.
documents indexed/flat agreement as an observed property of this frozen
fixture and pinned Lance version, not an algorithm guarantee.
The fixture is generated with the pins in lance_fixture_requirements.txt.
Its readers do not all run the same Lance version - a BE built from source uses
lance-c v0.1.2 (lance-rs 4.0.1) per thirdparty/vars.sh, the BE in CI comes from
the prebuilt doris-thirdparty package and is already on lance-c v0.1.6
(lance-rs 7.0.0-beta), and Spark writes into the same __manifest through
lance-java 4.0.0. The writer is therefore pinned to the oldest Lance in that
set, which every reader can read. Verified that this does not make the goldens
version-dependent: pylance 7.0.0 reads the committed fixture with results
identical to pylance 4.0.1 - same index, same refined top-5, same nprobes=1
boundary rows, same IVF partition ranges.
Index training is not bit-reproducible, so regenerating the fixture changes
the binary output; the reproducible properties are asserted by the generator
self-check instead. IVF_FLAT, IVF_SQ, IVF_HNSW_* and the other vector element
types are follow-up work for #66495.
Release note
None
Check List (For Author)
normal golden comparison
covering the pre-existing suites that share the regenerated __manifest
records probing the same physical index directly