lance-format / lance-format/lance
bug: build_chunk_index passes a data_buf_size that disagrees with its chunk deltas
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 7.1k
- Forks
- 852
- Avg merge
- 3d 18h
- Merged PRs (30d)
- 272
Description
Description
PrefixSums::from_deltas asserts that the deltas it receives sum to the total its caller passes (rust/lance-encoding/src/encodings/logical/primitive/chunk_index.rs:50, debug_assert_eq!(acc as u64, total)), and its doc states that "callers must pass the true sum". build_chunk_index breaks that on a legacy-version IVF_HNSW_SQ index build over a column with nulls.
Seen on windows-build, in index::vector::ivf::tests::test_create_index_nulls::case_5_ivf_hnsw_sq::index_version_1_IndexFileVersion__Legacy:
thread '...case_5_ivf_hnsw_sq::index_version_1_IndexFileVersion__Legacy' panicked at
rust\lance-encoding\src\encodings\logical\primitive\chunk_index.rs:50:13:
assertion `left == right` failed
left: 4104
right: 520
4: lance_encoding::...::chunk_index::PrefixSums::from_deltas<...build_chunk_index::closure_env$0>
5: lance_encoding::...::build_chunk_index
Log: https://github.com/lance-format/lance/actions/runs/33505260154/job/99847686907
The caller is build_chunk_index (rust/lance-encoding/src/encodings/logical/primitive.rs:2653), which derives each delta as ((word >> 4) + 1) * MINIBLOCK_ALIGNMENT and passes data_buf_size as the total, on the stated assumption that "the deltas are the chunk sizes and their grand total is the data buffer size". With MINIBLOCK_ALIGNMENT = 8, the two numbers say the deltas covered 513 alignment units while data_buf_size was 65 of them. I have not reduced this to a standalone reproducer, so I cannot say whether words.len() is wrong for the page, whether the words buffer carries entries past the page, or whether it is specific to the legacy writer.
Why it matters past the assertion
The check is debug-only, so a release build proceeds, and total is what selects the storage width: if total <= u32::MAX builds a Vec<u32> and accumulates delta as u32. A page whose real sum exceeds u32::MAX while total undercounts would truncate every prefix silently, and either way the resulting byte_starts describe offsets past the end of the data buffer.
Expected behavior
Either build_chunk_index passes a total that matches the deltas it generates, or the invariant is enforced with an error rather than a debug assertion, so a mismatch fails the write instead of being compiled out of release builds.
Notes
The failing job was on a pull request whose diff (Float16 literal coercion in lance-datafusion) does not touch this path, so it is not caused by that change. Whether it reproduces outside Windows is not established.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with rust/lance-encoding/src/encodings/logical/primitive.rs:2653 and chunk_index.rs:50, then run index::vector::ivf::tests::test_create_index_nulls::case_5_ivf_hnsw_sq::index_version_1_IndexFileVersion__Legacy. Compare the words-derived deltas with data_buf_size and investigate whether entries extend past the page or differ in the legacy writer. Done means the mismatch is covered by a regression test and cannot silently produce invalid byte_starts.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- data-engineering, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100