Repository navigation
Description
Search before asking
- I had searched in the issues and found no similar issues.
Version
branch-4.1 at 5dcf1066af4 (opt Support distributed execution for vector_search #66841)
What's Wrong?
test_lance_vector_search fails at the boundary_single_probe check on branch-4.1. The nprobes=1 IVF_PQ query returns a different candidate set than the golden file: only neighbours above the query point come back, and every neighbour below it is missing.
SELECT row_id, label, _distance
FROM vector_search(
"table"="test_lance_vector_search.doris.vs_ivf_pq_f32",
"column"="embedding",
"query_vector"="[255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270]",
"top_k"="9", "metric"="l2", "nprobes"="1",
"refine_factor"="10", "use_index"="true")
ORDER BY _distance, row_idExpected (regression-test/data/external_table_p0/lance/test_lance_vector_search.out):
256 0.0
255 16.0
257 16.0
254 64.0 <- missing
258 64.0
253 144.0 <- missing
259 144.0
252 256.0 <- missing
251 400.0 <- missing
Actual:
256 0.0
255 16.0
257 16.0
258 64.0
259 144.0
260 256.0
261 400.0
262 576.0
263 784.0
The framework reports it as a cell mismatch on line 4 (Expect cell is: 254 / But real is: 258), which reads like a tie-break ordering problem, but it is not: the returned rows themselves differ. The result set is now strictly row_id >= 256, i.e. one side of the query point only.
The failure is deterministic — reproduced on three consecutive runs, byte-identical each time.
What I verified
- Reproduced on a clean build of
5dcf1066af4with no other patches applied. git diff 5dcf1066af4^ 5dcf1066af4 -- regression-test/data/external_table_p0/lance/test_lance_vector_search.outdoes not touch theboundary_single_probeblock; [opt](lance) Support distributed execution for vector_search #66841 updated only thepost_search_filterblocks in that file.- The other six suites under
regression-test/suites/external_table_p0/lance/all pass on the same build, including the newtest_lance_vector_search_two_phase.
What I did not verify
I did not build 5dcf1066af4^ to confirm the case passed before that commit, so I cannot say from my own testing whether #66841 introduced this or whether it was already failing. What is established is that the expectation predates #66841 and no longer holds after it.
The boundary_single_probe case exists specifically to prove the IVF index is used rather than silently falling back to a flat scan (the assertion just above it compares the nprobes=1 distances against the flat search), so a one-sided candidate set here is worth a look rather than a golden refresh.
What You Expected?
nprobes=1 returns the candidate set the golden file records, with neighbours on both sides of the query point.
How to Reproduce?
sh run-regression-test.sh --run -s test_lance_vector_search
against the docker iceberg/MinIO environment (docker/thirdparties/run-thirdparties-docker.sh -c iceberg), with enableIcebergTest = true.
Anything Else?
Found while merging branch-4.1 into #66805, which touches the same Lance files. Filing separately because it reproduces without that PR.
Activity
Doris-Breakwater commentedon Aug 21, 2026
Initial assessment
This is most likely a fixture/object-store mismatch, not a confirmed Doris vector-search bug. The reported candidate set exactly identifies the newer IVF_PQ fixture that was merged after
5dcf1066af4. A clean Doris build does not make the persistent Iceberg/MinIO data clean.Verified facts
5dcf1066af4with its pinned Python dependencies. Itsvs_ivf_pq_f32index UUID is4231299b-169a-4694-b440-7fb7396b1188; the selected IVF partition contains rows1-259, and thenprobes=1boundary query returns the old golden sequence (256,255,257,254,258,253,259,252,251,...).branch-4.1commitf44b3fa49e2([test](lance) Add query coverage for the remaining ANN index types and vector element types #66512) regenerates this non-reproducible IVF index. The table path changes fromc325fd55_doris$vs_ivf_pq_f32to3adf6edd_doris$vs_ivf_pq_f32, and the index UUID changes tof7df7f0a-77f9-428a-abe2-fe510132cfa0.255-510and returns exactly the issue's actual rows:256,255,257,258,259,260,261,262,263.test_lance_vector_search.outto exactly those rows. It is a descendant of5dcf1066af4, is already on currentbranch-4.1, and does not change the relevant FE/BE production path.docker/thirdparties/docker-compose/iceberg/data/input/minio_data.run-thirdparties-docker.sh -c icebergbrings the stack down but does not clear that directory; startup recursively copies the checked-out fixture into the existingwarehouse/lanceprefix without first removing stale objects. Therefore switching commits or rebuilding Doris can leave code, golden, manifest, and index objects from different revisions.nprobes=1on a four-partition IVF_PQ index is approximate.refine_factor=10recomputes exact L2 distances only for candidates obtained from the probed partition; it cannot recover neighbours assigned to another partition. A one-sided result at a partition boundary is therefore not, by itself, a correctness violation. The test's functional discriminator still succeeds because this distance sequence differs from flat search.At
5dcf1066af4, distributed execution does change the physical path: FE creates a split for the physical index segment and its two fragments, and BE enables prefilter beforenearest()while passing the explicit fragment IDs and index-segment UUID. That path deserves isolation only if the old fixture is confirmed. The current evidence does not establish that it changed the old fixture's candidate set.Missing information
Please provide/confirm:
vs_ivf_pq_f32table directory and index UUID actually present in MinIO during the failing run. For an exact5dcf1066af4fixture these should bec325fd55_doris$vs_ivf_pq_f32and4231299b-169a-4694-b440-7fb7396b1188, not the newer values above.docker/thirdparties/docker-compose/iceberg/data/input/minio_data(or an equivalent isolated MinIO data root) was recreated after checking out5dcf1066af4. “Clean build” alone is insufficient.branch-4.1with a fresh, matching fixture. Current branch already contains the newer fixture and matching golden.EXPLAINoutput and BE profile for this query, showing two searched fragments, one index segment,LanceUseIndex=true, andLanceFragmentCount=2; plus results for the same fixture withnprobes=4anduse_index=false.Recommended next steps
branch-4.1,f44b3fa49e2already couples the regenerated fixture to the reported candidate set.5dcf1066af4^against the same unchanged object-store contents. If only5dcf1066af4differs, isolate the two new Lance inputs with a focused lance-c test: unrestricted whole-dataset search versuswith_fragments(all)plus the same singlewith_index_segments(...)selection. That will distinguish a prefilter/segment-planning change from IVF approximation before considering production code changes.nprobes=1are valid only when explicitly tied to the frozen index bytes.No production code change is recommended from the current evidence.
Breakwater-GitHub-Analysis-Slot: slot_a27bbd1ec9da
FANNG1 commentedon Aug 21, 2026
Correction: I can no longer reproduce this, so the "deterministic" characterisation above is wrong and I want to flag that before anyone spends time on it.
What I have now, on the same machine and the same docker environment:
test_lance_vector_searchpasses, three times in a row — once as part of-d external_table_p0/lance(all 7 suites green, includingtest_lance_vector_search_two_phase) and twice run on its own.boundary_single_proberesult now matches the golden, i.e. neighbours on both sides of the query point.What changed in between was FE/BE rebuilds and process restarts, plus an unrelated FE-side refactor of mine that cannot affect the search path. Nothing touched the fixture datasets or the index.
So the accurate statement is: the failure was real and reproduced four times, including on a clean build of
5dcf1066af4with none of my changes present, and then stopped reproducing after rebuilding and restarting. That makes it state-dependent rather than deterministic, and I do not know what the trigger is — the earlier runs were stable enough (byte-identical output across three runs) that it did not look like ordinary flakiness at the time.Two things still stand and are why I am leaving this open rather than closing it:
row_id >= 256only), which is a search-result difference and not a golden-ordering nit.@zhangstar333 — sorry for the noise. If this does not ring a bell as a known state-dependent behaviour in the distributed search path, it is probably not worth chasing on my report alone, and I am happy for this to be closed. I will reopen with a proper reproduction if I hit it again.