hcrosse commented on code in PR #2100:
URL: https://github.com/apache/iceberg-go/pull/2100#discussion_r4238658346


##########
table/table.go:
##########
@@ -690,11 +704,9 @@ func (t Table) doCommit(ctx context.Context, updates 
[]Update, reqs []Requiremen
                        current = fresh.metadata
                        reqs = rewriteRefSnapshotRequirements(reqs, co.branch, 
current, co.pinnedRefs)
 
-                       // A pinned assertion the fresh catalog state violates 
can
-                       // never succeed — fail now instead of burning the 
remaining
-                       // retries on it.
-                       if err := validatePinnedRefRequirements(reqs, 
co.pinnedRefs, current); err != nil {
-                               return nil, fmt.Errorf("%w: explicit ref 
requirement failed: %w", ErrCommitFailed, err)
+                       if err := validateNonRebasedRequirements(reqs, 
co.branch, co.pinnedRefs, current); err != nil {
+                               return nil, fmt.Errorf("%w: requirement no 
longer holds after refresh: %w: %w",

Review Comment:
   Good catch. It now starts with "transaction cannot be committed again" and 
still matches ErrCommitFailed. I also dropped the second wrap in the defer.



##########
table/transaction.go:
##########
@@ -3361,13 +3380,17 @@ func (t *Transaction) Commit(ctx context.Context) 
(*Table, error) {
                        withCommitPinnedRefs(t.pinnedRefs),
                )
                if err != nil {
-                       // A clean conflict (ErrCommitFailed) committed nothing 
and stays
-                       // retriable. Any other failure leaves the commit state 
unknown
-                       // (the catalog may have accepted it), so mark it 
terminal to
-                       // avoid a double-apply on retry.
+                       // A clean conflict (ErrCommitFailed) stays retriable 
unless the error
+                       // also matches ErrTransactionUnusable: cleanup removed 
files the staged
+                       // updates reference, or a requirement failed after a 
refresh. Any other
+                       // failure leaves the commit state unknown, so mark it 
terminal to avoid
+                       // a double-apply.
                        if !errors.Is(err, ErrCommitFailed) {
                                t.committed = true
                        }
+                       if errors.Is(err, ErrTransactionUnusable) {

Review Comment:
   With num-retries=0 a re-commit resends the same stale requirements, so I 
made any conflict there terminal. Non-replayable commits too. With retries on, 
the next Commit refreshes once and hits the post-refresh check. Since the 
default is 0 this changes default behavior, so I wrote up the tradeoff and a 
narrower option in the [Retries disabled section of the 
description](https://github.com/apache/iceberg-go/pull/2100#issue-5672245891). 
Let me know which you'd prefer.



##########
table/table.go:
##########
@@ -630,15 +632,27 @@ func (t Table) doCommit(ctx context.Context, updates 
[]Update, reqs []Requiremen
        // CommitTable, where the catalog may have silently accepted the commit 
and one
        // of the "orphaned" files may actually be the live snapshot.
        cleanupOrphans := true
+       committed := false
        defer func() {
-               if !cleanupOrphans || len(orphanedManifests) == 0 {
+               if !cleanupOrphans {
                        return
                }
+               // The final attempt's manifests are orphaned only if the 
commit failed.
+               for _, u := range updates {
+                       if su, ok := u.(*addSnapshotUpdate); ok && 
su.supersededSource != nil {
+                               orphanedManifests = append(orphanedManifests, 
su.supersededSource.supersededManifests(committed)...)
+                       }
+               }
                for _, path := range orphanedManifests {
                        if removeErr := wfs.Remove(path); removeErr != nil {
                                log.Printf("Warning: failed to delete orphaned 
manifest list %s: %v", path, removeErr)
                        }
                }
+               // Resubmitting staged updates after cleanup would reference 
deleted files.
+               if !committed && retErr != nil && len(orphanedManifests) > 0 {

Review Comment:
   Yes, that's intended. I spelled it out in the Commit comment and 
concurrent-writes.md. The wrap now keys on a flag set where files are removed.



##########
table/table.go:
##########
@@ -50,14 +50,18 @@ import (
 // commit fails due to a concurrent modification (e.g. HTTP 409 Conflict
 // from the REST catalog). Catalog implementations should wrap this
 // error so that callers using errors.Is(err, table.ErrCommitFailed)
-// can detect retryable commit conflicts.
-//
-// Currently only catalog/rest wraps this sentinel; Glue, SQL, and Hive
-// catalogs return their conflict errors raw and will not trigger
-// retries until follow-up work wires them through (tracked under
-// issue #830).
+// can detect retryable commit conflicts. Failed requirements also wrap it.

Review Comment:
   Done, I reworded the opener.



-- 
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