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 25137c1d8f [Feature][Connector-V2] Add StarRocks timer flush (#11518)
25137c1d8f is described below

commit 25137c1d8fb8b2410eb0641343cac4e524f8375f
Author: zhiwei.niu <[email protected]>
AuthorDate: Sat Jul 25 21:35:49 2026 +0800

    [Feature][Connector-V2] Add StarRocks timer flush (#11518)
---
 docs/en/connectors/sink/StarRocks.md               |  33 ++-
 docs/zh/connectors/sink/StarRocks.md               |  33 ++-
 .../seatunnel/starrocks/sink/StarRocksSink.java    |   2 +-
 .../starrocks/sink/StarRocksSinkWriter.java        |  11 +-
 .../starrocks/sink/StarRocksSinkWriterTest.java    |  92 +++++++
 .../connector/starrocks/StarRocksTimerFlushIT.java | 276 +++++++++++++++++++++
 .../mysqlcdc_to_starrocks_timer_flush.conf         |  53 ++++
 7 files changed, 496 insertions(+), 4 deletions(-)

diff --git a/docs/en/connectors/sink/StarRocks.md 
b/docs/en/connectors/sink/StarRocks.md
index cd92b81d1b..2d22517a64 100644
--- a/docs/en/connectors/sink/StarRocks.md
+++ b/docs/en/connectors/sink/StarRocks.md
@@ -15,7 +15,7 @@ import ChangeLog from '../changelog/connector-starrocks.md';
 - [ ] [exactly-once](../../introduction/concepts/connector-v2-features.md)
 - [x] [cdc](../../introduction/concepts/connector-v2-features.md)
 - [x] [support multiple table 
write](../../introduction/concepts/connector-v2-features.md)
-- [ ] [timer flush](../../introduction/concepts/connector-v2-features.md)
+- [x] [timer flush](../../introduction/concepts/connector-v2-features.md)
 
 ## Description
 
@@ -170,6 +170,37 @@ sink {
 }
 ```
 
+### Zeta Timer Flush
+
+This engine-level capability is available only in Zeta. Configure 
`sink.flush.interval` in `env` to periodically write buffered rows through 
StarRocks Stream Load even when `batch_max_rows` and `batch_max_bytes` have not 
been reached. Spark and Flink do not trigger this scheduled flush.
+
+:::tip
+
+StarRocks timer flush does not provide 2PC exactly-once semantics. The 
StarRocks Sink remains at-least-once, and a task restart may submit rows again. 
When appropriate for the workload, a Primary Key table with deterministic keys 
can absorb duplicate writes.
+
+:::
+
+```hocon
+env {
+  job.mode = "STREAMING"
+  checkpoint.interval = 300000
+  sink.flush.interval = 5000
+}
+
+sink {
+  StarRocks {
+    nodeUrls = ["starrocks-fe:8030"]
+    base-url = "jdbc:mysql://starrocks-fe:9030/mydb"
+    username = root
+    password = ""
+    database = "mydb"
+    table = "mytable"
+    batch_max_rows = 10000
+    batch_max_bytes = 104857600
+  }
+}
+```
+
 ## Data Type Mapping
 
 | StarRocks Data type | SeaTunnel Data type |
diff --git a/docs/zh/connectors/sink/StarRocks.md 
b/docs/zh/connectors/sink/StarRocks.md
index e3a27b9af9..32726cfff7 100644
--- a/docs/zh/connectors/sink/StarRocks.md
+++ b/docs/zh/connectors/sink/StarRocks.md
@@ -14,7 +14,7 @@ import ChangeLog from '../changelog/connector-starrocks.md';
 
 - [ ] [精准一次](../../introduction/concepts/connector-v2-features.md)
 - [x] [cdc](../../introduction/concepts/connector-v2-features.md)
-- [ ] [timer flush](../../introduction/concepts/connector-v2-features.md)
+- [x] [timer flush](../../introduction/concepts/connector-v2-features.md)
 
 ## 描述
 
@@ -164,6 +164,37 @@ sink {
 }
 ```
 
+### Zeta 定时刷新
+
+该引擎级能力仅由 Zeta 支持。可以在 `env` 块中配置 `sink.flush.interval`,使尚未达到 `batch_max_rows` 和 
`batch_max_bytes` 的缓冲数据也能定时通过 StarRocks Stream Load 写出。Spark 和 Flink 不会触发该定时刷新。
+
+:::tip
+
+StarRocks 定时刷新不提供基于 2PC 的精准一次语义,StarRocks Sink 
仍为至少一次语义,任务重启后可能重复提交数据。如果业务场景适用,可以使用具有确定性主键的 Primary Key 表吸收重复写入。
+
+:::
+
+```hocon
+env {
+  job.mode = "STREAMING"
+  checkpoint.interval = 300000
+  sink.flush.interval = 5000
+}
+
+sink {
+  StarRocks {
+    nodeUrls = ["starrocks-fe:8030"]
+    base-url = "jdbc:mysql://starrocks-fe:9030/mydb"
+    username = root
+    password = ""
+    database = "mydb"
+    table = "mytable"
+    batch_max_rows = 10000
+    batch_max_bytes = 104857600
+  }
+}
+```
+
 ## 数据类型映射
 
 | StarRocks数据类型 | SeaTunnel数据类型 |
diff --git 
a/seatunnel-connectors-v2/connector-starrocks/src/main/java/org/apache/seatunnel/connectors/seatunnel/starrocks/sink/StarRocksSink.java
 
b/seatunnel-connectors-v2/connector-starrocks/src/main/java/org/apache/seatunnel/connectors/seatunnel/starrocks/sink/StarRocksSink.java
index b856ca1e64..7abdf06e3e 100644
--- 
a/seatunnel-connectors-v2/connector-starrocks/src/main/java/org/apache/seatunnel/connectors/seatunnel/starrocks/sink/StarRocksSink.java
+++ 
b/seatunnel-connectors-v2/connector-starrocks/src/main/java/org/apache/seatunnel/connectors/seatunnel/starrocks/sink/StarRocksSink.java
@@ -85,7 +85,7 @@ public class StarRocksSink extends 
AbstractSimpleSink<SeaTunnelRow, Void>
             log.warn("Failed to load JDBC driver {}", 
"com.mysql.cj.jdbc.Driver", e);
         }
         TablePath sinkTablePath = catalogTable.getTablePath();
-        return new StarRocksSinkWriter(sinkConfig, tableSchema, sinkTablePath);
+        return new StarRocksSinkWriter(context, sinkConfig, tableSchema, 
sinkTablePath);
     }
 
     @Override
