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

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


The following commit(s) were added to refs/heads/main by this push:
     new 0222e8bb4d Flink: Backport: Fix DynamicIcebergSink branch creation 
race in TableUpdater (#18422)
0222e8bb4d is described below

commit 0222e8bb4d3b338c4366426356fe86a60a09c6de
Author: Anton Kozmirchuk <[email protected]>
AuthorDate: Thu Oct 8 16:19:48 2026 +0100

    Flink: Backport: Fix DynamicIcebergSink branch creation race in 
TableUpdater (#18422)
---
 .../iceberg/flink/sink/dynamic/TableUpdater.java   |  7 +++++--
 .../flink/sink/dynamic/TestTableUpdater.java       | 22 ++++++++++++++++++++++
 .../iceberg/flink/sink/dynamic/TableUpdater.java   |  7 +++++--
 .../flink/sink/dynamic/TestTableUpdater.java       | 22 ++++++++++++++++++++++
 4 files changed, 54 insertions(+), 4 deletions(-)

diff --git 
a/flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/sink/dynamic/TableUpdater.java
 
b/flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/sink/dynamic/TableUpdater.java
index 26eb34db3c..25efa744a7 100644
--- 
a/flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/sink/dynamic/TableUpdater.java
+++ 
b/flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/sink/dynamic/TableUpdater.java
@@ -107,8 +107,11 @@ class TableUpdater {
       try {
         table.manageSnapshots().createBranch(branch).commit();
         LOG.info("Branch {} for {} created", branch, identifier);
-      } catch (CommitFailedException e) {
-        table.refresh();
+      } catch (CommitFailedException | IllegalArgumentException e) {
+        if (!table.refs().containsKey(branch)) {
+          table.refresh();
+        }
+
         if (table.refs().containsKey(branch)) {
           LOG.debug("Branch {} concurrently created for {}.", branch, 
identifier);
         } else {
diff --git 
a/flink/v1.20/flink/src/test/java/org/apache/iceberg/flink/sink/dynamic/TestTableUpdater.java
 
b/flink/v1.20/flink/src/test/java/org/apache/iceberg/flink/sink/dynamic/TestTableUpdater.java
index 48c0ffda64..367250cf27 100644
--- 
a/flink/v1.20/flink/src/test/java/org/apache/iceberg/flink/sink/dynamic/TestTableUpdater.java
+++ 
b/flink/v1.20/flink/src/test/java/org/apache/iceberg/flink/sink/dynamic/TestTableUpdater.java
@@ -117,6 +117,28 @@ public class TestTableUpdater extends 
TestFlinkIcebergSinkBase {
     assertThat(result.f1).isEqualTo(PartitionSpec.unpartitioned());
   }
 
+  @Test
+  void testBranchConcurrentlyCreatedByAnotherWriter() {
+    Catalog catalog = CATALOG_EXTENSION.catalog();
+    TableIdentifier tableIdentifier = TableIdentifier.parse("myTable");
+    catalog.createTable(tableIdentifier, SCHEMA);
+
+    TableMetadataCache cache =
+        new TableMetadataCache(catalog, 10, Long.MAX_VALUE, 10, 
CASE_SENSITIVE, PRESERVE_COLUMNS);
+    TableUpdater tableUpdater = new TableUpdater(cache, catalog, 
CASE_SENSITIVE, PRESERVE_COLUMNS);
+
+    // This writer's cache while the branch genuinely does not exist yet.
+    assertThat(cache.branch(tableIdentifier, "myBranch")).isNull();
+
+    // Simulate another writer subtask committing the branch
+    
catalog.loadTable(tableIdentifier).manageSnapshots().createBranch("myBranch").commit();
+
+    // Must not throw: a branch concurrently created by another writer must be 
treated success
+    tableUpdater.update(
+        tableIdentifier, "myBranch", SCHEMA, PartitionSpec.unpartitioned(), 
TableCreator.DEFAULT);
+    assertThat(cache.branch(tableIdentifier, "myBranch")).isNotNull();
+  }
+
   @Test
   void testBranchCreationAndCaching() {
     Catalog catalog = CATALOG_EXTENSION.catalog();
diff --git 
a/flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/sink/dynamic/TableUpdater.java
 
b/flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/sink/dynamic/TableUpdater.java
index 26eb34db3c..25efa744a7 100644
--- 
a/flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/sink/dynamic/TableUpdater.java
+++ 
b/flink/v2.2/flink/src/main/java/org/apache/iceberg/flink/sink/dynamic/TableUpdater.java
@@ -107,8 +107,11 @@ class TableUpdater {
       try {
         table.manageSnapshots().createBranch(branch).commit();
         LOG.info("Branch {} for {} created", branch, identifier);
-      } catch (CommitFailedException e) {
-        table.refresh();
+      } catch (CommitFailedException | IllegalArgumentException e) {
+        if (!table.refs().containsKey(branch)) {
+          table.refresh();
+        }
+
         if (table.refs().containsKey(branch)) {
           LOG.debug("Branch {} concurrently created for {}.", branch, 
identifier);
         } else {
diff --git 
a/flink/v2.2/flink/src/test/java/org/apache/iceberg/flink/sink/dynamic/TestTableUpdater.java
 
b/flink/v2.2/flink/src/test/java/org/apache/iceberg/flink/sink/dynamic/TestTableUpdater.java
index 48c0ffda64..367250cf27 100644
--- 
a/flink/v2.2/flink/src/test/java/org/apache/iceberg/flink/sink/dynamic/TestTableUpdater.java
+++ 
b/flink/v2.2/flink/src/test/java/org/apache/iceberg/flink/sink/dynamic/TestTableUpdater.java
@@ -117,6 +117,28 @@ public class TestTableUpdater extends 
TestFlinkIcebergSinkBase {
     assertThat(result.f1).isEqualTo(PartitionSpec.unpartitioned());
   }
 
+  @Test
+  void testBranchConcurrentlyCreatedByAnotherWriter() {
+    Catalog catalog = CATALOG_EXTENSION.catalog();
+    TableIdentifier tableIdentifier = TableIdentifier.parse("myTable");
+    catalog.createTable(tableIdentifier, SCHEMA);
+
+    TableMetadataCache cache =
+        new TableMetadataCache(catalog, 10, Long.MAX_VALUE, 10, 
CASE_SENSITIVE, PRESERVE_COLUMNS);
+    TableUpdater tableUpdater = new TableUpdater(cache, catalog, 
CASE_SENSITIVE, PRESERVE_COLUMNS);
+
+    // This writer's cache while the branch genuinely does not exist yet.
+    assertThat(cache.branch(tableIdentifier, "myBranch")).isNull();
+
+    // Simulate another writer subtask committing the branch
+    
catalog.loadTable(tableIdentifier).manageSnapshots().createBranch("myBranch").commit();
+
+    // Must not throw: a branch concurrently created by another writer must be 
treated success
+    tableUpdater.update(
+        tableIdentifier, "myBranch", SCHEMA, PartitionSpec.unpartitioned(), 
TableCreator.DEFAULT);
+    assertThat(cache.branch(tableIdentifier, "myBranch")).isNotNull();
+  }
+
   @Test
   void testBranchCreationAndCaching() {
     Catalog catalog = CATALOG_EXTENSION.catalog();

Reply via email to