feat(commit): align functionality with java paimon commit#397
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.
This PR expands commit conflict detection and index-file handling in FileStoreCommitImpl, adds row-id/write-column conflict checking for data-evolution scenarios, and updates/extends unit tests accordingly.
Changes:
- Add
ConflictDetection,ManifestEntryChanges, andRowIdColumnConflictCheckerto centralize commit change collection and conflict checks (including DV/global index considerations). - Support committing index files during overwrite / filter-and-overwrite flows, and adjust commit-kind selection (e.g., DV index append => overwrite kind).
- Update commit retry behavior and add/adjust tests for index files, retries, and PK table commit behavior.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/paimon/core/operation/file_store_commit_test.cpp | Adds test validating DV index append triggers overwrite commit kind |
| src/paimon/core/operation/file_store_commit_impl_test.cpp | Extends commit tests for index files, retry metrics, and conflict checking signature changes |
| src/paimon/core/operation/file_store_commit_impl.h | Introduces new conflict/index APIs and wires in ConflictDetection |
| src/paimon/core/operation/file_store_commit_impl.cpp | Implements index-aware overwrite, retry commit checking, and centralized change collection |
| src/paimon/core/operation/file_store_commit.cpp | Removes prior PK-commit restriction logic in Create |
| src/paimon/core/operation/commit/row_id_column_conflict_checker.h | Adds row-id/write-column overlap conflict checker interface |
| src/paimon/core/operation/commit/row_id_column_conflict_checker.cpp | Implements row-id/write-column overlap conflict checker |
| src/paimon/core/operation/commit/manifest_entry_changes.h | Adds helper to collect manifest + index changes from commit messages |
| src/paimon/core/operation/commit/manifest_entry_changes.cpp | Implements change collection and “changed partitions” logic |
| src/paimon/core/operation/commit/manifest_entry_changes_test.cpp | Adds unit tests for ManifestEntryChanges |
| src/paimon/core/operation/commit/conflict_detection.h | Adds conflict detection API covering bucket/key-range/row-id/global-index checks |
| src/paimon/core/operation/commit/conflict_detection.cpp | Implements conflict detection logic and row-id history scan |
| src/paimon/core/operation/commit/conflict_detection_test.cpp | Adds unit test coverage for conflict detection behaviors |
| src/paimon/common/data/binary_row.h | Adds std::hash for tuple key used in conflict detection |
| src/paimon/CMakeLists.txt | Wires new sources + tests into the build |
Comments suppressed due to low confidence (1)
src/paimon/core/operation/file_store_commit.cpp:1
- The PR description is still the unfilled template (e.g., linked issue
#xxx, tests section empty), but this change alters user-visible behavior by removing the previous PK-table commit restriction inFileStoreCommit::Create. Please update the PR title/description to clearly state the behavioral change, link the real issue, and list the tests validating the new PK commit behavior.
/*
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
zjw1111
left a comment
There was a problem hiding this comment.
Reviewed the commit conflict-detection alignment against Java Paimon. The port is largely faithful (ManifestEntryChanges collection order, RetryWaiter backoff math, SequenceSnapshotProperties ADD-only max, row-tracking assignment all match Java). A few correctness gaps and divergences are worth fixing before merge — see inline.
Highest priority: RowIdColumnConflictChecker::ConflictsWith swallows field-resolution errors and can let a schema mismatch pass conflict detection.
Also, a few items not inlined:
- Test coverage:
RowIdColumnConflictCheckerhas no C++ test at all (Java has 7 cases, incl.testUsesFieldIdAcrossRename/testFailsOnUnknownNonSystemWriteColumnwhich cover two of the issues below);CheckRowIdExistence/CheckRowIdRangeConflicts/SequenceSnapshotPropertiesalso lack direct tests. Please add these. - Style (
docs/code-style.md): plainintinrow_id_column_conflict_checker.cppbinary search (low/high/mid/index) andstd::unordered_set<int>infile_store_commit_impl.cppNumChangedBuckets — use fixed-width types. - Intentional partial ports (please confirm and add a note): DV+UNAWARE entry enrichment,
pkClusteringOverrideshort-circuit in CheckKeyRange, partition-expire handling in CheckDeleteInEntries, anddropStatson overwrite delete entries are all omitted. - The PR description is still the unfilled template — please fill in the change summary, linked issue, and tests.
| return field_name == SequenceNumber().Name() || field_name == ValueKind().Name() || | ||
| field_name == RowKind().Name() || field_name == RowId().Name() || | ||
| field_name == IndexScore().Name(); |
| /*commit_user=*/"commit_user_1", /*commit_identifier=*/9223372036854775807, | ||
| /*commit_kind=*/Snapshot::CommitKind::Append(), /*time_millis=*/1758097357597, | ||
| /*log_offsets=*/std::map<int32_t, int64_t>(), /*total_record_count=*/5, | ||
| /*total_record_count=*/5, |
| /*commit_user=*/"commit_user_1", /*commit_identifier=*/9223372036854775807, | ||
| /*commit_kind=*/Snapshot::CommitKind::Append(), /*time_millis=*/1758097357597, | ||
| /*log_offsets=*/std::map<int32_t, int64_t>(), /*total_record_count=*/5, | ||
| /*total_record_count=*/5, |
|
|
||
| class PAIMON_EXPORT CoreOptions { | ||
| public: | ||
| // Specifies how to initialize the next sequence number for primary key table writers. |
zjw1111
left a comment
There was a problem hiding this comment.
Cross-language review against Java Paimon found four additional correctness and boundary-condition gaps; see the inline comments.
The existing unresolved threads about overwrite ADD/DELETE deduplication and preserving the underlying commit error also still apply. I would not approve this revision until the P1 data-integrity issues are addressed.
| const std::vector<ManifestEntry>& delta_files, | ||
| std::vector<ManifestEntry>* snapshot_assigned) { | ||
| for (const auto& entry : delta_files) { | ||
| ManifestEntry assigned_entry = CloneEntryWithClonedFileMeta(entry); |
There was a problem hiding this comment.
ManifestEntry assigned_entry = entry only copies the shared DataFileMeta, so this assignment mutates the fixed provider's original input. If the atomic commit loses a CAS race, the next attempt receives metadata already stamped with the old snapshot ID and first-row ID; because those fields are now nonzero/non-null, it skips reassignment against the advanced latest snapshot and can commit overlapping row IDs or an old sequence. Java's entry.assignSequenceNumber / assignFirstRowId return new immutable entries. Please deep-copy the metadata (or make these APIs return new objects) and add a retry test where the first attempt fails and nextRowId advances concurrently.
| for (size_t j = group_start; j < i; ++j) { | ||
| const ManifestEntry& entry = entries_with_ranges[j].second; | ||
| if (IsDedicatedStorageFile(entry.FileName())) { | ||
| continue; |
There was a problem hiding this comment.
Skipping dedicated-storage files here omits Java's second half of the range check. Java verifies that every blob/vector row-ID range is fully contained by one data-file range; with this implementation, data [0,9] plus blob [5,14] returns OK. That can commit a dangling or cross-data-file mapping. Please split data and dedicated ranges and add the equivalent containment check.
|
|
||
| PAIMON_ASSIGN_OR_RAISE(Snapshot check_snapshot, | ||
| snapshot_manager_->LoadSnapshot(row_id_check_from_snapshot_.value())); | ||
| if (!check_snapshot.NextRowId()) { |
There was a problem hiding this comment.
This should fail closed rather than silently disable history conflict detection. Java requires nextRowId to be present for rowIdCheckFromSnapshot and throws otherwise. Returning OK here lets migrated/older snapshots bypass the row-ID column conflict scan and may admit overlapping writes. Please return an invalid status that identifies the snapshot.
|
|
||
| void RetryWaiter::RetryWait(int32_t retry_count) const { | ||
| int32_t non_negative_retry_count = std::max<int32_t>(0, retry_count); | ||
| double exponential = std::pow(2.0, static_cast<double>(non_negative_retry_count)); |
There was a problem hiding this comment.
Clamp before converting to int64_t. With a sufficiently large configured commit.max-retries, min_retry_wait_ms_ * pow(2, retry_count) exceeds the integer range before std::min is applied; the conversion is not safe and can produce a non-positive delay, turning retries into a hot loop. Java applies Math.min(..., maxRetryWait) before the cast. A saturating calculation would preserve the configured maximum wait.
Purpose
Linked issue: close #xxx
Tests
API and Format
Documentation
Generative AI tooling