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{