perf: serialize updated_fragment_offsets as RoaringBitmap bytes in proto#7432
perf: serialize updated_fragment_offsets as RoaringBitmap bytes in proto#7432jerryjch wants to merge 2 commits into
Conversation
|
Important This PR touches the Lance format specification. Substantive changes to the format specification — the If this is a meaningful format change:
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Hi, @wjones127 @pengw0048 I created a PR for #7080. Can you check if this is a good approach for the issue? |
|
Thanks, @jerryjch . I'm not very familiar with the Lance internals and it would be best if a maintainer can review this PR. |
wjones127
left a comment
There was a problem hiding this comment.
I have one question. Once that's addressed them I'm happy to open a PMC vote on this change.
| // Per-fragment physical row offsets that matched an update_columns hash join (RewriteColumns). | ||
| // Deprecated: use updated_fragment_offset_bitmaps (field 10) instead. | ||
| map<uint64, UInt32List> updated_fragment_offsets = 9; | ||
| // Per-fragment matched offsets as portable RoaringBitmap bytes (replaces field 9). | ||
| // Writers emit field 10 only. Readers prefer field 10; fall back to field 9 for | ||
| // manifests written before this change. | ||
| map<uint64, bytes> updated_fragment_offset_bitmaps = 10; |
There was a problem hiding this comment.
question(blocking): if an older reader sees just field 10, it will ignore it. What are the consequences of that?
There was a problem hiding this comment.
@wjones127 Thanks for the review.
If an old reader deserializes a commit that only has field 10, it will not populate updated_fragment_offsets (field 9 is empty and field 10 is unknown to the old generated proto). That does not affect table data or OCC. Each writer—old or new—applies in-memory offsets in build_manifest at commit time and updates fragment.last_updated_at_version_meta in the manifest, which is what drives _row_last_updated_at_version at scan time. Both old and new readers read that from fragment metadata. The conflict resolver does not use updated_fragment_offsets. Field 9/10 is only the on-disk protobuf txn encoding.
The only consequence is on old Lance reading the stored txn (e.g. readTransaction()): offsets won’t round-trip for audit/history. Committed dataset versions remain correct. We chose single-write to field 10 to avoid persisting the large UInt32List on new commits. New readers can fall back to field 9 for txns written by older Lance. Old binaries need an upgrade to decode field 10 from txn bytes. This is pretty normal for the kind of proto migration.
|
Hello @jerryjch, do you have any progress in the pr? |
|
@dshepelev15, The PR is pending review and approval. cc @wjones127 |
|
Thanks, I've opened a PMC vote thread here: #7705 That will close in one week, at which point we can merge this PR. |
|
Hi @wjones127 , are we good with the vote? |
Hi @pengw0048 I'm still trying to get votes. We need 3 +1 votes. We currently have zero. I'm asking PMC members to weigh in. |
Fixes: #7080
Summary
Follow-up to #6650. The
updated_fragment_offsetsfield (proto field 9) stores per-fragmentmatched row offsets as
map<uint64, UInt32List>-- one uint32 per matched row. For denserewrites this produces multi-GB manifests (e.g. 86k matched rows x 4 bytes x many fragments).
This PR adds proto field 10 (
map<uint64, bytes>) using portable RoaringBitmap serialization,which typically compresses the same data to tens of bytes per fragment. Writers emit field 10
only; readers prefer field 10, falling back to field 9 for manifests written before this change.
Background
PR #6650 added
updated_fragment_offsetsto theUpdatetransaction message so thatbuild_manifestcan partially refresh_row_last_updated_at_versionfor matched rows only.The encoding choice -- one uint32 per offset in a
UInt32List-- was flagged post-merge as asize regression for dense updates. The offsets are already stored internally as
RoaringBitmap;this PR aligns the proto encoding with that representation.
Changes
protos/transaction.proto
map<uint64, UInt32List> updated_fragment_offsets) with a commentpointing to field 10.
map<uint64, bytes> updated_fragment_offset_bitmapswith documentation ofthe dual-read strategy.
rust/lance/src/dataset/transaction.rs
Serialization (
From<&Transaction> for pb::Transaction):RoaringBitmap::serialize_intoproduces portable bytes for eachfragment's bitmap.
HashMap(forward compat; old readers ignore unknown fields).Deserialization (
TryFrom<pb::Transaction> for Transaction):RoaringBitmap::deserialize_from.UInt32ListtoRoaringBitmap::from_iter(legacy fallback).
if !new_field.is_empty() { ... } else { ... }pattern used by the existingRewrite.groups/Rewrite.old_fragmentsmigration.Invalid field 10 bytes fail deserialize with
Error::invalid_input.In-memory type unchanged:
UpdatedFragmentOffsets(HashMap<u64, RoaringBitmap>).Test plan
test_proto_round_trip_field_10-- write a transaction with field 10, read back, verifyoffsets match for two fragments.
test_proto_legacy_field_9_read-- construct a proto with only field 9 populated(simulating an old writer), deserialize, verify offsets are correctly recovered.
test_proto_field_10_takes_precedence_over_field_9-- when both fields are present,field 10 values are used and field 9 is ignored.
Proto wire format change; team vote may be needed.
Backward compatibility
number reuse.
commits. Old Lance versions deserializing commits written by this PR will not recover
offsets from the txn; that only affects audit/
readTransaction()on historicalcommits, not table data or OCC.
versions that predate this change.
UpdatedFragmentOffsets) is unchanged; onlythe proto wire encoding changes.
Independent of #6748 and lance-spark #528 (JNI wiring). No mutual merge dependencies.