lance-format / lance-format/lance

bug: build_chunk_index passes a data_buf_size that disagrees with its chunk deltas

Open
#8,947 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.