zeroshade commented on code in PR #2054:
URL: https://github.com/apache/iceberg-go/pull/2054#discussion_r4108439983


##########
table/row_delta.go:
##########
@@ -379,6 +400,49 @@ func (rd *RowDelta) validateRemovedDeletes(resolved, 
replacedLive []iceberg.Data
        return nil
 }
 
+// findReplacedLiveDVs walks the current snapshot's delete manifests and
+// collects all live deletion vectors whose referenced data file this delta
+// adds a replacement for. This is used to enforce the v3 spec invariant
+// that at most one live DV can exist per data file: if any such live DVs
+// are found and they are not being explicitly removed, the commit must fail.
+func (rd *RowDelta) findReplacedLiveDVs(fs iceio.IO, meta *MetadataBuilder) 
([]iceberg.DataFile, error) {
+       snap := rd.txn.planningSnapshot(meta)
+       if snap == nil {
+               // No existing snapshot means no live DVs to worry about.
+               return nil, nil
+       }
+
+       addedRefs := make(map[string]struct{}, len(rd.delFiles))
+       for _, f := range rd.delFiles {

Review Comment:
   **Same-delta duplicate DVs are still accepted.** The existing 
`RemoveDeletes` contract says, “The v3 spec allows at most one live deletion 
vector per data file.” Here `addedRefs` collapses two new DVs referencing the 
same data file into one key. If the data file has no live DV yet, the scan 
returns no `replacedLive`; without `RemoveDeletes`, `validateRemovedDeletes` 
(which checks duplicate additions) is never called. `AddDeletes(dvA, dvB)` can 
therefore commit two live DVs and make scans reject the table. Please reject 
duplicate added references on the fast-append path and test that case.



##########
table/row_delta.go:
##########
@@ -234,6 +234,27 @@ func (rd *RowDelta) Commit(ctx context.Context) error {
 
        op := rd.Operation()
 
+       // Find live DVs that would be superseded by added DVs, regardless of
+       // whether removals are present. If any such live DVs exist and are not
+       // being removed, the commit must fail.
+       replacedLive, err := rd.findReplacedLiveDVs(fs, meta)

Review Comment:
   **The check does not cover concurrent first-DV additions.** Two transactions 
based on a snapshot with no DV for the same data file both pass this scan. 
After one commits, the other's no-removal transaction is replayable: `doCommit` 
rebuilds its manifest list against the refreshed parent but does not rerun this 
check, and `RowDelta.validate` checks the referenced data file rather than DV 
uniqueness. The retry can inherit the first DV and commit the second. Please 
check against the refreshed parent during retry, or prevent replay of these 
additions, and cover the two-writer case in a test. The existing 
`RemoveDeletes` contract requires at most one live DV per data file.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to