Skip to content

[EPIC] Deletion vector read support (deletion-vector-v1 Puffin blobs) #2792

Description

@mbutrovich

Is your feature request related to a problem or challenge?

Iceberg format version 3 stores positional deletes as deletion vectors instead of position delete files. A deletion vector is a roaring bitmap of deleted row positions, written as a deletion-vector-v1 Puffin blob and referenced from a delete manifest entry by content_offset and content_size_in_bytes.

iceberg-rust does not apply them on read. CachingDeleteFileLoader::load_file_for_task branches only on DataContentType (PositionDeletes / EqualityDeletes / Data), and the PositionDeletes arm unconditionally calls parquet_to_batch_stream(...) on the file path (crates/iceberg/src/arrow/caching_delete_file_loader.rs). A V3 deletion vector arrives as PositionDeletes content but points at a Puffin file, so the read fails when the loader parses that file as Parquet. The DV branch is stubbed: DeleteFileContext carries // TODO: Delete Vector loader from Puffin files (caching_delete_file_loader.rs:54). Any V3 table with positional deletes is therefore unreadable.

This issue tracks the read path under the V3 epic #2411, scoped to land independently of the DV write path.

What already exists on main

  • DataFile parses the V3 manifest fields referenced_data_file, content_offset, content_size_in_bytes (crates/iceberg/src/spec/manifest/data_file.rs).
  • DeleteVector (a RoaringTreemap wrapper) and the ParsedDeleteFileContext::DelVecs loader variant exist (crates/iceberg/src/delete_vector.rs, caching_delete_file_loader.rs).
  • Positional deletes already flow DeleteVector -> DeleteFilter -> ArrowReader, so DV application reuses that path unchanged.
  • PuffinReader, PuffinWriter, and the DELETION_VECTOR_V1 blob-type constant exist (crates/iceberg/src/puffin/).

Describe the solution you'd like

Implement DV read as a series of small PRs that depend only on each other, not on the write path (#2678 / #2203). DV writing is out of scope here; it belongs with #2678 under #2411.

Status: all tasks below are implemented on the dv-read branch and validated end to end against Spark / Iceberg-Java-written deletion vectors via a draft DataFusion Comet PR (apache/datafusion-comet#4887), which reads real V3 merge-on-read tables through this code and confirms the deleted rows match Spark. They are now being peeled into small, independently reviewable PRs, tracked per task below. The codec PR supersedes #2414, which has been idle since review feedback.

Task list

Two read-path rules from the table spec must hold. A DV applies to a data file when the file path equals the DV's referenced_data_file, the file's data sequence number is <= the DV's, and the partitions match. When a DV applies to a data file, readers ignore any position delete files that would otherwise match it, because the DV subsumes them. At most one DV exists per data file per snapshot.

References

  • Iceberg table spec format/spec.md, "Deletion Vectors": semantics, at-most-one-per-data-file, DV-subsumes-position-deletes, apply-scope rules, and the referenced_data_file / content_offset / content_size_in_bytes fields (required for DVs, must match the Puffin footer).
  • Iceberg Puffin spec format/puffin-spec.md: the deletion-vector-v1 blob byte layout.
  • Iceberg-Java: core/.../deletes/BitmapPositionDeleteIndex.java (framing, length, magic, CRC) and core/.../deletes/RoaringPositionBitmap.java (portable 64-bit layout).

Willingness to contribute

I can contribute to this feature independently.

Metadata

Metadata

Assignees

Labels

epicEpic issue

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions