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

github-merge-queue[bot] 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 9544fb8cf2 [Test][Connector-V2][Iceberg] Poll for CDC capture and 
checkpoint commits instead of fixed sleeps (#13067)
9544fb8cf2 is described below

commit 9544fb8cf21843239bf835ca1ac8f82afe0ddd96
Author: Jast <[email protected]>
AuthorDate: Sat Oct 31 12:14:14 2026 +0000

    [Test][Connector-V2][Iceberg] Poll for CDC capture and checkpoint commits 
instead of fixed sleeps (#13067)
    
    Co-authored-by: jast <[email protected]>
---
 .../e2e/connector/iceberg/IcebergSinkCDCIT.java    | 36 +++++++++++--
 .../iceberg/IcebergSinkParallelCommitIT.java       | 60 +++++++++++++++++-----
 2 files changed, 81 insertions(+), 15 deletions(-)

diff --git 
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-iceberg-e2e/src/test/java/org/apache/seatunnel/e2e/connector/iceberg/IcebergSinkCDCIT.java
 
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-iceberg-e2e/src/test/java/org/apache/seatunnel/e2e/connector/iceberg/IcebergSinkCDCIT.java
index 0099c3f586..79b6a168af 100644
--- 
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-iceberg-e2e/src/test/java/org/apache/seatunnel/e2e/connector/iceberg/IcebergSinkCDCIT.java
+++ 
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-iceberg-e2e/src/test/java/org/apache/seatunnel/e2e/connector/iceberg/IcebergSinkCDCIT.java
@@ -69,7 +69,6 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Stream;
 
-import static java.lang.Thread.sleep;
 import static 
org.apache.seatunnel.connectors.seatunnel.iceberg.config.IcebergCatalogType.HADOOP;
 import static org.awaitility.Awaitility.given;
 
@@ -326,7 +325,18 @@ public class IcebergSinkCDCIT extends TestSuiteBase 
implements TestResource {
                         MYSQL_DATABASE, SOURCE_TABLE, modifyTypeField1, 
modifyTypeField2);
         executeSql(insertTypeColumnsSql);
 
-        sleep(30000); // Wait for source capture data
+        // Wait until the CDC source has captured the pre-ALTER record (it 
becomes visible in
+        // the sink table) instead of waiting a fixed time, so the schema 
change is applied
+        // only after the old-schema row was consumed.
+        given().ignoreExceptions()
+                .await()
+                .atMost(120000, TimeUnit.MILLISECONDS)
+                .untilAsserted(
+                        () ->
+                                Assertions.assertTrue(
+                                        icebergTableHasRecordWithId(300),
+                                        "Record 300 should be captured before 
the column type"
+                                                + " change"));
 
         // Now modify multiple column types in a single ALTER TABLE statement
         String modifyTypesSql =
@@ -428,7 +438,18 @@ public class IcebergSinkCDCIT extends TestSuiteBase 
implements TestResource {
                         MYSQL_DATABASE, SOURCE_TABLE, modifyField1, 
modifyField2);
         executeSql(insertModifyColumnsSql);
 
-        sleep(30000); // Wait for source capture data
+        // Wait until the CDC source has captured the pre-ALTER record (it 
becomes visible in
+        // the sink table) instead of waiting a fixed time, so the schema 
change is applied
+        // only after the old-schema row was consumed.
+        given().ignoreExceptions()
+                .await()
+                .atMost(120000, TimeUnit.MILLISECONDS)
+                .untilAsserted(
+                        () ->
+                                Assertions.assertTrue(
+                                        icebergTableHasRecordWithId(400),
+                                        "Record 400 should be captured before 
the column type"
+                                                + " change"));
 
         // Now modify multiple columns in a single ALTER TABLE statement
         String modifyColumnsSql =
@@ -748,6 +769,15 @@ public class IcebergSinkCDCIT extends TestSuiteBase 
implements TestResource {
         return results;
     }
 
+    private boolean icebergTableHasRecordWithId(int id) {
+        for (Record record : loadIcebergTable()) {
+            if (Integer.valueOf(id).equals(record.getField("id"))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @NotNull private static IcebergTableLoader getTableLoader() {
         Map<String, Object> configs = new HashMap<>();
         Map<String, Object> catalogProps = new HashMap<>();
diff --git 
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-iceberg-e2e/src/test/java/org/apache/seatunnel/e2e/connector/iceberg/IcebergSinkParallelCommitIT.java
 
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-iceberg-e2e/src/test/java/org/apache/seatunnel/e2e/connector/iceberg/IcebergSinkParallelCommitIT.java
index 0698a83964..617a631b2f 100644
--- 
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-iceberg-e2e/src/test/java/org/apache/seatunnel/e2e/connector/iceberg/IcebergSinkParallelCommitIT.java
+++ 
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-iceberg-e2e/src/test/java/org/apache/seatunnel/e2e/connector/iceberg/IcebergSinkParallelCommitIT.java
@@ -244,12 +244,15 @@ public class IcebergSinkParallelCommitIT extends 
TestSuiteBase {
      * <ol>
      *   <li>Start a streaming job with {@code parallelism=2}. {@code 
split.read-interval} keeps
      *       FakeSource alive long enough to trigger several checkpoints.
-     *   <li>Wait 9 s — at least 3 checkpoints complete and their snapshots 
land in Iceberg.
+     *   <li>Wait until at least one checkpoint has been committed and its 
snapshots have landed in
+     *       Iceberg — bounded polling on committed state, not a fixed sleep.
      *   <li>Savepoint the job (stops it and persists the last committed 
state).
      *   <li>Record the set of {@code seatunnel.checkpoint-id} values already 
present in the table.
      *   <li>Restore the job from the savepoint. {@code 
IcebergAggregatedCommitter.restoreCommit()}
      *       must detect the already-committed checkpoint via the snapshot 
summary and skip it.
-     *   <li>Let the restored job run and produce additional checkpoints, then 
verify:
+     *   <li>Wait until the restored job commits a checkpoint-id that was 
absent before the
+     *       savepoint — this proves the restored run is live before any 
uniqueness claims — then
+     *       verify:
      *       <ul>
      *         <li>Every snapshot in the table has a <em>unique</em> 
checkpoint-id — no checkpoint
      *             was committed more than once.
@@ -278,8 +281,18 @@ public class IcebergSinkParallelCommitIT extends 
TestSuiteBase {
                             }
                         });
 
-        // checkpoint.interval=3s × 3 = 9 s
-        Thread.sleep(9_000);
+        // Wait until at least one checkpoint has been committed to the table 
instead of
+        // waiting a fixed number of checkpoint intervals, so the savepoint 
always happens
+        // after real commits even when checkpoints run slower under CI load.
+        given().ignoreExceptions()
+                .await()
+                .atMost(120000, TimeUnit.MILLISECONDS)
+                .untilAsserted(
+                        () ->
+                                Assertions.assertFalse(
+                                        
collectCheckpointIds(loadRecoveryTable()).isEmpty(),
+                                        "At least one checkpoint must have 
been committed"
+                                                + " before the savepoint"));
 
         Assertions.assertEquals(
                 0,
@@ -294,14 +307,37 @@ public class IcebergSinkParallelCommitIT extends 
TestSuiteBase {
                 "At least one checkpoint must have been committed before the 
savepoint");
         log.info("Checkpoint-ids committed before savepoint: {}", 
checkpointIdsBefore);
 
-        CompletableFuture.supplyAsync(
-                () -> {
-                    try {
-                        return container.restoreJob(recoveryConf, 
String.valueOf(jobId));
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
-                    }
-                });
+        CompletableFuture<Container.ExecResult> restoreRun =
+                CompletableFuture.supplyAsync(
+                        () -> {
+                            try {
+                                return container.restoreJob(recoveryConf, 
String.valueOf(jobId));
+                            } catch (Exception e) {
+                                throw new RuntimeException(e);
+                            }
+                        });
+
+        // The pre-savepoint snapshot set already satisfies every uniqueness 
and occurrence
+        // assertion below, so those checks alone cannot prove the restored 
run did anything.
+        // Wait until the restored job commits a checkpoint-id that was absent 
before the
+        // savepoint before making any recovery claims. The restored job is 
streaming, so the
+        // restore command only returns once the job stops — it must never be 
joined here;
+        // only a non-blocking fail-fast on an already-failed restore is done.
+        given().ignoreExceptions()
+                .await()
+                .atMost(120000, TimeUnit.MILLISECONDS)
+                .untilAsserted(
+                        () -> {
+                            Assertions.assertFalse(
+                                    restoreRun.isDone() && 
restoreRun.isCompletedExceptionally(),
+                                    "restoreJob must not fail while waiting 
for its commits");
+                            Set<String> current = 
collectCheckpointIds(loadRecoveryTable());
+                            current.removeAll(checkpointIdsBefore);
+                            Assertions.assertFalse(
+                                    current.isEmpty(),
+                                    "The restored job must commit at least one 
new checkpoint-id"
+                                            + " before recovery can be 
verified");
+                        });
 
         // Wait long enough for the restored job to finish (4 splits × 3 s + 
buffer)
         given().ignoreExceptions()

Reply via email to