diff --git 
a/seatunnel-connectors-v2/connector-starrocks/src/main/java/org/apache/seatunnel/connectors/seatunnel/starrocks/sink/StarRocksSinkWriter.java
 
b/seatunnel-connectors-v2/connector-starrocks/src/main/java/org/apache/seatunnel/connectors/seatunnel/starrocks/sink/StarRocksSinkWriter.java
index 902e288e3d..41df65a3fc 100644
--- 
a/seatunnel-connectors-v2/connector-starrocks/src/main/java/org/apache/seatunnel/connectors/seatunnel/starrocks/sink/StarRocksSinkWriter.java
+++ 
b/seatunnel-connectors-v2/connector-starrocks/src/main/java/org/apache/seatunnel/connectors/seatunnel/starrocks/sink/StarRocksSinkWriter.java
@@ -17,6 +17,7 @@
 
 package org.apache.seatunnel.connectors.seatunnel.starrocks.sink;
 
+import org.apache.seatunnel.api.sink.SinkWriter;
 import org.apache.seatunnel.api.sink.SupportMultiTableSinkWriter;
 import org.apache.seatunnel.api.sink.SupportSchemaEvolutionSinkWriter;
 import org.apache.seatunnel.api.table.catalog.TablePath;
@@ -57,13 +58,17 @@ public class StarRocksSinkWriter extends 
AbstractSinkWriter<SeaTunnelRow, Void>
             new TableSchemaChangeEventDispatcher();
 
     public StarRocksSinkWriter(
-            SinkConfig sinkConfig, TableSchema tableSchema, TablePath 
tablePath) {
+            SinkWriter.Context context,
+            SinkConfig sinkConfig,
+            TableSchema tableSchema,
+            TablePath tablePath) {
         this.tableSchema = tableSchema;
         SeaTunnelRowType seaTunnelRowType = 
tableSchema.toPhysicalRowDataType();
         this.serializer = createSerializer(sinkConfig, seaTunnelRowType);
         this.manager = new StarRocksSinkManager(sinkConfig, tableSchema);
         this.sinkConfig = sinkConfig;
         this.sinkTablePath = tablePath;
+        context.registerFlushAction(this::timerFlush);
     }
 
     @Override
