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

davidzollo pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/seatunnel.git


The following commit(s) were added to refs/heads/dev by this push:
     new e5d8f94414 [Fix][API] Honor multi-table failure policy during timer 
flush (#11537)
e5d8f94414 is described below

commit e5d8f94414e8e4a504615cd44511aea9f49c78f6
Author: Doyeon Kim <[email protected]>
AuthorDate: Sat Jul 25 21:48:29 2026 +0900

    [Fix][API] Honor multi-table failure policy during timer flush (#11537)
---
 .../common/multitable/MultiTableFailurePhase.java  |  1 +
 .../sink/multitablesink/MultiTableSinkWriter.java  | 16 +++-
 .../multitablesink/MultiTableSinkWriterTest.java   | 90 ++++++++++++++++++++++
 3 files changed, 106 insertions(+), 1 deletion(-)

diff --git 
a/seatunnel-api/src/main/java/org/apache/seatunnel/api/common/multitable/MultiTableFailurePhase.java
 
b/seatunnel-api/src/main/java/org/apache/seatunnel/api/common/multitable/MultiTableFailurePhase.java
index 3fafecef94..f2d13a7b78 100644
--- 
a/seatunnel-api/src/main/java/org/apache/seatunnel/api/common/multitable/MultiTableFailurePhase.java
+++ 
b/seatunnel-api/src/main/java/org/apache/seatunnel/api/common/multitable/MultiTableFailurePhase.java
@@ -24,6 +24,7 @@ public enum MultiTableFailurePhase {
     SINK_INIT("sink_init"),
     SAVE_MODE("save_mode"),
     RUNTIME_WRITE("runtime_write"),
+    TIMER_FLUSH("timer_flush"),
     CHECKPOINT("checkpoint"),
     CLOSE("close");
 
diff --git 
a/seatunnel-api/src/main/java/org/apache/seatunnel/api/sink/multitablesink/MultiTableSinkWriter.java
 
b/seatunnel-api/src/main/java/org/apache/seatunnel/api/sink/multitablesink/MultiTableSinkWriter.java
index 5734566709..39bc951fbc 100644
--- 
a/seatunnel-api/src/main/java/org/apache/seatunnel/api/sink/multitablesink/MultiTableSinkWriter.java
+++ 
b/seatunnel-api/src/main/java/org/apache/seatunnel/api/sink/multitablesink/MultiTableSinkWriter.java
@@ -725,7 +725,21 @@ public class MultiTableSinkWriter
                 for (SinkIdentifier id : sinkWritersWithIndex.get(i).keySet()) 
{
                     SinkContextProxy proxy = proxyContexts.get(id);
                     if (proxy != null && proxy.getFlushAction() != null) {
-                        proxy.getFlushAction().run();
+                        try {
+                            proxy.getFlushAction().run();
+                        } catch (InterruptedException error) {
+                            Thread.currentThread().interrupt();
+                            throw error;
+                        } catch (Exception error) {
+                            if (failurePolicy.continueOtherTables()) {
+                                handleTableFailure(
+                                        id.getTableIdentifier(),
+                                        MultiTableFailurePhase.TIMER_FLUSH,
+                                        error);
+                                continue;
+                            }
+                            throw error;
+                        }
                     }
                 }
             }
diff --git 
a/seatunnel-api/src/test/java/org/apache/seatunnel/api/sink/multitablesink/MultiTableSinkWriterTest.java
 
b/seatunnel-api/src/test/java/org/apache/seatunnel/api/sink/multitablesink/MultiTableSinkWriterTest.java
index c068921f0f..17a87d3d16 100644
--- 
a/seatunnel-api/src/test/java/org/apache/seatunnel/api/sink/multitablesink/MultiTableSinkWriterTest.java
+++ 
b/seatunnel-api/src/test/java/org/apache/seatunnel/api/sink/multitablesink/MultiTableSinkWriterTest.java
@@ -345,6 +345,96 @@ public class MultiTableSinkWriterTest {
         Assertions.assertDoesNotThrow(multiTableSinkWriter::close);
     }
 
+    @Test
+    public void testAggregatedFlushIsolatesFailedTableAndFlushesHealthyTable() 
{
+        Map<SinkIdentifier, SinkWriter<SeaTunnelRow, ?, ?>> sinkWriters = new 
HashMap<>();
+        Map<SinkIdentifier, SinkWriter.Context> sinkWritersContext = new 
HashMap<>();
+        Map<SinkIdentifier, SinkContextProxy> proxyContexts = new HashMap<>();
+        SinkIdentifier failedIdentifier = SinkIdentifier.of("test.failed", 0);
+        SinkIdentifier healthyIdentifier = SinkIdentifier.of("test.healthy", 
1);
+        sinkWriters.put(failedIdentifier, new TestSinkWriter());
+        sinkWriters.put(healthyIdentifier, new TestSinkWriter());
+        sinkWritersContext.put(failedIdentifier, new TestSinkWriterContext());
+        sinkWritersContext.put(healthyIdentifier, new TestSinkWriterContext());
+
+        AtomicInteger failedFlushCount = new AtomicInteger();
+        SinkContextProxy failedProxy = new SinkContextProxy(0, 2, new 
TestSinkWriterContext());
+        failedProxy.registerFlushAction(
+                () -> {
+                    failedFlushCount.incrementAndGet();
+                    throw new IOException("intentional flush failure");
+                });
+        AtomicInteger healthyFlushCount = new AtomicInteger();
+        SinkContextProxy healthyProxy = new SinkContextProxy(1, 2, new 
TestSinkWriterContext());
+        healthyProxy.registerFlushAction(healthyFlushCount::incrementAndGet);
+        proxyContexts.put(failedIdentifier, failedProxy);
+        proxyContexts.put(healthyIdentifier, healthyProxy);
+
+        MultiTableSinkWriter multiTableSinkWriter =
+                new MultiTableSinkWriter(
+                        sinkWriters,
+                        2,
+                        sinkWritersContext,
+                        MultiTableFailurePolicy.CONTINUE_OTHER_TABLES,
+                        JobMode.BATCH);
+
+        Assertions.assertDoesNotThrow(() -> 
multiTableSinkWriter.aggregatedFlush(proxyContexts));
+        Assertions.assertEquals(1, failedFlushCount.get());
+        Assertions.assertEquals(1, healthyFlushCount.get());
+
+        Assertions.assertDoesNotThrow(() -> 
multiTableSinkWriter.aggregatedFlush(proxyContexts));
+        Assertions.assertEquals(1, failedFlushCount.get());
+        Assertions.assertEquals(2, healthyFlushCount.get());
+
+        IOException closeException =
+                Assertions.assertThrows(IOException.class, 
multiTableSinkWriter::close);
+        
Assertions.assertTrue(closeException.getMessage().contains("test.failed"));
+        
Assertions.assertTrue(closeException.getMessage().contains("phase=timer_flush"));
+    }
+
+    @Test
+    public void testAggregatedFlushFailFastStopsAtFirstFailure() {
+        Map<SinkIdentifier, SinkWriter<SeaTunnelRow, ?, ?>> sinkWriters = new 
HashMap<>();
+        Map<SinkIdentifier, SinkWriter.Context> sinkWritersContext = new 
HashMap<>();
+        Map<SinkIdentifier, SinkContextProxy> proxyContexts = new HashMap<>();
+        SinkIdentifier failedIdentifier = SinkIdentifier.of("test.failed", 0);
+        SinkIdentifier healthyIdentifier = SinkIdentifier.of("test.healthy", 
1);
+        sinkWriters.put(failedIdentifier, new TestSinkWriter());
+        sinkWriters.put(healthyIdentifier, new TestSinkWriter());
+        sinkWritersContext.put(failedIdentifier, new TestSinkWriterContext());
+        sinkWritersContext.put(healthyIdentifier, new TestSinkWriterContext());
+
+        AtomicInteger failedFlushCount = new AtomicInteger();
+        SinkContextProxy failedProxy = new SinkContextProxy(0, 2, new 
TestSinkWriterContext());
+        failedProxy.registerFlushAction(
+                () -> {
+                    failedFlushCount.incrementAndGet();
+                    throw new IOException("intentional flush failure");
+                });
+        AtomicInteger healthyFlushCount = new AtomicInteger();
+        SinkContextProxy healthyProxy = new SinkContextProxy(1, 2, new 
TestSinkWriterContext());
+        healthyProxy.registerFlushAction(healthyFlushCount::incrementAndGet);
+        proxyContexts.put(failedIdentifier, failedProxy);
+        proxyContexts.put(healthyIdentifier, healthyProxy);
+
+        MultiTableSinkWriter multiTableSinkWriter =
+                new MultiTableSinkWriter(
+                        sinkWriters,
+                        2,
+                        sinkWritersContext,
+                        MultiTableFailurePolicy.FAIL_FAST,
+                        JobMode.BATCH);
+
+        IOException flushException =
+                Assertions.assertThrows(
+                        IOException.class,
+                        () -> 
multiTableSinkWriter.aggregatedFlush(proxyContexts));
+        Assertions.assertEquals("intentional flush failure", 
flushException.getMessage());
+        Assertions.assertEquals(1, failedFlushCount.get());
+        Assertions.assertEquals(0, healthyFlushCount.get());
+        Assertions.assertDoesNotThrow(multiTableSinkWriter::close);
+    }
+
     @Test
     public void testIsolatedFailureMarkerRecognition() {
         String message =

Reply via email to