From 9ba1b9d5cc02a43ea1b29a24f79c732c2fedfd20 Mon Sep 17 00:00:00 2001 From: Xinli Shang Date: Sun, 21 Dec 2025 16:03:12 -0800 Subject: [PATCH 1/4] fix: prevent double-commit in Transaction Prevents Transaction::Commit() from being called multiple times by: - Adding committed_ flag to track transaction state - Checking flag at start of Commit() and returning error if already committed - Setting flag after successful commit (both empty and non-empty updates) - Updating table_ reference after successful catalog update This ensures transactions can only be committed once, preventing unintended side effects and maintaining transaction semantics. --- src/iceberg/transaction.cc | 13 ++++++++++++- src/iceberg/transaction.h | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/iceberg/transaction.cc b/src/iceberg/transaction.cc index ca39ec043..666d8fdd5 100644 --- a/src/iceberg/transaction.cc +++ b/src/iceberg/transaction.cc @@ -75,6 +75,9 @@ Status Transaction::Apply(std::vector> updates) { } Result> Transaction::Commit() { + if (committed_) { + return InvalidArgument("Transaction already committed"); + } if (!last_update_committed_) { return InvalidArgument( "Cannot commit transaction when previous update is not committed"); @@ -82,6 +85,7 @@ Result> Transaction::Commit() { const auto& updates = metadata_builder_->changes(); if (updates.empty()) { + committed_ = true; return table_; } @@ -98,7 +102,14 @@ Result> Transaction::Commit() { } // XXX: we should handle commit failure and retry here. - return table_->catalog()->UpdateTable(table_->name(), requirements, updates); + ICEBERG_ASSIGN_OR_RAISE(auto updated_table, table_->catalog()->UpdateTable( + table_->name(), requirements, updates)); + + // Mark as committed and update table reference + committed_ = true; + table_ = updated_table; + + return updated_table; } Result> Transaction::NewUpdateProperties() { diff --git a/src/iceberg/transaction.h b/src/iceberg/transaction.h index 36328026b..73c346833 100644 --- a/src/iceberg/transaction.h +++ b/src/iceberg/transaction.h @@ -80,6 +80,8 @@ class ICEBERG_EXPORT Transaction : public std::enable_shared_from_this> pending_updates_; From ead3c912118927024d8afd8c1155509185706fd2 Mon Sep 17 00:00:00 2001 From: Xinli Shang Date: Mon, 22 Dec 2025 06:47:02 -0800 Subject: [PATCH 2/4] Update src/iceberg/transaction.cc Co-authored-by: Gang Wu --- src/iceberg/transaction.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iceberg/transaction.cc b/src/iceberg/transaction.cc index 666d8fdd5..50064ea7d 100644 --- a/src/iceberg/transaction.cc +++ b/src/iceberg/transaction.cc @@ -107,7 +107,7 @@ Result> Transaction::Commit() { // Mark as committed and update table reference committed_ = true; - table_ = updated_table; + table_ = std::move(updated_table); return updated_table; } From 4bdeb7367447e0ca6cd24256f183fba45a1c59dd Mon Sep 17 00:00:00 2001 From: Xinli Shang Date: Mon, 22 Dec 2025 06:47:12 -0800 Subject: [PATCH 3/4] Update src/iceberg/transaction.cc Co-authored-by: Gang Wu --- src/iceberg/transaction.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iceberg/transaction.cc b/src/iceberg/transaction.cc index 50064ea7d..882a5895f 100644 --- a/src/iceberg/transaction.cc +++ b/src/iceberg/transaction.cc @@ -109,7 +109,7 @@ Result> Transaction::Commit() { committed_ = true; table_ = std::move(updated_table); - return updated_table; + return table_; } Result> Transaction::NewUpdateProperties() { From 9327954b4facfe75fe9a308a8823873c33dd0702 Mon Sep 17 00:00:00 2001 From: Xinli Shang Date: Mon, 22 Dec 2025 06:47:33 -0800 Subject: [PATCH 4/4] Update src/iceberg/transaction.cc Co-authored-by: Gang Wu --- src/iceberg/transaction.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iceberg/transaction.cc b/src/iceberg/transaction.cc index 882a5895f..c8cf42d36 100644 --- a/src/iceberg/transaction.cc +++ b/src/iceberg/transaction.cc @@ -76,7 +76,7 @@ Status Transaction::Apply(std::vector> updates) { Result> Transaction::Commit() { if (committed_) { - return InvalidArgument("Transaction already committed"); + return Invalid("Transaction already committed"); } if (!last_update_committed_) { return InvalidArgument(