@@ -110,6 +115,10 @@ public class StarRocksSinkWriter extends 
AbstractSinkWriter<SeaTunnelRow, Void>
         return super.prepareCommit();
     }
 
+    private void timerFlush() throws IOException {
+        manager.flush();
+    }
+
     @Override
     public void close() throws IOException {
         try {
diff --git 
a/seatunnel-connectors-v2/connector-starrocks/src/test/java/org/apache/seatunnel/connectors/seatunnel/starrocks/sink/StarRocksSinkWriterTest.java
 
b/seatunnel-connectors-v2/connector-starrocks/src/test/java/org/apache/seatunnel/connectors/seatunnel/starrocks/sink/StarRocksSinkWriterTest.java
new file mode 100644
index 0000000000..e7fa042c0a
--- /dev/null
+++ 
b/seatunnel-connectors-v2/connector-starrocks/src/test/java/org/apache/seatunnel/connectors/seatunnel/starrocks/sink/StarRocksSinkWriterTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.connectors.seatunnel.starrocks.sink;
+
+import org.apache.seatunnel.api.sink.SinkWriter;
+import org.apache.seatunnel.api.table.catalog.PhysicalColumn;
+import org.apache.seatunnel.api.table.catalog.TablePath;
+import org.apache.seatunnel.api.table.catalog.TableSchema;
+import org.apache.seatunnel.api.table.type.BasicType;
+import org.apache.seatunnel.common.utils.function.RunnableWithException;
+import 
org.apache.seatunnel.connectors.seatunnel.starrocks.client.StarRocksSinkManager;
+import org.apache.seatunnel.connectors.seatunnel.starrocks.config.SinkConfig;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.MockedConstruction;
+import org.mockito.Mockito;
+
+import java.io.IOException;
+
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+public class StarRocksSinkWriterTest {
+
+    @Test
+    void shouldRegisterTimerFlushAction() throws Exception {
+        SinkWriter.Context context = mock(SinkWriter.Context.class);
+        ArgumentCaptor<RunnableWithException> actionCaptor =
+                ArgumentCaptor.forClass(RunnableWithException.class);
+
+        try (MockedConstruction<StarRocksSinkManager> mockedManager =
+                Mockito.mockConstruction(StarRocksSinkManager.class)) {
+            createWriter(context);
+
+            verify(context, 
times(1)).registerFlushAction(actionCaptor.capture());
+            actionCaptor.getValue().run();
+            verify(mockedManager.constructed().get(0), times(1)).flush();
+        }
+    }
+
+    @Test
+    void shouldPropagateTimerFlushFailure() throws Exception {
+        SinkWriter.Context context = mock(SinkWriter.Context.class);
+        ArgumentCaptor<RunnableWithException> actionCaptor =
+                ArgumentCaptor.forClass(RunnableWithException.class);
+
+        try (MockedConstruction<StarRocksSinkManager> mockedManager =
+                Mockito.mockConstruction(StarRocksSinkManager.class)) {
+            createWriter(context);
+            StarRocksSinkManager manager = mockedManager.constructed().get(0);
+            IOException expected = new IOException("timer flush failed");
+            doThrow(expected).when(manager).flush();
+
+            verify(context).registerFlushAction(actionCaptor.capture());
+            IOException actual =
+                    Assertions.assertThrows(IOException.class, 
actionCaptor.getValue()::run);
+            Assertions.assertSame(expected, actual);
+        }
+    }
+
+    private StarRocksSinkWriter createWriter(SinkWriter.Context context) {
+        SinkConfig sinkConfig = new SinkConfig();
+        sinkConfig.setLoadFormat(SinkConfig.StreamLoadFormat.JSON);
+        TableSchema tableSchema =
+                TableSchema.builder()
+                        .column(
+                                PhysicalColumn.of(
+                                        "id", BasicType.INT_TYPE, (Long) null, 
false, null, null))
+                        .build();
+        return new StarRocksSinkWriter(
+                context, sinkConfig, tableSchema, TablePath.of("test", 
"timer_flush"));
+    }
+}
diff --git 
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-starrocks-e2e/src/test/java/org/apache/seatunnel/e2e/connector/starrocks/StarRocksTimerFlushIT.java
 
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-starrocks-e2e/src/test/java/org/apache/seatunnel/e2e/connector/starrocks/StarRocksTimerFlushIT.java
new file mode 100644
index 0000000000..5f472bd014
--- /dev/null
+++ 
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-starrocks-e2e/src/test/java/org/apache/seatunnel/e2e/connector/starrocks/StarRocksTimerFlushIT.java
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.e2e.connector.starrocks;
+
+import 
org.apache.seatunnel.connectors.seatunnel.cdc.mysql.testutils.MySqlContainer;
+import 
org.apache.seatunnel.connectors.seatunnel.cdc.mysql.testutils.MySqlVersion;
+import 
org.apache.seatunnel.connectors.seatunnel.cdc.mysql.testutils.UniqueDatabase;
+import org.apache.seatunnel.e2e.common.TestResource;
+import org.apache.seatunnel.e2e.common.TestSuiteBase;
+import org.apache.seatunnel.e2e.common.container.ContainerExtendedFactory;
+import org.apache.seatunnel.e2e.common.container.EngineType;
+import org.apache.seatunnel.e2e.common.container.TestContainer;
+import org.apache.seatunnel.e2e.common.junit.DisabledOnContainer;
+import org.apache.seatunnel.e2e.common.junit.TestContainerExtension;
+import org.apache.seatunnel.e2e.common.util.JobIdGenerator;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.TestTemplate;
+import org.testcontainers.containers.Container;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.lifecycle.Startables;
+import org.testcontainers.utility.DockerLoggerFactory;
+import org.testcontainers.utility.MountableFile;
+
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import static org.awaitility.Awaitility.await;
+import static org.awaitility.Awaitility.given;
+
+@Slf4j
+@DisabledOnContainer(
+        value = {},
+        type = {EngineType.SPARK, EngineType.FLINK},
+        disabledReason =
+                "engine-level timer flush (sink.flush.interval) is only 
supported on Zeta engine")
+public class StarRocksTimerFlushIT extends TestSuiteBase implements 
TestResource {
+
+    private static final String STARROCKS_IMAGE = 
"seatunnelhub/starrocks-starter:2.2.1";
+    private static final String STARROCKS_HOST = "starrocks_timer_flush_e2e";
+    private static final int STARROCKS_QUERY_PORT = 9030;
+    private static final String MYSQL_HOST = "mysql_starrocks_timer_flush_e2e";
+    private static final String MYSQL_USER_NAME = "mysqluser";
+    private static final String MYSQL_USER_PASSWORD = "mysqlpw";
+    private static final String MYSQL_DATABASE = "shop";
+    private static final String SINK_DATABASE = "timer_flush";
+    private static final String SINK_TABLE = "starrocks_timer_flush";
+    private static final String JDBC_PLUGIN_LIB = 
"/tmp/seatunnel/plugins/Jdbc/lib";
+    private static final String MYSQL_CDC_PLUGIN_LIB = 
"/tmp/seatunnel/plugins/MySQL-CDC/lib";
+    private static final MySqlContainer MYSQL_CONTAINER = 
createMySqlContainer(MySqlVersion.V8_0);
+
+    private final UniqueDatabase shopDatabase = new 
UniqueDatabase(MYSQL_CONTAINER, MYSQL_DATABASE);
+    private GenericContainer<?> starRocksServer;
+    private Connection starRocksConnection;
+
+    @TestContainerExtension
+    private final ContainerExtendedFactory extendedFactory =
+            container -> {
+                copyMySQLDriverToContainer(container, JDBC_PLUGIN_LIB);
+                copyMySQLDriverToContainer(container, MYSQL_CDC_PLUGIN_LIB);
+            };
+
+    @BeforeAll
+    @Override
+    public void startUp() {
+        starRocksServer =
+                new GenericContainer<>(STARROCKS_IMAGE)
+                        .withNetwork(NETWORK)
+                        .withNetworkAliases(STARROCKS_HOST)
+                        .withExposedPorts(STARROCKS_QUERY_PORT)
+                        .withLogConsumer(
+                                new Slf4jLogConsumer(
+                                        
DockerLoggerFactory.getLogger(STARROCKS_IMAGE)));
+        Startables.deepStart(Stream.of(starRocksServer, 
MYSQL_CONTAINER)).join();
+        given().ignoreExceptions()
+                .await()
+                .atMost(360, TimeUnit.SECONDS)
+                .untilAsserted(this::initializeStarRocksConnection);
+        shopDatabase.createAndInitialize();
+        initializeTimerFlushTable();
+    }
+
+    @TestTemplate
+    public void testStarRocksTimerFlush(TestContainer testContainer) throws 
Exception {
+        String jobId = String.valueOf(JobIdGenerator.newJobId());
+        CompletableFuture<Container.ExecResult> jobFuture =
+                CompletableFuture.supplyAsync(
+                        () -> {
+                            try {
+                                return testContainer.executeJob(
+                                        
"/mysqlcdc_to_starrocks_timer_flush.conf", jobId);
+                            } catch (Exception e) {
+                                throw new RuntimeException(e);
+                            }
+                        });
+
+        try {
+            await().atMost(2, TimeUnit.MINUTES)
+                    .pollInterval(2, TimeUnit.SECONDS)
+                    .untilAsserted(
+                            () -> {
+                                Assertions.assertFalse(
+                                        jobFuture.isDone(),
+                                        "The streaming job terminated before 
reaching RUNNING");
+                                Assertions.assertEquals(
+                                        "RUNNING", 
testContainer.getJobStatus(jobId));
+                            });
+
+            await().atMost(120, TimeUnit.SECONDS)
+                    .ignoreExceptions()
+                    .pollInterval(2, TimeUnit.SECONDS)
+                    .untilAsserted(
+                            () -> {
+                                Assertions.assertFalse(
+                                        jobFuture.isDone(),
+                                        "The streaming job must still be 
running when timer flush publishes the snapshot");
+                                Assertions.assertEquals(9, tableCount());
+                            });
+
+            try (Connection connection = shopDatabase.getJdbcConnection();
+                    Statement statement = connection.createStatement()) {
+                statement.executeUpdate(
+                        "INSERT INTO products (id, name, description, weight) "
+                                + "VALUES (110, 'timer-flush', 'timer-flush 
probe', 1.0)");
+            }
+
+            await().atMost(120, TimeUnit.SECONDS)
+                    .ignoreExceptions()
+                    .pollInterval(2, TimeUnit.SECONDS)
+                    .untilAsserted(
+                            () -> {
+                                Assertions.assertFalse(
+                                        jobFuture.isDone(),
+                                        "The streaming job must still be 
running when timer flush publishes the binlog event");
+                                Assertions.assertEquals(10, tableCount());
+                            });
+        } finally {
+            if (!jobFuture.isDone()) {
+                Container.ExecResult cancelResult = 
testContainer.cancelJob(jobId);
+                Assertions.assertEquals(0, cancelResult.getExitCode(), 
cancelResult.getStderr());
+            }
+        }
+
+        Container.ExecResult jobResult = jobFuture.get(120, TimeUnit.SECONDS);
+        Assertions.assertEquals(0, jobResult.getExitCode(), 
jobResult.getStderr());
+    }
+
+    private void initializeStarRocksConnection() throws SQLException {
+        starRocksConnection =
+                DriverManager.getConnection(
+                        String.format(
+                                "jdbc:mysql://%s:%s",
+                                starRocksServer.getHost(),
+                                
starRocksServer.getMappedPort(STARROCKS_QUERY_PORT)),
+                        "root",
+                        "");
+    }
+
+    private void initializeTimerFlushTable() {
+        String createTableSql =
+                "CREATE TABLE IF NOT EXISTS "
+                        + SINK_DATABASE
+                        + "."
+                        + SINK_TABLE
+                        + " ("
+                        + "id INT NOT NULL, "
+                        + "name VARCHAR(255), "
+                        + "description VARCHAR(512), "
+                        + "weight FLOAT) "
+                        + "ENGINE=OLAP DUPLICATE KEY(id) "
+                        + "DISTRIBUTED BY HASH(id) BUCKETS 1 "
+                        + "PROPERTIES ('replication_num' = '1')";
+        try (Statement statement = starRocksConnection.createStatement()) {
+            statement.execute("CREATE DATABASE IF NOT EXISTS " + 
SINK_DATABASE);
+            statement.execute(createTableSql);
+            statement.execute("TRUNCATE TABLE " + SINK_DATABASE + "." + 
SINK_TABLE);
+        } catch (SQLException e) {
+            throw new RuntimeException("Failed to initialize StarRocks timer 
flush table", e);
+        }
+    }
+
+    private int tableCount() {
+        String sql = "SELECT COUNT(*) FROM " + SINK_DATABASE + "." + 
SINK_TABLE;
+        try (Statement statement = starRocksConnection.createStatement();
+                ResultSet resultSet = statement.executeQuery(sql)) {
+            Assertions.assertTrue(resultSet.next());
+            return resultSet.getInt(1);
+        } catch (SQLException e) {
+            throw new RuntimeException("Failed to count rows in StarRocks 
timer flush table", e);
+        }
+    }
+
+    private void copyMySQLDriverToContainer(GenericContainer<?> container, 
String pluginLibDir)
+            throws IOException, InterruptedException {
+        Path driverJarPath = mysqlDriverJarPath();
+        Assertions.assertTrue(
+                Files.isRegularFile(driverJarPath),
+                "MySQL JDBC driver should be resolved from the test classpath 
before E2E runs: "
+                        + driverJarPath);
+        Container.ExecResult extraCommands =
+                container.execInContainer("bash", "-c", "mkdir -p " + 
pluginLibDir);
+        Assertions.assertEquals(0, extraCommands.getExitCode(), 
extraCommands.getStderr());
+        container.copyFileToContainer(
+                MountableFile.forHostPath(driverJarPath),
+                pluginLibDir + "/" + driverJarPath.getFileName());
+    }
+
+    private Path mysqlDriverJarPath() {
+        try {
+            return Paths.get(
+                    com.mysql.cj.jdbc.Driver.class
+                            .getProtectionDomain()
+                            .getCodeSource()
+                            .getLocation()
+                            .toURI());
+        } catch (Exception e) {
+            throw new RuntimeException(
+                    "Failed to resolve MySQL JDBC driver jar from the test 
classpath", e);
+        }
+    }
+
+    private static MySqlContainer createMySqlContainer(MySqlVersion version) {
+        return new MySqlContainer(version)
+                .withConfigurationOverride("docker/server-gtids/my.cnf")
+                .withSetupSQL("docker/setup.sql")
+                .withNetwork(NETWORK)
+                .withNetworkAliases(MYSQL_HOST)
+                .withDatabaseName(MYSQL_DATABASE)
+                .withUsername(MYSQL_USER_NAME)
+                .withPassword(MYSQL_USER_PASSWORD)
+                .withLogConsumer(
+                        new 
Slf4jLogConsumer(DockerLoggerFactory.getLogger("mysql-docker-image")));
+    }
+
+    @AfterAll
+    @Override
+    public void tearDown() throws Exception {
+        if (starRocksConnection != null) {
+            starRocksConnection.close();
+        }
+        if (starRocksServer != null) {
+            starRocksServer.close();
+        }
+        MYSQL_CONTAINER.close();
+    }
+}
diff --git 
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-starrocks-e2e/src/test/resources/mysqlcdc_to_starrocks_timer_flush.conf
 
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-starrocks-e2e/src/test/resources/mysqlcdc_to_starrocks_timer_flush.conf
new file mode 100644
index 0000000000..077cb3c222
--- /dev/null
+++ 
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-starrocks-e2e/src/test/resources/mysqlcdc_to_starrocks_timer_flush.conf
@@ -0,0 +1,53 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+env {
+  parallelism = 1
+  job.mode = "STREAMING"
+  checkpoint.interval = 300000
+  sink.flush.interval = 500
+}
+
+source {
+  MySQL-CDC {
+    parallelism = 1
+    server-id = 5670
+    username = "st_user_source"
+    password = "mysqlpw"
+    table-names = ["shop.products"]
+    url = "jdbc:mysql://mysql_starrocks_timer_flush_e2e:3306/shop"
+  }
+}
+
+sink {
+  StarRocks {
+    nodeUrls = ["starrocks_timer_flush_e2e:8030"]
+    base-url = "jdbc:mysql://starrocks_timer_flush_e2e:9030/timer_flush"
+    username = root
+    password = ""
+    database = "timer_flush"
+    table = "starrocks_timer_flush"
+    labelPrefix = "timer-flush-"
+    batch_max_rows = 100000
+    batch_max_bytes = 104857600
+    schema_save_mode = "IGNORE"
+    data_save_mode = "APPEND_DATA"
+    starrocks.config = {
+      format = "JSON"
+    }
+  }
+}

Reply via email to