Conversation
…a files Extends the concurrent-DV merge added for pure-delete RowDelta commits to operations that also add data files (e.g. UPDATE and MERGE). Pure deletes are commutative, so any concurrent DV can be unioned; an operation that rewrites rows into new data files can only union a concurrent delete's DV when the newly deleted positions are disjoint from the positions this operation deletes, otherwise the same row would be both rewritten here and deleted concurrently. Positions already deleted at the operation's starting snapshot (the DVs it supersedes and removes) are subtracted before comparing, so they are not counted as a conflict. Those base positions are read once and cached per referenced data file so that the check stays correct on commit retries, after the superseded DVs are dropped from the pending removals. DV reads go through EncryptingFileIO so they work on encrypted tables. Concurrent DVs added by overwrite operations remain excluded. The merge runs in the validation path, which re-runs after refresh() on every commit attempt, so the union is recomputed against fresh table state. The DV bitmaps are read on the driver only for data files that both this operation and a concurrent commit added a DV to, so the check is skipped entirely when the commits touch disjoint data files. It is gated by the table property commit.row-level.concurrent-dv-merge.enabled (default true); when disabled, an operation that adds data files falls back to failing on any concurrent DV for a data file it also added a DV to. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sezruby
force-pushed
the
feat/dv-rlc-update-merge
branch
from
September 11, 2026 02:03
5a15a68 to
15867ee
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Extends the concurrent deletion-vector (DV) merge added in apache#17754 to operations that also add data files (UPDATE / MERGE), not just pure deletes.
apache#17754 lets a
RowDeltamerge a concurrently added DV into its own DV for the same data file instead of failing, for operations that only add DVs. Because deletes are commutative, those DVs are unioned unconditionally.An operation that also rewrites rows into new data files cannot union unconditionally. If the concurrent DV newly deletes a row position that this operation also newly deletes (the row it is rewriting), merging would let the same row be both rewritten here and deleted concurrently, which is a lost update. This change allows the merge only when the newly deleted positions are disjoint, and fails validation otherwise.
How
BaseRowDelta.canMergeConcurrentDVs()no longer excludes operations that add data files; it returns!validateNewDeleteFiles && !deletesDataFiles(), and for operations that add data files it additionally requires the table property below.MergingSnapshotProducer.mergedConcurrentDVsMustBeDisjoint()(defaultfalse;BaseRowDeltareturnsaddsDataFiles()).mergeConcurrentDVscallsvalidateDisjointConcurrentDV, which fails when(dv_self ∩ dv_concurrent) − dv_baseis non-empty for the referenced data file.dv_baseis the set of positions already deleted at this operation's starting snapshot (the DVs it supersedes and removes); those positions are shared by both DVs and are not a conflict, so they are subtracted before comparing. Base positions are read once and cached per referenced file so that commit retries stay correct after the superseded DVs are dropped from the pending removals.commit.row-level.concurrent-dv-merge.enabled(defaulttrue) gates this. When disabled, an operation that adds data files falls back to failing on any concurrent DV for a data file it also added a DV to (the prior behavior). Pure delete commits are unaffected and always merge.Where it runs and what it reads
The merge runs in the validation path (
validate()→validateAddedDVs→mergeConcurrentDVs), which re-runs afterrefresh()on every commit attempt, so the union is recomputed against fresh table state rather than only on a retry. DV bitmaps are read on the driver only for data files that both this operation and a concurrent commit added a DV to — the file-level overlap is checked from manifest metadata first, so the read is skipped entirely when the commits touch disjoint data files. DV reads go throughEncryptingFileIOso they work on encrypted tables.Testing
TestRowDelta, format versions 3 and 4:commit.row-level.concurrent-dv-merge.enabled=false, a disjoint concurrent DV is not merged and the commit fails.Scope
This is the DV-merge part of the concurrency refinement for DV tables. Real Spark UPDATE/MERGE also set
validateNoConflictingDeleteFiles(), whose file-granular check fails before this merge runs; relaxing that check for DV tables so end-to-end UPDATE/MERGE can reach this merge is a separate follow-up. Design context: apache#18020.🤖 Generated with Claude Code