Skip to content

feat(iceberg): serialize DV-s to the deletion-vector-v1 blob layout - #3232

Open
laskoviymishka wants to merge 2 commits into
apache:mainfrom
laskoviymishka:feat/dv-v1-serializer
Open

laskoviymishka wants to merge 2 commits into
apache:mainfrom
laskoviymishka:feat/dv-v1-serializer

Conversation

@laskoviymishka

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Add DeleteVector::serialize, which produces a deletion-vector-v1 blob that DeleteVector::deserialize reads back (#2580). It frames the roaring treemap as [length][magic][vector][crc], matching what deserialize reads: the roaring portable 64-bit directory (a little-endian bitmap count followed by ascending u32-keyed bitmaps), a big-endian length prefix covering the magic and vector, and a big-endian CRC-32 over the same bytes. The vector is written as-is with sparse-key encoding rather than run-length-encoded, so for the same positions the bytes need not match Iceberg-Java's BitmapPositionDeleteIndex, which run-optimizes and writes dense keys; both encodings are spec-conformant and mutually decodable. This emits the raw blob only; wrapping it in a Puffin Blob with the snapshot and sequence-number properties is left to a later change.

Fold the framing into a single implementation shared by serialize and the tests, so the encode paths no longer duplicate the layout: the test-only frame_dv_blob and the encode_dv_blob helpers now delegate to serialize, and the length prefix uses a checked u32 conversion instead of a truncating cast. Round-trip tests cover an empty vector, a single position, a dense single-container run, positions spanning multiple 2^32 containers, and a run-optimized dense range, plus golden assertions on the magic offset, the length prefix, and CRC validation, and a byte-stability test that re-serializes a decoded blob to guard against container-type normalization on the round trip.

…lob layout

Add DeleteVector::serialize, which produces a deletion-vector-v1 blob that
DeleteVector::deserialize reads back (apache#2580). It frames the roaring treemap as
[length][magic][vector][crc], matching what deserialize reads: the roaring
portable 64-bit directory (a little-endian bitmap count followed by ascending
u32-keyed bitmaps), a big-endian length prefix covering the magic and vector,
and a big-endian CRC-32 over the same bytes. The vector is written as-is with
sparse-key encoding rather than run-length-encoded, so for the same positions
the bytes need not match Iceberg-Java's BitmapPositionDeleteIndex, which
run-optimizes and writes dense keys; both encodings are spec-conformant and
mutually decodable. This emits the raw blob only; wrapping it in a Puffin Blob
with the snapshot and sequence-number properties is left to a later change.

Fold the framing into a single implementation shared by serialize and the
tests, so the encode paths no longer duplicate the layout: the test-only
frame_dv_blob and the encode_dv_blob helpers now delegate to serialize, and the
length prefix uses a checked u32 conversion instead of a truncating cast.
Round-trip tests cover an empty vector, a single position, a dense
single-container run, positions spanning multiple 2^32 containers, and a
run-optimized dense range, plus golden assertions on the magic offset, the
length prefix, and CRC validation, and a byte-stability test that re-serializes
a decoded blob to guard against container-type normalization on the round trip.
@laskoviymishka
laskoviymishka marked this pull request as ready for review September 15, 2026 20:03
Comment thread crates/iceberg/src/delete_vector.rs
Comment thread crates/iceberg/src/delete_vector.rs Outdated
…ad of panicking

Anoop's review noted that the u32 overflow guard in the deletion-vector-v1
framing should not panic. Following the project's preference for avoiding
panics in library code, DeleteVector::serialize and the internal frame_dv_blob
helper now return crate::Result<Vec<u8>>: the u32 length-prefix conversion
yields an ErrorKind::DataInvalid when the body would exceed u32::MAX, and the
roaring treemap write maps its (in practice infallible) error to
ErrorKind::Unexpected instead of expect(). The doc comment's Panics section is
replaced with an Errors section describing these cases. Test-only callers and
the cfg(test) helper are updated for the new Result.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants