This is an automated email from the ASF dual-hosted git repository.

zeroshade pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new a950faae refactor(table): make PostCommit optional in ExpireSnapshots 
(#677)
a950faae is described below

commit a950faaecd7861904d7f0d6f3fb643d79cd85a1a
Author: Krutika Dhananjay <[email protected]>
AuthorDate: Sat Jan 17 05:45:48 2026 +0530

    refactor(table): make PostCommit optional in ExpireSnapshots (#677)
    
    Allow callers to control whether PostCommit (file deletion) runs after
    expiring snapshots. PostCommit is enabled by default to preserve
    existing behavior. Use WithPostCommit(false) to skip file cleanup when
    metadata-only updates are desired.
    
    ---------
    
    Co-authored-by: Krutika Dhananjay <[email protected]>
---
 table/transaction.go  | 17 +++++++++++++++--
 table/updates.go      |  5 +++++
 table/updates_test.go | 11 +++++++++++
 3 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/table/transaction.go b/table/transaction.go
index b2eccbb6..68961bf3 100644
--- a/table/transaction.go
+++ b/table/transaction.go
@@ -167,6 +167,7 @@ func (t *Transaction) UpdateSchema(caseSensitive bool, 
allowIncompatibleChanges
 type expireSnapshotsCfg struct {
        minSnapshotsToKeep *int
        maxSnapshotAgeMs   *int64
+       postCommit         bool
 }
 
 type ExpireSnapshotsOpt func(*expireSnapshotsCfg)
@@ -184,9 +185,19 @@ func WithOlderThan(t time.Duration) ExpireSnapshotsOpt {
        }
 }
 
+// WithPostCommit controls whether orphaned files (manifests, manifest lists,
+// data files) are deleted immediately after expiring snapshots. Defaults to 
true.
+// Set to false to defer file deletion to a separate maintenance job, avoiding
+// conflicts with in-flight queries that may still reference those files.
+func WithPostCommit(postCommit bool) ExpireSnapshotsOpt {
+       return func(cfg *expireSnapshotsCfg) {
+               cfg.postCommit = postCommit
+       }
+}
+
 func (t *Transaction) ExpireSnapshots(opts ...ExpireSnapshotsOpt) error {
        var (
-               cfg         expireSnapshotsCfg
+               cfg         = expireSnapshotsCfg{postCommit: true}
                updates     []Update
                reqs        []Requirement
                snapsToKeep = make(map[int64]struct{})
@@ -275,7 +286,9 @@ func (t *Transaction) ExpireSnapshots(opts 
...ExpireSnapshotsOpt) error {
                }
        }
 
-       updates = append(updates, NewRemoveSnapshotsUpdate(snapsToDelete))
+       update := NewRemoveSnapshotsUpdate(snapsToDelete)
+       update.postCommit = cfg.postCommit
+       updates = append(updates, update)
 
        return t.apply(updates, reqs)
 }
diff --git a/table/updates.go b/table/updates.go
index db31da01..ad302a29 100644
--- a/table/updates.go
+++ b/table/updates.go
@@ -412,6 +412,7 @@ func (u *removePropertiesUpdate) Apply(builder 
*MetadataBuilder) error {
 type removeSnapshotsUpdate struct {
        baseUpdate
        SnapshotIDs []int64 `json:"snapshot-ids"`
+       postCommit  bool
 }
 
 // NewRemoveSnapshotsUpdate creates a new update that removes all snapshots 
from
@@ -428,6 +429,10 @@ func (u *removeSnapshotsUpdate) Apply(builder 
*MetadataBuilder) error {
 }
 
 func (u *removeSnapshotsUpdate) PostCommit(ctx context.Context, preTable 
*Table, postTable *Table) error {
+       if !u.postCommit {
+               return nil
+       }
+
        prefs, err := preTable.FS(ctx)
        if err != nil {
                return err
diff --git a/table/updates_test.go b/table/updates_test.go
index 9c9af93d..831abb4b 100644
--- a/table/updates_test.go
+++ b/table/updates_test.go
@@ -18,6 +18,7 @@
 package table
 
 import (
+       "context"
        "encoding/json"
        "testing"
 
@@ -27,6 +28,16 @@ import (
        "github.com/stretchr/testify/require"
 )
 
+func TestRemoveSnapshotsPostCommitSkipped(t *testing.T) {
+       update := NewRemoveSnapshotsUpdate([]int64{1, 2, 3})
+       update.postCommit = false
+
+       // PostCommit should return nil immediately when postCommit is false,
+       // without accessing the table arguments (which are nil here)
+       err := update.PostCommit(context.Background(), nil, nil)
+       assert.NoError(t, err)
+}
+
 func TestUnmarshalUpdates(t *testing.T) {
        spec := iceberg.NewPartitionSpecID(3,
                iceberg.PartitionField{

Reply via email to