[python] Extend commit protocol for compaction (DataIncrement/CompactIncrement) - #7873
[python] Extend commit protocol for compaction (DataIncrement/CompactIncrement)#7873TheR1sing3un wants to merge 2 commits into
Conversation
c1fc089 to
4ee0654
Compare
|
Ready for review, 1st pr of the entire compaction feature. |
JingsongLi
left a comment
There was a problem hiding this comment.
Review: [python] Extend commit protocol for compaction (DataIncrement/CompactIncrement)
Overall this is a well-structured PR that cleanly separates write-side and compaction-side semantics by introducing DataIncrement / CompactIncrement value objects, aligning pypaimon's CommitMessage with Java's CommitMessageImpl. The code is clean, well-documented, and has good test coverage. A few observations:
Correctness
-
encode_valuedoes not handlelist/tuplevalues. If a GenericRow or partition column ever carries an ARRAY-typed value, serialization will raiseTypeError. This may be fine for the current scope (partition keys are typically scalar), but worth a brief comment or a graceful error message mentioning "ARRAY" if it's intentionally out of scope. -
_index_file_to_dictdropsdv_rangesandglobal_index_meta— the comment explains this is deferred to Phase 6/7. Just want to flag: if any code path populates those fields before Phase 6/7 lands, a round-trip through the serializer will silently lose them. TheIndexFileMeta.__eq__only checks the four scalar fields so tests won't catch this. Consider adding an assertion/warning in_index_file_to_dictif those fields are non-None, similar to how_build_commit_entriesrejects un-wired increment slots.
Design
-
_build_commit_entriesentry ordering. For a message with bothnew_filesandcompact_before/compact_after(currently rejected at the API level but structurally possible), the method emits ADD(new_files), then DELETE(compact_before), then ADD(compact_after). The explicit separation ofcommit()vscommit_compact()makes this a non-issue today, but the ordering assumption should be documented in a comment on_build_commit_entriesfor future maintainers who may relax the validation. -
total_bucketsfallback logic is good. Using the message'stotal_buckets(captured at plan time) over the table's current value correctly handles bucket rescale races. The testtest_build_entries_uses_message_total_buckets_when_setexplicitly covers this, which is great. -
Convenience properties on
CommitMessage(new_files,compact_before, etc.) preserve ergonomics nicely. These delegate cleanly and avoid breaking existing call sites.
Minor / Nits
-
In
_generic_row_to_dict, the fallback path[row.get_field(i) for i in range(len(row))]assumes the row implements__len__. A brief type annotation or protocol check (e.g.,InternalRow) would improve clarity for readers unfamiliar with the row hierarchy. -
The
CommitMessageSerializerusesseparators=(",", ":")for compact JSON output — good choice for a wire format since it minimizes payload size without sacrificing readability when pretty-printed for debugging. -
DataIncrement.empty_increment()andCompactIncrement.empty_increment()class methods duplicate whatDataIncrement()/CompactIncrement()already do with default factories. Unless these are intended as semantic markers for readability in call sites, they could be removed to reduce API surface.
Summary
Solid foundation work. The separation of concerns between write and compaction paths is well-motivated, the serializer is defensively versioned, and the fail-loud approach for un-wired slots (NotImplementedError) is the right call for incremental development. The test suite covers the key round-trip and rejection scenarios thoroughly.
|
I think the new
If such a message is shipped through this serializer, the deserialized message will lose the index manifest deletions and the commit can leave stale global-index entries behind. Could we include |
Nice catch, fixed~ |
|
@JingsongLi @XiaoHongbo-Hope Hi, could me help to review it? Hope to land it! |
12cbd57 to
f545125
Compare
…Increment) Align CommitMessage with Java's CommitMessageImpl shape and add a JSON-safe wire format, so compaction work has somewhere to plug compact_before / compact_after files and a serializer to ship them through Ray workers. - DataIncrement / CompactIncrement value objects; CommitMessage now holds (partition, bucket, total_buckets, data_increment, compact_increment, check_from_snapshot, index_adds, index_deletes). Convenience properties preserve msg.new_files / msg.compact_before ergonomics. - FileStoreCommit.commit() is a single entry that splits each batch by kind: data increments feed an APPEND/OVERWRITE snapshot, compact increments feed a separate COMPACT snapshot (up to two snapshots per commit, mirroring Java). Compaction skips row-id assignment and conflict detection; a COMPACT snapshot inherits the previous index manifest. index_adds / index_deletes ride the snapshot their payload belongs to. - DataFileMeta.to_dict / from_dict with tagged encoding for bytes / Decimal / datetime / date / time / Timestamp; encode_value / decode_value public for CommitMessage.partition round-trip. - CommitMessageSerializer (VERSION=1) covers the full DataIncrement + CompactIncrement shape including IndexManifestEntry and index_adds/deletes. - Convert all CommitMessage construction sites to the increment API. Tests: commit_message_serializer_test, file_store_commit_compact_test, and adapted file_store_commit / partition_predicate / table_update tests.
f545125 to
96b81f1
Compare
check_from_snapshot was collected from every message (including compaction-only ones) and only ever set — never reset — on the reusable ConflictDetection instance. A compact message can round-trip that field through the serializer, so a compact-only commit left the row-id check anchor set, and because StreamTableCommit reuses one FileStoreCommit the next APPEND/OVERWRITE commit ran conflict detection + rollback even when its own data messages had check_from_snapshot=-1. Anchor row-id conflict detection only from messages that contribute data-side entries (non-empty data_increment), and assign the anchor unconditionally (None when absent) so it is recomputed from scratch each data phase and cannot leak across commits.
JingsongLi
left a comment
There was a problem hiding this comment.
Thank you for your continuous updates, but I still feel concerned about this ability because Compaction is a very inefficient task in Python. I would prefer Compaction to be completed by the Rust layer.
Agreed! Lets close it,thanks for your kindly reviews |
Purpose
Align
CommitMessagewith Java'sCommitMessageImplshape and add a JSON-safe wire format, so later compaction work has somewhere to plugcompact_before/compact_afterfiles and a serializer to ship them through Ray workers.Foundation only — read / write / commit produce the same snapshots.
Split from #7771.
Changes
DataIncrement/CompactIncrementvalue objects;CommitMessagenow holds(partition, bucket, total_buckets, data_increment, compact_increment, check_from_snapshot). Convenience properties preservemsg.new_files/msg.compact_beforeergonomics.FileStoreCommitemits ADD forcompact_after, DELETE forcompact_before, auto-pickscommit_kind=COMPACTwhen only compact increments are present. Newcommit_compact()skips row-id assignment.DataFileMeta.to_dict/from_dictwith tagged encoding forbytes/Decimal/datetime/date/time/Timestamp;encode_value/decode_valuepublic forCommitMessage.partitionround-trip.CommitMessageSerializer(VERSION=1) covers the fullDataIncrement+CompactIncrementshape includingIndexFileMeta.Tests
commit_message_serializer_test— round-trip with non-JSON-native partition values + index files + version rejection.file_store_commit_compact_test—compact_before→ DELETE,compact_after→ ADD, auto-COMPACT kind.file_store_commit_test/partition_predicate_test/table_commit_testadapted to the newCommitMessagesignature.