Repository navigation
Conversation
sezruby
force-pushed
the
row-level-concurrency-dv-poc
branch
from
July 8, 2026 03:11
001b81e to
73c7bc7
Compare
…merge POC for issue delta-io#7057. Adds a config-gated conflict-resolution phase to ConflictChecker that resolves 'same physical file' conflicts between concurrent DML. It decodes both transactions' deletion vectors and, when the newly-deleted rows are disjoint, merges them (dv_win UNION dv_cur), writes a new DV file, and rebases the losing transaction onto the winner's post-image. The delete/read, delete/delete, and append checks skip resolved paths; rewrite-only DML (DELETE/UPDATE) winners also skip the append check. DV-only (row tracking not required). Gated by spark.databricks.delta.rowLevelConcurrency.enabled (default off). Adds RowLevelConcurrencySuite. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sezruby
force-pushed
the
row-level-concurrency-dv-poc
branch
from
July 8, 2026 03:19
73c7bc7 to
e356283
Compare
sezruby
added a commit
that referenced
this pull request
Jul 27, 2026
… remap Opt-in (spark.databricks.delta.optimize.conflictReconciliation.enabled, internal, default off). When a compaction OPTIMIZE loses to a concurrent DELETE/UPDATE that added a deletion vector to a removed source file, the ConflictChecker remaps that DV onto the compacted output by offset arithmetic (outputPos = runOutStart + (sourceRow - runSourceStart)) and unions it into the output's DV, instead of aborting. The run layout (source_file, start, count) is observed at write time -- cheaply. SourceCompositionCaptureExec reads the source file from InputFileBlockHolder and counts rows per file; no helper column, no _metadata.row_index (avoiding the DV-aware scan cost), rows pass through unchanged (~0% overhead). The composition is persisted in the output AddFile's OPTIMIZE_SOURCE_COMPOSITION tag, so the reconcile is cross-cluster-safe (replayed from the commit, no in-memory state). DV'd source files supported: the driver splits their live rows into contiguous physical segments around the read-time DV gaps, and only the winner's incremental deletions (winnerDv \ readTimeDv) are remapped. Coalesce compaction only; repartition (shuffle empties the holder) and reclustering/ZORDER (row-permuting) are not tagged and abort as before. All-resolvable-or-abort. OPTIMIZE-loser direction only; DELETE-loser and MERGE are follow-ups. Stacked on the row-level-concurrency DV PoC (#5 / PR #2). Adds OptimizeConflictReconciliationSuite (6). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Jul 30, 2026
Remove the unsound "Layer 2" append-suppression for rewrite-only DML winners. Keying the append-check skip on the winner's op type (DELETE/UPDATE) is unsound: an UPDATE can move a row into the loser's predicate (winner `SET x=15`, loser `DELETE WHERE x>10`, row was x=5), a genuine write-skew that suppressing the winner's image file would hide. `canSkipAddedFileForRowLevelConcurrency` now skips only the same-file DV union paths (`rowLevelResolvedPaths`). A rewrite-only DML winner's new image files flow into the standard added-files check (conservative abort, one-way safe); reconciling the provably-disjoint case via conflict-time data skipping is owned by Case 1 (#4/#8). Tests reframed to the Layer-1 envelope (9 tests, all passing): the two "both commit" UPDATE cases move to Case 1; DELETE-vs-UPDATE now asserts the image file conservatively conflicts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sezruby
added a commit
that referenced
this pull request
Aug 2, 2026
… remap Opt-in (spark.databricks.delta.optimize.conflictReconciliation.enabled, internal, default off). When a compaction OPTIMIZE loses to a concurrent DELETE/UPDATE that added a deletion vector to a removed source file, the ConflictChecker remaps that DV onto the compacted output by offset arithmetic (outputPos = runOutStart + (sourceRow - runSourceStart)) and unions it into the output's DV, instead of aborting. The run layout (source_file, start, count) is observed at write time -- cheaply. SourceCompositionCaptureExec reads the source file from InputFileBlockHolder and counts rows per file; no helper column, no _metadata.row_index (avoiding the DV-aware scan cost), rows pass through unchanged (~0% overhead). The composition is persisted in the output AddFile's OPTIMIZE_SOURCE_COMPOSITION tag, so the reconcile is cross-cluster-safe (replayed from the commit, no in-memory state). DV'd source files supported: the driver splits their live rows into contiguous physical segments around the read-time DV gaps, and only the winner's incremental deletions (winnerDv \ readTimeDv) are remapped. Coalesce compaction only; repartition (shuffle empties the holder) and reclustering/ZORDER (row-permuting) are not tagged and abort as before. All-resolvable-or-abort. OPTIMIZE-loser direction only; DELETE-loser and MERGE are follow-ups. Stacked on the row-level-concurrency DV PoC (#5 / PR #2). Adds OptimizeConflictReconciliationSuite (6). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…refs Add coverage for the deletion-vector-merge row-level concurrency path: - non-empty base DV: DELETE id=5 first, then two disjoint concurrent DELETEs, exercising the `(dv_win INTERSECT dv_cur) MINUS base` subtraction that every prior test left empty. - 3-way where the last (overlapping) txn aborts while the two disjoint ones commit. - disjoint DELETEs reconcile under explicit Serializable isolation. - row tracking: surviving rows keep their stable `_metadata.row_id` across the same-file DV merge. - change data feed: each deleted row is emitted once, attributed to the version that deleted it (winner at N, reconciled txn at N+1). Also scrub fork-internal taxonomy from the suite comments (drop "Case 1 / fork issue #4" and "Layer-1" wording) in favor of self-contained descriptions ("conflict-time reader-side data skipping (a separate change)", "same-file DV union"). No behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the RLC deletion-vector merge (rowLevelConcurrencyEnabled,
winningOperationName, canSkipAddedFileForRowLevelConcurrency,
resolveRowLevelConflicts, the DV read/write helpers, and
rowLevelResolvedPaths) out of the 1862-line ConflictChecker into
RowLevelConcurrencyResolution -- a `self: ConflictChecker =>` trait mixed
into the checker. ConflictChecker keeps only the call sites; `spark` and
`winningCommitSummary` are widened to `protected val`, and the DV helpers
stay `protected` so the OPTIMIZE-vs-DML reconciliation (which mixes into
the same checker) can reuse them.
Also add a concrete worked example to the resolveRowLevelConflicts doc:
winner DELETEs {5,10}, current DELETEs {20,21}, disjoint over base {} ->
merge to {5,10,20,21}; had current deleted row 5, overlap {5} -> abort.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…iled canSkipAddedFileForRowLevelConcurrency can only return true for a path in rowLevelResolvedPaths, so when that set is empty the filterNot is a no-op. Guard on rowLevelResolvedPaths.isEmpty to skip the traversal and its allocation on the common no-conflict path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend the same-file deletion-vector union coverage to MERGE: - disjoint concurrent MERGE matched-deletes reconcile (DV union, like DELETE) - a MERGE matched-delete reconciles against a concurrent plain DELETE (the union is operation-agnostic) - overlapping MERGE matched-deletes still conflict - a MERGE matched-update (winner) writes an image file, so a concurrent loser conservatively conflicts, mirroring the standalone-UPDATE case MERGE inserts / WHEN NOT MATCHED BY SOURCE stay out of scope (they need per-file row-tracking classification) and are not exercised. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Apply the same review feedback from Case 1 (delta-io#7358) proactively to Case 2: - Fail-safe fallback: extract the per-file DV read/overlap/merge/write into reconcileFileDeletionVectors and wrap the per-path call in try/catch (NonFatal). If resolving one shared file throws (unreadable/corrupt DV, transient I/O), skip row-level resolution for it and let the standard file-level checks abort cleanly with a retryable Concurrent* exception, instead of surfacing an unexpected error out of conflict detection. Other shared files are still resolved independently. - Index the current transaction's AddFile/RemoveFile maps in a single pass over actions instead of two `.collect{}.toMap` traversals. No behavior change on the happy/genuine-conflict paths: RowLevelConcurrencySuite 18/18 still pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
resolveRowLevelConflicts reconciled each shared file's deletion vectors sequentially on the caller thread. That per-file work is driver-side object-store DV I/O (read + merge + write), so when a winning transaction conflicts on many files the reconciliation window grows with the file count. Reconcile each shared file independently on a bounded pool (cf. DeltaFileOperations footer reads, which use 8); a single conflicting file stays on the caller thread, so the common case is unchanged. Per-file results are collected and then applied on the caller thread, so there is no shared-state race across the pool. The existing per-file NonFatal fail-safe is preserved (moved into reconcileOnePath): a DV decode/merge/write failure for one file skips row-level resolution for it (the standard checks then abort cleanly) without affecting the others. No behavior change on the happy/genuine-conflict paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…l path Every existing RowLevelConcurrencySuite conflict is single-file (sharedPaths.size == 1), which takes the caller-thread branch of resolveRowLevelConflicts. Add a two-file test: a DELETE whose predicate spans two data files DV-updates both in one commit, so the loser reconciles two shared paths at once (parallelism == 2), exercising the ThreadUtils.parmap branch. parmap preserves input order, so the result matches the sequential branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rializable read-op tests Expand reconcileFileDeletionVectors' comment into an explicit induction on the winner chain: after merging winner k the current txn's base advances to winner k's DV, so each step's overlap test reduces to the two ops' own new deletes -- precise for any number of winners and independent of winner order. Add two RowLevelConcurrencySuite tests: - three disjoint DELETEs reconciling over a non-empty base DV (the deepest chain the ordering helpers reach; combines the base-DV and N-way subtleties). - a Serializable MERGE matched-delete loser (a read-then-modify op) vs a concurrent disjoint DELETE, confirming reconciled paths drop from readFiles under the stricter isolation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…f guarding each check Row-level resolution previously threaded a `!rowLevelResolvedPaths.contains(...)` guard into four separate file-level checks (append, delete-read, whole-table, delete-delete) in ConflictChecker. Instead, rewrite both sides of the conflict once in resolveRowLevelConflicts: rebase the current transaction (as before) and prune the reconciled AddFile(P)/RemoveFile(P) pair from a single effective `winningCommitSummary` (now a `var`, mirroring `currentTransactionInfo`). The four checks revert to byte-identical-to-upstream, since they now see a winner that never touched the reconciled files. Removes the trait-level mutable `rowLevelResolvedPaths` set and `canSkipAddedFileForRowLevelConcurrency`; the "don't skip UPDATE image files" reasoning moves to pruneReconciledFiles (only the same-path pair is pruned). ConflictChecker footprint drops to a mixin + one `var` + one call site; all subtle DV logic stays confined to reconcileFileDeletionVectors. RowLevelConcurrencySuite 21/21, FeatureEnablementConcurrencySuite 40/40. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Owner
Author
|
Cross-link: the out-of-scope item "
|
…io#7818) #### Which Delta project/connector is this regarding? - [x] Spark ## Description Retain the loading `KernelContext` when wrapping a Kernel snapshot in `DeltaV2Snapshot`, and use its default engine for subsequent snapshot operations. This keeps snapshot operations associated with the table's context and configured filesystem options instead of constructing an engine from session defaults alone. Thread the context through snapshot managers, transactions, and snapshot copies, and update the affected callers and test fixtures. The snapshot managers, table, and transaction adapter retain context instead of a default-engine field. The context creates a default engine when requested, materializing Hadoop configuration from the active Spark session plus the table's invariant filesystem options. No per-session engine cache or credential-provider refresh policy is introduced. ## How was this patch tested? - `spark/testOnly org.apache.spark.sql.delta.v2.interop.DeltaV2SnapshotSuite`: all 23 tests passed. - `sparkV2/Test/compile` passed, including Java formatting and style checks. - Negative control: restoring the old lazy getter makes the new filesystem regression fail with stale session properties; restoring the fix makes it pass. - Added regression coverage for context-engine delegation on latest and historical snapshots and context retention in catalog copies. - Added a real recording-filesystem regression across sessions A/B/A and inherited child-thread sessions, exercising latest/historical snapshots, allFiles, and transaction-version lookup while checking session properties and table-option precedence. ## Does this PR introduce _any_ user-facing changes? No changes to released APIs. This fixes engine selection in the unreleased Spark connector's snapshot facade. --------- Signed-off-by: Vishnu Chandrashekhar <seewishnew@users.noreply.github.com> Co-authored-by: Vishnu Chandrashekhar <seewishnew@users.noreply.github.com>
#### Which Delta project/connector is this regarding? - [x] Spark ## Description AMT checkpoints currently serialize as an object with separate fields. Change the checkpoint payload to an array of single-action envelopes, as specified by the [adaptive metadata RFC](delta-io#6978 (comment)). Introduce `AMTCheckpointAction` and `AMTCheckpointSingleAction`, carry the checkpoint version in `CheckpointMetadata`, and preserve the field accessors and field-based constructor. Validate that each required singleton action appears exactly once, allow unknown envelopes, and preserve action order. Align Jackson serialization and Spark decoding, including the sidecar `type` field. ## How was this patch tested? Adds and updates coverage in `AdaptiveMetadataActionsSerializerSuite`, `ActionSerializerSuite`, and `LastCheckpointInfoSuite` for action wrapping, JSON round trips, required-action validation, unknown envelopes, Spark schemas, and decoding with sidecars. Updates AMT checkpoint, snapshot, inheritance, writer, and field-spec tests for the new representation. Whitespace and patch-integrity checks pass. Scala tests have not been run locally for this revision; CI validation is pending. ## Does this PR introduce _any_ user-facing changes? The AMT checkpoint JSON payload changes from a field object to an array of action envelopes, matching the RFC. The checkpoint version is stored in its `checkpointMetadata` action. Signed-off-by: Yumingxuan Guo <171533467+yumingxuanguo-db@users.noreply.github.com> Co-authored-by: Yumingxuan Guo <171533467+yumingxuanguo-db@users.noreply.github.com>
…tries (delta-io#7851) ## Description Generates `snapshot_id` and `modified_snapshot_id` for data entries written to adaptive metadata tree (AMT) checkpoints, so each data entry records the snapshot that added it and the snapshot that last modified it. This implements the file-modification tracking described in the protocol RFC delta-io#7820. Key changes: - Populate `snapshot_id` / `modified_snapshot_id` on AMT data entries in `AMTWriteHelper` and `IncrementalAMTWriter`, and carry them through the AMT action model. - Propagate the fields through the commit path (`OptimisticTransaction`, `Checkpoints`, `Snapshot`) and through commands that rewrite or re-add files (`CloneTableBase`, `RestoreTableCommand`, `TahoeRemoveFileIndex`). ## How was this patch tested? Added/updated coverage across the AMT suites (`AMTCheckpointWriteSuite`, `AMTIncrementalWriteInvariantSuite`, `AMTInheritanceSuite`, `AMTInheritanceReadSuite`, `AMTSnapshotSuite`, `AMTBackReferenceSuite`, `AMTPartitionValuesSuite`, `AMTSingleActionSerializerSuite`, `AMTUtilsSuite`) plus `ActionSerializerSuite`, `CheckpointsSuite`, and `LastCheckpointInfoSuite`. ## Does this PR introduce _any_ user-facing changes? No. Co-authored-by: Jinhua Song <jinhua.song@databricks.com>
…elta-io#7852) ## Description Spark's optimizer can request scan statistics (`estimateSizeInBytes` / `estimateStatistics`) before physical planning. Previously the Delta V2 scan always selected files to answer these requests, even for unfiltered scans where the result is already known. For an unfiltered scan (no data filters, no partition filters, no pushed limit, not a CDC read), the snapshot checksum already records the exact size of every file the scan would select. The scan now returns that size directly before planning, so an early optimizer statistics request no longer triggers file selection. Physical planning stays lazy and still selects files exactly once. Filtered, limit, and CDC scans continue to plan files so Spark receives their accurate post-pruning size. If the checksum does not record a table size, the scan falls back to planning rather than reporting an unknown size. ## How was this patch tested? Added tests to `DeltaV2ScanTest`: - Unfiltered scans report statistics without selecting files, and later physical planning selects files exactly once and reuses that selection. - Filtered scans and positive-limit scans plan files exactly once and reuse the selection for repeated statistics requests. - Tables written without checksums fall back to file planning for the size estimate. Existing statistics tests were updated to reflect that full statistics for unfiltered scans now come from snapshot metadata. ## Does this PR introduce _any_ user-facing changes? No.
…ta-io#7847) <!-- Thanks for sending a pull request! Here are some tips for you: 1. If this is your first time, please read our contributor guidelines: https://github.com/delta-io/delta/blob/master/CONTRIBUTING.md 2. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP] Your PR title ...'. 3. Be sure to keep the PR description updated to reflect all changes. 4. Please write your PR title to summarize what this PR proposes. 5. If possible, provide a concise example to reproduce the issue for a faster review. 6. If applicable, include the corresponding issue number in the PR title and link it in the body. --> #### Which Delta project/connector is this regarding? <!-- Please add the component selected below to the beginning of the pull request title For example: [Spark] Title of my pull request --> - [x] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [ ] Other (fill in here) ## Description Stop pushing down non deterministic predicates to CDF nodes, these predicates will get reevaluated at query level thus potentially returning inconsistent results. <!-- - Describe what this PR changes. - Describe why we need the change. If this PR resolves an issue be sure to include "Resolves #XXX" to correctly link and close the issue upon merge. --> ## How was this patch tested? New UTs <!-- If tests were added, say they were added here. Please make sure to test the changes thoroughly including negative and positive cases if possible. If the changes were tested in any way other than unit tests, please clarify how you tested step by step (ideally copy and paste-able, so that other reviewers can test and check, and descendants can verify in the future). If the changes were not tested, please explain why. --> ## Does this PR introduce _any_ user-facing changes? No <!-- If yes, please clarify the previous behavior and the change this PR proposes - provide the console output, description and/or an example to show the behavior difference if possible. If possible, please also clarify if this is a user-facing change compared to the released Delta Lake versions or within the unreleased branches such as master. If no, write 'No'. --> --------- Co-authored-by: Omar Elhadidy <omar.elhadidy@databricks.com>
…ed relative DVs in VACUUM (delta-io#7853) #### Which Delta project/connector is this regarding? - [x] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [ ] Other (fill in here) ## Description A few more places that identify deletion vectors (DVs) now support object identity, so that the same DV blob is recognized whether it is described by a table-relative (`r`) descriptor or a UUID-based (`u`) descriptor. 1. `DeletionVectorDescriptor.uniqueFileId` was a lazy val that always used the legacy `storageType + pathOrInlineDv` identity. It is replaced by `uniqueFileId(tableRoot, useObjectIdentity)`, which mirrors `uniqueId`: with object identity it returns the normalized table-relative identity of the DV file, without the offset. The legacy value is kept as the private `legacyUniqueFileId`, which `legacyUniqueId` is built on. 2. `getDeletionVectorRelativePathAndSize`, used by VACUUM, now treats unencoded relative (`r`) DVs like UUID-based (`u`) ones and returns their table-relative path. Before this change, `r` DVs fell through to the absolute-path branch, which asserts that the DV path is absolute. 3. Adds a `FileAction.useDeletionVectorObjectIdentity(snapshot, spark)` overload and uses it in the OPTIMIZE check that compares candidate file sets. ## How was this patch tested? Existing tests. `DeletionVectorDescriptorSuite` and `AMTCheckpointTestBase` are updated for the new `uniqueFileId` signature. ## Does this PR introduce _any_ user-facing changes? No.
…delta-io#7850) #### Which Delta project/connector is this regarding? - [ ] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [x] V4 RFC ## Description Align the adaptive metadata tree RFC's manifest deletion vector encoding to use the Mumbling bitmap spec in place of Roaring. ## How was this patch tested? NA ## Does this PR introduce _any_ user-facing changes? No
…ormat (delta-io#7855) The AMT manifest deletion vectors (the masking MDV in `manifest_info.dv` and the CDF `tracking.deleted_positions` / `replaced_positions`) were backed by a Roaring bitmap. This switches `ManifestBitmap` to an implementation backed by the immutable Mumbling bitmap format: `MumblingManifestBitmap` wraps a `MumblingBitmap`, and its mutable counterpart `MutableMumblingManifestBitmap` wraps a `MutableMumblingBitmap`, so `ManifestBitmap.fromPositions` / `fromSerializedByteArray` now produce and consume the Mumbling on-disk bytes. It also adds `MumblingRowIndexFilter` and `MumblingRowIndexFilterProvider`: a `RowIndexFilter` that drops a leaf row when its row index is contained in the MDV and keeps it otherwise, probing the bitmap per row (leaf row indices are dense and small, matching the point-membership shape of the Mumbling bitmap). The provider is self-contained and serializable so a scan task owns its own filter. The test-only position-dump helper (`toArrayForTesting`) is removed; the manifest-bitmap and AMT incremental-write tests now assert through `cardinality` / `contains` and exercise all three `RowIndexFilter` materialization entry points.
#### Which Delta project/connector is this regarding? - [x] Spark ## Description Convert `DeltaV2Table` from Java to Scala. Preserve its Java-facing collection and optional interfaces, synchronized schema initialization, and `KernelContext`-backed engine lookup. Constants and static helpers move to a Scala companion. Existing tests remain unchanged. Compatibility notes: - Scala `protected` methods are JVM-public. Java subclasses that override `protocol()`, `metadata()`, `kernelEngine()`, `initialSnapshot()`, or `catalogTable()` with protected visibility must widen their overrides to public when recompiling. - Helper visibility and reflection metadata differ from the Java implementation, including the public static forwarder for `getDecodedPath`. - Scala introduces some tuple and callback allocations; performance impact is unmeasured. ## How was this patch tested? Existing tests are unchanged. Local tests have not been run in this checkout; CI validation is pending. ## Does this PR introduce _any_ user-facing changes? No intended SQL behavior change. Java subclass and reflection compatibility differences are listed above. Signed-off-by: Hao Sun <86338940+HaoSun1993@users.noreply.github.com>
…rop dv_cardinality (delta-io#7816) <!-- Thanks for sending a pull request! Here are some tips for you: 1. If this is your first time, please read our contributor guidelines: https://github.com/delta-io/delta/blob/master/CONTRIBUTING.md 2. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP] Your PR title ...'. 3. Be sure to keep the PR description updated to reflect all changes. 4. Please write your PR title to summarize what this PR proposes. 5. If possible, provide a concise example to reproduce the issue for a faster review. 6. If applicable, include the corresponding issue number in the PR title and link it in the body. --> #### Which Delta project/connector is this regarding? <!-- Please add the component selected below to the beginning of the pull request title For example: [Spark] Title of my pull request --> - [x] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [ ] Other (fill in here) ## Description Renumber the AMT V4 manifest_info file/row count field IDs to match the spec PR: replaced_files_count 520 -> 523 modified_files_count 524 -> 525 replaced_rows_count 521 -> 524 modified_rows_count 525 -> 526 Separate DV cardinality Field id 523 is dropped now, readers have to read the cardinality from the bitmap itself. This field ID is now used for replaced_files_count. dv/dv_cardinality consistency invariant is now moot since there's only a single place so that logic is all dropped. format_version moves from top level field 157 into manifest_info as field 521, matching the spec PR. ## How was this patch tested? Existing unit tests, dropped no longer relevant assertions/tests for the fields that were removed. ## Does this PR introduce _any_ user-facing changes? <!-- If yes, please clarify the previous behavior and the change this PR proposes - provide the console output, description and/or an example to show the behavior difference if possible. If possible, please also clarify if this is a user-facing change compared to the released Delta Lake versions or within the unreleased branches such as master. If no, write 'No'. --> --------- Co-authored-by: Isaac <no-reply@databricks.com>
## Description Use `snapshotAtSourceInit` to provide table tags for four existing streaming source events: the unsafe column-mapping read warning, missing trailing commits, schema changes, and the `ignoreFileDeletion` deprecation warning. This keeps event attribution tied to the snapshot the source was initialized to read instead of the latest snapshot cached by `DeltaLog`. Event names, payloads, exception handling, and streaming behavior are unchanged. No additional snapshot fetches are introduced. ## Why this is safe `recordDeltaEvent` uses its provider only for table-context tags. Snapshots delegate to the same `DeltaLog.getCommonTags` helper; only the source of the table identifier tag changes. This affects attribution, not logging mechanics or streaming decisions. The table identifier is the table's `metadata.id`, not its version. Ordinary data writes and schema updates preserve it. Table replacement or drop-and-recreation can create a new table identity and change the ID, even at the same path. - **Source construction:** `snapshotAtSourceInit` is the snapshot used to construct the source. Initialization and deprecation warnings should identify that source, regardless of later table changes. - **Commit-processing diagnostics:** events describe the existing source and should carry its pinned `tableId`, obtained from `snapshotAtSourceInit.metadata.id`. When commit metadata validation runs, an ID mismatch fails before that commit's files are processed; schema-change logging also follows the ID check. Existing ID and schema-tracking checks remain unchanged. ## Testing - `git diff --check` - Verified that `SnapshotDescriptor` implements `DeltaLoggingProvider`, the provider type accepted by `recordDeltaEvent`. - Builds and tests have not been run locally.
#### Which Delta project/connector is this regarding? - [x] Spark ## Description Decode CDC actions from Kernel commit batches into `AddCDCFile` actions, preserving the path, partition values, size, and optional tags. These actions previously caused an `UnsupportedOperationException` during commit-batch conversion. Rename the shared string-map converter to reflect its use for both partition values and tags. ## How was this patch tested? Added coverage in `KernelActionUtilsSuite` for two CDC cases in one test method: an action without tags and an action with all supported fields populated. Local tests have not been run in this checkout; CI validation is pending. ## Does this PR introduce _any_ user-facing changes? Kernel commit-batch conversion now supports CDC actions. Signed-off-by: Hao Sun <86338940+HaoSun1993@users.noreply.github.com>
…elta-io#7848) ## Description Update CDF schema change check to reject partition column changes, which can otherwise result in `null` values in older versions for columns upgraded to a partition column. This also adds logging for incompatible schema changes encountered during CDF reads. ## How was this patch tested? Added tests in `CDCReaderSuite` (`CDCSchemaChangeTests`) that cover: - A partition column change, with the check disabled (the usage event is logged and the read succeeds) and enabled (the read fails). - An intermediate partition change. - A partition change followed by `RESTORE`. - Additional test coverage for other schema changes: column dropped, renamed, or changes type after the start version. Co-authored-by: Isaac <no-reply@databricks.com>
…ta-io#7860) #### Which Delta project/connector is this regarding? - [x] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [ ] Other ## Description Converts `TableNotFoundException` to Scala as part of the V2 connector migration. Preserves its constructor, getter, error message, subclassability, and field initialization order. The path field initializes after the superclass constructor so a getter called from an overridden `fillInStackTrace()` sees the same default value as before. ## How was this patch tested? No new test cases. Validation in this repository is pending; existing tests will exercise the converted class through its existing callers. ## Does this PR introduce any user-facing changes? No. Signed-off-by: Rakesh Veeramacheneni <rakesh.veeramacheneni@databricks.com>
…or classes (delta-io#7618) <!-- Thanks for sending a pull request! Here are some tips for you: 1. If this is your first time, please read our contributor guidelines: https://github.com/delta-io/delta/blob/master/CONTRIBUTING.md 2. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP] Your PR title ...'. 3. Be sure to keep the PR description updated to reflect all changes. 4. Please write your PR title to summarize what this PR proposes. 5. If possible, provide a concise example to reproduce the issue for a faster review. 6. If applicable, include the corresponding issue number in the PR title and link it in the body. --> #### Which Delta project/connector is this regarding? <!-- Please add the component selected below to the beginning of the pull request title For example: [Spark] Title of my pull request --> - [x] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [ ] Other (fill in here) ## Description This PR is the first out of a stack of seven PRs in total. The combined stack adds support for the Concurrent Identity Column (CIC) feature following the RFC [here](delta-io#7272). The TLDR for this RFC/feature: It allows for two concurrent 'inserters' to both succeed by adding an identity sequence service that hands out disjoint ranges of id values. This way the two writers do not conflict and can both commit. **_Stack overview:_** 1. Foundation <- this PR (delta-io#7618) 2. Service abstraction + In-Memory one (delta-io#7619) 3. Transport and buffering for the service requests (delta-io#7620) 4. Table feature and the insert path (delta-io#7621) 5. DDL integration (delta-io#7622) 6. Merge support (delta-io#7623) 7. UC Client addition (delta-io#7785) This PR introduces no behaviour changes, just scaffolding. It adds: - `ConcurrentIdentityColumnSchema`: the `delta.identity.concurrent.sequenceId` column-metadata key that marks a column as a CIC. - `ConcurrentIdentityColumnErrors` + 8 DELTA_CONCURRENT_IDENTITY_COLUMN_* error classes (disabled, too-many-columns, sequence-not-found, metadata-mismatch, empty-reserve-range, service-timeout, conversion-incomplete, using-wrong-generator). - Config `spark.databricks.delta.identityColumn.concurrent.maxColumnsPerTable` ## How was this patch tested? <!-- If tests were added, say they were added here. Please make sure to test the changes thoroughly including negative and positive cases if possible. If the changes were tested in any way other than unit tests, please clarify how you tested step by step (ideally copy and paste-able, so that other reviewers can test and check, and descendants can verify in the future). If the changes were not tested, please explain why. --> The ConcurrentIdentityColumnErrorsSuite adds three tests for the `EMPTY_RESERVE_RANGE`, `METADATA_MISMATCH`, and `USING_WRONG_GENERATOR` errors. The tests are only added for these errors because these errors can not occur 'naturally' in the code and are therefore tested this way. ## Does this PR introduce _any_ user-facing changes? <!-- If yes, please clarify the previous behavior and the change this PR proposes - provide the console output, description and/or an example to show the behavior difference if possible. If possible, please also clarify if this is a user-facing change compared to the released Delta Lake versions or within the unreleased branches such as master. If no, write 'No'. --> No --------- Signed-off-by: Robert Weeke <weeke.robert@gmail.com> Co-authored-by: mkroll-db <marco.kroll@databricks.com>
…taSQLCommandTest (delta-io#7863) #### Which Delta project/connector is this regarding? - [x] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [ ] Other (fill in here) ## Description In `DeleteSuiteBase`, replaces the `SharedSparkSession` test util with `DeltaSQLCommandTest` as the latter is the 'delta wrapper' of the former, to slightly decouple from Spark OSS. Also replaces `withSQLConf` with the more generic `withConf` to set confs via public APIs. ## How was this patch tested? test-only change. ## Does this PR introduce _any_ user-facing changes? No Co-authored-by: Matthis Gördel <matthis.goerdel@databricks.com>
…ta-io#7854) <!-- Thanks for sending a pull request! Here are some tips for you: 1. If this is your first time, please read our contributor guidelines: https://github.com/delta-io/delta/blob/master/CONTRIBUTING.md 2. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP] Your PR title ...'. 3. Be sure to keep the PR description updated to reflect all changes. 4. Please write your PR title to summarize what this PR proposes. 5. If possible, provide a concise example to reproduce the issue for a faster review. 6. If applicable, include the corresponding issue number in the PR title and link it in the body. --> #### Which Delta project/connector is this regarding? <!-- Please add the component selected below to the beginning of the pull request title For example: [Spark] Title of my pull request --> - [x] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [ ] Other (fill in here) ## Description <!-- - Describe what this PR changes. - Describe why we need the change. If this PR resolves an issue be sure to include "Resolves #XXX" to correctly link and close the issue upon merge. --> Problem: Widening file statistics through the shared AddFile.withoutTightBoundStats helper can round DECIMAL bounds inward, causing data skipping to discard matching rows. The SQL below demonstrates this through DELETE rebasing over a concurrent OPTIMIZE; the fix also affects other callers of the shared helper. ``` CREATE TABLE t (id BIGINT, amount DECIMAL(18, 2)) USING DELTA TBLPROPERTIES ('delta.enableDeletionVectors' = 'true', 'delta.autoOptimize.autoCompact' = 'false'); INSERT INTO t SELECT /*+ COALESCE(1) */ * FROM VALUES (1L, 0.64), (2L, 3647609576981584.64), (3L, 1.00), (4L, 2.00), (5L, -3647609576981584.64) AS test_data(id, amount); DELETE FROM t WHERE id = 1; INSERT INTO t VALUES (6L, 3.00); -- Session 1: the test harness pauses this DELETE before commit. DELETE FROM t WHERE id = 3; -- Session 2: OPTIMIZE commits while Session 1 is paused. OPTIMIZE t; -- Resume Session 1 and let DELETE commit before running these queries. -- With the fix disabled and data skipping enabled, both incorrectly return no rows. SELECT id, amount FROM t WHERE amount = -3647609576981584.64; -- Expected: id=5 SELECT id, amount FROM t WHERE amount = 3647609576981584.64; -- Expected: id=2 ``` Root cause: AddFile.withoutTightBoundStats parses fractional numbers as Double in its JSON-tree rewrite while setting tightBounds=false. In the example, DV conflict rebasing reaches this helper through removeRows(updateStats=true). The amount bounds round inward from [-3647609576981584.64, 3647609576981584.64] to [-3647609576981584.50, 3647609576981584.50]. Both surviving endpoints then fall outside the recorded bounds, so data skipping discards their file. The precision loss is in the shared helper and can affect any caller that rewrites numeric bounds. Fix: Reuse Jackson's `copyCurrentEventExact` in `withoutTightBoundStats` to preserve numeric precision while replacing tightBounds. ## How was this patch tested? <!-- If tests were added, say they were added here. Please make sure to test the changes thoroughly including negative and positive cases if possible. If the changes were tested in any way other than unit tests, please clarify how you tested step by step (ideally copy and paste-able, so that other reviewers can test and check, and descendants can verify in the future). If the changes were not tested, please explain why. --> UT ## Does this PR introduce _any_ user-facing changes? <!-- If yes, please clarify the previous behavior and the change this PR proposes - provide the console output, description and/or an example to show the behavior difference if possible. If possible, please also clarify if this is a user-facing change compared to the released Delta Lake versions or within the unreleased branches such as master. If no, write 'No'. --> No Signed-off-by: zhipeng.mao <zhipeng.mao@databricks.com>
…LCommandTest (delta-io#7864) #### Which Delta project/connector is this regarding? - [x] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [ ] Other (fill in here) ## Description Replaces `SharedSparkSession` with `DeltaSQLCommandTest`, which is a 'delta wrapper' of the former. Also replaces `withSQLConf` with the more generic `withConf`. ## How was this patch tested? test-only change. ## Does this PR introduce _any_ user-facing changes? No Co-authored-by: Matthis Gördel <matthis.goerdel@databricks.com>
…ta-io#7865) #### Which Delta project/connector is this regarding? - [x] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [ ] Other (fill in here) ## Description `delta.inCommitTimestampEnablementVersion` and `delta.inCommitTimestampEnablementTimestamp` record the version and timestamp at which In-Commit Timestamps (ICT) were enabled on a table. They are written by the commit that enables ICT and describe that table's log. A user-specified value, for example one copied from another table, can point to a version that is not in the table's log. That breaks later commits and ICT-based time travel. This PR: - Marks both properties as not user-configurable, so CREATE, REPLACE, CTAS, RTAS, ALTER TABLE SET TBLPROPERTIES, DataFrameWriter options, session defaults and the `DeltaTable` builder API fail with `DELTA_CANNOT_MODIFY_TABLE_PROPERTY`. - Stops CREATE TABLE LIKE and CLONE from copying them from the source table. The target still gets `delta.enableInCommitTimestamps` from the source, and the commit derives the target's own enablement info when needed. - Adds `InCommitTimestampUtils.ENABLEMENT_INFO_PROPERTY_KEYS` so the filtering and the tests share one list of keys. ## How was this patch tested? New tests in `InCommitTimestampSuite`: - For each property, setting it fails through CREATE/REPLACE/CTAS/RTAS TBLPROPERTIES, ALTER TABLE SET TBLPROPERTIES, DataFrameWriter options, session defaults and the `DeltaTable` builder API. - CREATE TABLE LIKE from a table whose ICT enablement info is set produces a target that has ICT enabled but no copied enablement info. ## Does this PR introduce _any_ user-facing changes? Yes. Setting `delta.inCommitTimestampEnablementVersion` or `delta.inCommitTimestampEnablementTimestamp` now fails with `DELTA_CANNOT_MODIFY_TABLE_PROPERTY`; before, the value was accepted. CREATE TABLE LIKE and CLONE no longer copy these properties from the source table. Tables that already have these properties are unaffected. This pull request and its description were written by Isaac. --------- Co-authored-by: Murali Ramanujam <murali.ramanujam@databricks.com>
… DeltaV2Table (delta-io#7866) <!-- Thanks for sending a pull request! Here are some tips for you: 1. If this is your first time, please read our contributor guidelines: https://github.com/delta-io/delta/blob/master/CONTRIBUTING.md 2. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP] Your PR title ...'. 3. Be sure to keep the PR description updated to reflect all changes. 4. Please write your PR title to summarize what this PR proposes. 5. If possible, provide a concise example to reproduce the issue for a faster review. 6. If applicable, include the corresponding issue number in the PR title and link it in the body. --> #### Which Delta project/connector is this regarding? <!-- Please add the component selected below to the beginning of the pull request title For example: [Spark] Title of my pull request --> - [x] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [ ] Other (fill in here) ## Description DeltaV2Table` built its identity partition transforms with `Expressions.identity(String)`, which re-parses each partition column name as a SQL multipart identifier. Partition column names are exact top-level field names, so this goes wrong for names that need quoting: - A dotted name such as `customer.id` silently becomes the two-part reference `[customer, id]`. Consumers that expect one name part per identity partition column (for example `DataSourceV2Relation.partitionColumns`, which takes `fieldNames().head`) see the wrong column. - Names with spaces or backticks, which column mapping allows (for example `order date`), throw a `ParseException` as soon as `partitioning()` is called. This PR builds each transform from `FieldReference.column(name)`, a single exact name part. Partition-column order, case, and column-mapping logical names are unchanged. <!-- - Describe what this PR changes. - Describe why we need the change. If this PR resolves an issue be sure to include "Resolves #XXX" to correctly link and close the issue upon merge. --> ## How was this patch tested? CI and new tests <!-- If tests were added, say they were added here. Please make sure to test the changes thoroughly including negative and positive cases if possible. If the changes were tested in any way other than unit tests, please clarify how you tested step by step (ideally copy and paste-able, so that other reviewers can test and check, and descendants can verify in the future). If the changes were not tested, please explain why. --> ## Does this PR introduce _any_ user-facing changes? <!-- If yes, please clarify the previous behavior and the change this PR proposes - provide the console output, description and/or an example to show the behavior difference if possible. If possible, please also clarify if this is a user-facing change compared to the released Delta Lake versions or within the unreleased branches such as master. If no, write 'No'. --> No
#### Which Delta project/connector is this regarding? - [x] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [ ] Other (fill in here) ## Description Adds `DeleteAMT` and `RowTrackingDeleteAMT` dimensions to the delta-suite-generator, plus a generated `deleteamt` group that runs the DELETE tests against AMT (`adaptiveMetadata-preview`) tables, following the UPDATE AMT suites. - `DeleteAMTMixin` brackets each `executeDelete` with the `AMTDMLTestUtils` checkpoints. `RowTrackingDeleteAMTMixin` brackets each test table's body instead, since its row tracking checks inspect the latest commit. - AMT requires deletion vectors, so `AMTDMLTestUtils` now unsets a suite's `delta.enableDeletionVectors = false` default for new tables for all AMT DML suites (moved from the UPDATE AMT base), and keeps DELETE, UPDATE and MERGE from writing deletion vectors where the tests assert file rewrites. - The DELETE schema pruning tests only collect scans over a `TahoeFileIndex`, so they skip the checkpoint's tree read. - Excludes the tests the row tracking and deletion vector DELETE suites already exclude, and the row tracking tests that create tables with row tracking disabled, which AMT rejects. `RowTrackingDeleteDvBase` is not generated, since its only test disables deletion vectors on new tables. ## How was this patch tested? Test-only change. The new generated `deleteamt` suites run the existing DELETE tests on AMT tables. ## Does this PR introduce _any_ user-facing changes? No.
…a-io#7870) <!-- Thanks for sending a pull request! Here are some tips for you: 1. If this is your first time, please read our contributor guidelines: https://github.com/delta-io/delta/blob/master/CONTRIBUTING.md 2. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP] Your PR title ...'. 3. Be sure to keep the PR description updated to reflect all changes. 4. Please write your PR title to summarize what this PR proposes. 5. If possible, provide a concise example to reproduce the issue for a faster review. 6. If applicable, include the corresponding issue number in the PR title and link it in the body. --> #### Which Delta project/connector is this regarding? <!-- Please add the component selected below to the beginning of the pull request title For example: [Spark] Title of my pull request --> - [x] Spark - [ ] Standalone - [ ] Flink - [ ] Kernel - [ ] Other (fill in here) ## Description The V1 read path uses a LIMIT to prune files only when DeltaSQLConf.DELTA_LIMIT_PUSHDOWN_ENABLED is on, but DeltaV2ScanBuilder.pushLimit always accepted the limit, so the V2 connector ignored the config. pushLimit now declines the limit and records nothing when the config is off, so Spark applies its own LIMIT to an unlimited scan. Nothing changes when the config is on, which is the default. <!-- - Describe what this PR changes. - Describe why we need the change. If this PR resolves an issue be sure to include "Resolves #XXX" to correctly link and close the issue upon merge. --> ## How was this patch tested? Added new tests <!-- If tests were added, say they were added here. Please make sure to test the changes thoroughly including negative and positive cases if possible. If the changes were tested in any way other than unit tests, please clarify how you tested step by step (ideally copy and paste-able, so that other reviewers can test and check, and descendants can verify in the future). If the changes were not tested, please explain why. --> ## Does this PR introduce _any_ user-facing changes? <!-- If yes, please clarify the previous behavior and the change this PR proposes - provide the console output, description and/or an example to show the behavior difference if possible. If possible, please also clarify if this is a user-facing change compared to the released Delta Lake versions or within the unreleased branches such as master. If no, write 'No'. --> No
Reconciling a shared file hides the winner's deletes in it from the delete-read check. Require that none of the rows the winner newly deleted match the current transaction's read predicates (one signed-weight scan of the base-DV and winner-DV images), refuse whole-table reads, and accept a MERGE without a scan only when it has no NOT MATCHED clause or has an unconditional MATCHED clause. Previously a MERGE with a conditional matched clause and an insert could commit after a winner deleted a row it matched but left alone, skipping the insert that row's deletion requires. Merged DV files are now written only once the whole resolution is known to succeed, and the deletionVectorsMerged event is recorded after all checks against the winning commit pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cy-dv-poc Adapt to the onDiskWithRelativePath -> onDiskWithUuidRelativePath rename (delta-io#7473).
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.
Part of the row-level-concurrency umbrella (#3) — Case 2. Full design: #5. Addresses delta-io#7057.
Summary
POC implementing deletion-vector-based row-level concurrency for OSS Delta (Spark). Two concurrent DML operations that modify the same physical file but touch disjoint rows no longer abort the loser. A new config-gated phase in
ConflictCheckerdecodes both transactions' deletion vectors, and when the newly-deleted rows are disjoint, merges them (dv_win ∪ dv_cur), writes a new DV file, and rebases the losing transaction onto the winner's post-image.This is the sound same-file DV union. It is deliberately narrow: it reconciles conflicts that are provable from DV positions alone, and falls back to today's abort for everything else — including a rewrite-only DML winner's new image files, which are handed to conflict-time data skipping (Case 1, #4 / #8). See Correctness: UPDATE image files below.
What it does
resolveRowLevelConflicts()inConflictChecker, run before the file-level checks.dv_win,dv_cur, and their common basedv_base(from the current txn's pre-imageRemoveFile).(dv_win ∩ dv_cur) \ dv_baseempty → merge the DVs, persist a new DV file, and rebase the currentAddFile(P)onto the winner's post-image (itsRemoveFile(P)now tombstones the winner'sAddFile(P)). Non-empty → genuine same-row conflict, aborts as before.rowLevelResolvedPaths) — re-checking the winner's re-addedAddFile(P)(whose DV we already folded in) would be a false conflict.reassignOverlappingRowIdsphase (already run) keeps IDs consistent since the merged file is the same physical file with an unchanged base row ID.spark.databricks.delta.rowLevelConcurrency.enabled(internal, default off). No protocol / action / reader changes — the output is a standard DV action.Correctness: UPDATE image files (deferred to Case 1 / #4)
An
UPDATEproduces two things per touched file: the same-path DV pair (handled here, soundly) and a new image file at a fresh path carrying the updated row values. This PR does not skip those image files in the append check. That is intentional:An
UPDATEcan move a row into the loser's predicate — winnerUPDATE SET x = 15 WHERE id = r, loserDELETE WHERE x > 10, whererstarted atx = 5. Under a winner→loser serial order the loser would deleter, so this is a genuine write-skew conflict. The DV union cannot detect it (ris a different row from the ones the loser touched), so any op-agnostic "suppress a rewrite-only DML winner's added files" shortcut would be unsound — it would let the loser commit withrsurviving atx = 15, unmatched by its own predicate, strictly weaker than base WriteSerializable.Instead, image files remain ordinary non-blind changed-data files and flow into the standard added-files check. On an unpartitioned table that conservatively conflicts (one-way safe: falls back to today's abort). Reconciling the provably-disjoint case — the loser's read predicate vs the image file's stats — is exactly conflict-time data skipping, owned by Case 1 (#4 / #8). Case 2's UPDATE image-file reconcile therefore depends on #8 being in the build; standalone, Case 2 is DV-union only.
Scope
DELETE-vs-DELETE(fully reconciled), and the same-file DV union forUPDATE/DELETEmixes.UPDATEimage files → conservative abort here; provably-disjoint reconcile owned by Case 1 ([RLC] Case 1 — conflict-time data skipping (disjoint data ranges) #4 / [RLC] Case 1 — conflict-time data skipping (disjoint data ranges) #8).MERGEwith net-new inserts still conflicts by design (umbrella backlog).OPTIMIZE-compaction-vs-DML needs a cross-file offset remap → Case 4 ([RLC] Case 4 — OPTIMIZE ⟂ DML reconciliation (compaction offset remap) #7 / [RLC] Case 4a — OPTIMIZE loses to DML (forward compaction offset remap) #9 · [RLC] Case 4b — DML loses to OPTIMIZE (reverse compaction offset remap) #11), which reuses this PR's DV helpers.Testing
RowLevelConcurrencySuite(new), using the phase-locking concurrency harness:DELETE(loser) vsUPDATE(winner) → winner's image file conservatively conflicts, loser aborts cleanly (one-way safe; Case 1 refines this to reconcile the disjoint case); overlappingUPDATE/UPDATEon the same row → still conflict..andNot(base)), 3-way overlap → abort, explicit Serializable, row-tracking_metadata.row_idstability, CDF one-delete-per-version.Data correctness is asserted end-to-end (final row set) plus merged-DV cardinalities.
🤖 Generated with Claude Code