You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ro...@apache.org on 2024/11/14 07:29:53 UTC
(iotdb) branch master updated: Pipe IT: Add table model related IT (#14086)
This is an automated email from the ASF dual-hosted git repository.
rong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 86a33773e8c Pipe IT: Add table model related IT (#14086)
86a33773e8c is described below
commit 86a33773e8c95be59837fb7704967ec4b0cdc54b
Author: Zhenyu Luo <lu...@gmail.com>
AuthorDate: Thu Nov 14 15:29:47 2024 +0800
Pipe IT: Add table model related IT (#14086)
---
.github/workflows/pipe-it-2cluster.yml | 37 +
integration-test/pom.xml | 16 +-
.../iotdb/it/env/cluster/env/AbstractEnv.java | 11 +-
.../iotdb/it/env/remote/env/RemoteServerEnv.java | 2 +-
.../itbase/category/MultiClusterIT2TableModel.java | 22 +
.../java/org/apache/iotdb/itbase/env/BaseEnv.java | 11 +-
.../org/apache/iotdb/db/it/utils/TestUtils.java | 141 +++-
.../tablemodel/AbstractPipeTableModelTestIT.java | 69 ++
.../iotdb/pipe/it/tablemodel/IoTDBPipeAlterIT.java | 457 ++++++++++
.../it/tablemodel/IoTDBPipeAutoConflictIT.java | 267 ++++++
.../pipe/it/tablemodel/IoTDBPipeAutoDropIT.java | 152 ++++
.../pipe/it/tablemodel/IoTDBPipeClusterIT.java | 909 ++++++++++++++++++++
.../IoTDBPipeConnectorCompressionIT.java | 326 ++++++++
.../tablemodel/IoTDBPipeConnectorParallelIT.java | 99 +++
.../pipe/it/tablemodel/IoTDBPipeDataSinkIT.java | 219 +++++
.../pipe/it/tablemodel/IoTDBPipeExtractorIT.java | 921 +++++++++++++++++++++
.../pipe/it/tablemodel/IoTDBPipeLifeCycleIT.java | 642 ++++++++++++++
.../pipe/it/tablemodel/IoTDBPipeNullValueIT.java | 169 ++++
.../pipe/it/tablemodel/IoTDBPipeProtocolIT.java | 466 +++++++++++
.../it/tablemodel/IoTDBPipeSwitchStatusIT.java | 348 ++++++++
.../pipe/it/tablemodel/IoTDBPipeSyntaxIT.java | 685 +++++++++++++++
.../pipe/it/tablemodel/IoTDBPipeWithLoadIT.java | 120 +++
.../it/tablemodel/IoTDBTablePatternFormatIT.java | 572 +++++++++++++
.../iotdb/pipe/it/tablemodel/TableModelUtils.java | 395 +++++++++
24 files changed, 7048 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/pipe-it-2cluster.yml b/.github/workflows/pipe-it-2cluster.yml
index 29aaeea3fc6..aca219cdcbd 100644
--- a/.github/workflows/pipe-it-2cluster.yml
+++ b/.github/workflows/pipe-it-2cluster.yml
@@ -234,3 +234,40 @@ jobs:
name: cluster-log-subscription-regression-misc-java${{ matrix.java }}-${{ runner.os }}-${{ matrix.cluster1 }}-${{ matrix.cluster2 }}
path: integration-test/target/cluster-logs
retention-days: 30
+ table-model:
+ strategy:
+ fail-fast: false
+ max-parallel: 15
+ matrix:
+ java: [17]
+ # StrongConsistencyClusterMode is ignored now because RatisConsensus has not been supported yet.
+ cluster: [LightWeightStandaloneMode, ScalableSingleNodeMode, HighPerformanceMode]
+ os: [ ubuntu-latest ]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up JDK ${{ matrix.java }}
+ uses: actions/setup-java@v4
+ with:
+ distribution: liberica
+ java-version: ${{ matrix.java }}
+ - name: IT Test
+ shell: bash
+ # we do not compile client-cpp for saving time, it is tested in client.yml
+ # we can skip influxdb-protocol because it has been tested separately in influxdb-protocol.yml
+ run: |
+ mvn clean verify \
+ -P with-integration-tests \
+ -DskipUTs \
+ -DintegrationTest.forkCount=1 -DConfigNodeMaxHeapSize=256 -DDataNodeMaxHeapSize=1024 -DDataNodeMaxDirectMemorySize=768 \
+ -DClusterConfigurations=${{ matrix.cluster }},${{ matrix.cluster }} \
+ -pl integration-test \
+ -am -PMultiClusterIT2TableModel \
+ -ntp
+ - name: Upload Artifact
+ if: failure()
+ uses: actions/upload-artifact@v4
+ with:
+ name: cluster-log-table-model-java${{ matrix.java }}-${{ runner.os }}-${{ matrix.cluster }}-${{ matrix.cluster }}
+ path: integration-test/target/cluster-logs
+ retention-days: 30
diff --git a/integration-test/pom.xml b/integration-test/pom.xml
index da5f9b7c1c0..75fd871993d 100644
--- a/integration-test/pom.xml
+++ b/integration-test/pom.xml
@@ -415,7 +415,7 @@
</activation>
<properties>
<integrationTest.excludedGroups/>
- <integrationTest.includedGroups>org.apache.iotdb.itbase.category.MultiClusterIT1,org.apache.iotdb.itbase.category.MultiClusterIT2AutoCreateSchema,org.apache.iotdb.itbase.category.MultiClusterIT2ManualCreateSchema,org.apache.iotdb.itbase.category.MultiClusterIT2SubscriptionArchVerification,org.apache.iotdb.itbase.category.MultiClusterIT2SubscriptionRegressionConsumer,org.apache.iotdb.itbase.category.MultiClusterIT2SubscriptionRegressionMisc,org.apache.iotdb.itbase.category [...]
+ <integrationTest.includedGroups>org.apache.iotdb.itbase.category.MultiClusterIT1,org.apache.iotdb.itbase.category.MultiClusterIT2AutoCreateSchema,org.apache.iotdb.itbase.category.MultiClusterIT2ManualCreateSchema,org.apache.iotdb.itbase.category.MultiClusterIT2SubscriptionArchVerification,org.apache.iotdb.itbase.category.MultiClusterIT2SubscriptionRegressionConsumer,org.apache.iotdb.itbase.category.MultiClusterIT2SubscriptionRegressionMisc,org.apache.iotdb.itbase.category [...]
<integrationTest.launchNodeInSameJVM>false</integrationTest.launchNodeInSameJVM>
<integrationTest.randomSelectWriteNode>true</integrationTest.randomSelectWriteNode>
<integrationTest.readAndVerifyWithMultiNode>true</integrationTest.readAndVerifyWithMultiNode>
@@ -464,6 +464,20 @@
<integrationTest.testEnv>MultiCluster</integrationTest.testEnv>
</properties>
</profile>
+ <profile>
+ <id>MultiClusterIT2TableModel</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <properties>
+ <integrationTest.excludedGroups/>
+ <integrationTest.includedGroups>org.apache.iotdb.itbase.category.MultiClusterIT2TableModel</integrationTest.includedGroups>
+ <integrationTest.launchNodeInSameJVM>false</integrationTest.launchNodeInSameJVM>
+ <integrationTest.randomSelectWriteNode>true</integrationTest.randomSelectWriteNode>
+ <integrationTest.readAndVerifyWithMultiNode>true</integrationTest.readAndVerifyWithMultiNode>
+ <integrationTest.testEnv>MultiCluster</integrationTest.testEnv>
+ </properties>
+ </profile>
<profile>
<id>MultiClusterIT2SubscriptionArchVerification</id>
<activation>
diff --git a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
index 2b2a262dbd2..90d61d754c6 100644
--- a/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
+++ b/integration-test/src/main/java/org/apache/iotdb/it/env/cluster/env/AbstractEnv.java
@@ -436,11 +436,18 @@ public abstract class AbstractEnv implements BaseEnv {
@Override
public Connection getWriteOnlyConnectionWithSpecifiedDataNode(
- final DataNodeWrapper dataNode, final String username, final String password)
+ final DataNodeWrapper dataNode,
+ final String username,
+ final String password,
+ String sqlDialect)
throws SQLException {
return new ClusterTestConnection(
getWriteConnectionWithSpecifiedDataNode(
- dataNode, null, username, password, TREE_SQL_DIALECT),
+ dataNode,
+ null,
+ username,
+ password,
+ TABLE_SQL_DIALECT.equals(sqlDialect) ? TABLE_SQL_DIALECT : TREE_SQL_DIALECT),
Collections.emptyList());
}
diff --git a/integration-test/src/main/java/org/apache/iotdb/it/env/remote/env/RemoteServerEnv.java b/integration-test/src/main/java/org/apache/iotdb/it/env/remote/env/RemoteServerEnv.java
index 04e3399ad32..70617f41f28 100644
--- a/integration-test/src/main/java/org/apache/iotdb/it/env/remote/env/RemoteServerEnv.java
+++ b/integration-test/src/main/java/org/apache/iotdb/it/env/remote/env/RemoteServerEnv.java
@@ -137,7 +137,7 @@ public class RemoteServerEnv implements BaseEnv {
@Override
public Connection getWriteOnlyConnectionWithSpecifiedDataNode(
- DataNodeWrapper dataNode, String username, String password) {
+ DataNodeWrapper dataNode, String username, String password, String sqlDialect) {
throw new UnsupportedOperationException();
}
diff --git a/integration-test/src/main/java/org/apache/iotdb/itbase/category/MultiClusterIT2TableModel.java b/integration-test/src/main/java/org/apache/iotdb/itbase/category/MultiClusterIT2TableModel.java
new file mode 100644
index 00000000000..c9280cd3fc6
--- /dev/null
+++ b/integration-test/src/main/java/org/apache/iotdb/itbase/category/MultiClusterIT2TableModel.java
@@ -0,0 +1,22 @@
+/*
+ * 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.iotdb.itbase.category;
+
+public class MultiClusterIT2TableModel {}
diff --git a/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseEnv.java b/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseEnv.java
index 1d721aaa798..4ef14786d8c 100644
--- a/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseEnv.java
+++ b/integration-test/src/main/java/org/apache/iotdb/itbase/env/BaseEnv.java
@@ -147,12 +147,19 @@ public interface BaseEnv {
default Connection getWriteOnlyConnectionWithSpecifiedDataNode(DataNodeWrapper dataNode)
throws SQLException {
return getWriteOnlyConnectionWithSpecifiedDataNode(
- dataNode, SessionConfig.DEFAULT_USER, SessionConfig.DEFAULT_PASSWORD);
+ dataNode, SessionConfig.DEFAULT_USER, SessionConfig.DEFAULT_PASSWORD, TREE_SQL_DIALECT);
+ }
+
+ default Connection getWriteOnlyConnectionWithSpecifiedDataNode(
+ DataNodeWrapper dataNode, String sqlDialect) throws SQLException {
+ return getWriteOnlyConnectionWithSpecifiedDataNode(
+ dataNode, SessionConfig.DEFAULT_USER, SessionConfig.DEFAULT_PASSWORD, sqlDialect);
}
// This is useful when you shut down a dataNode.
Connection getWriteOnlyConnectionWithSpecifiedDataNode(
- DataNodeWrapper dataNode, String username, String password) throws SQLException;
+ DataNodeWrapper dataNode, String username, String password, String sqlDialect)
+ throws SQLException;
default Connection getConnectionWithSpecifiedDataNode(DataNodeWrapper dataNode)
throws SQLException {
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/utils/TestUtils.java b/integration-test/src/test/java/org/apache/iotdb/db/it/utils/TestUtils.java
index 641c8c31cc2..6e201567cd9 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/utils/TestUtils.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/utils/TestUtils.java
@@ -638,24 +638,82 @@ public class TestUtils {
env, sql, SessionConfig.DEFAULT_USER, SessionConfig.DEFAULT_PASSWORD);
}
+ public static boolean tryExecuteNonQueryWithRetry(
+ String dataBaseName, String sqlDialect, BaseEnv env, String sql) {
+ return tryExecuteNonQueryWithRetry(
+ env,
+ sql,
+ SessionConfig.DEFAULT_USER,
+ SessionConfig.DEFAULT_PASSWORD,
+ dataBaseName,
+ sqlDialect);
+ }
+
public static boolean tryExecuteNonQueryWithRetry(
BaseEnv env, String sql, String userName, String password) {
return tryExecuteNonQueriesWithRetry(env, Collections.singletonList(sql), userName, password);
}
+ public static boolean tryExecuteNonQueryWithRetry(
+ BaseEnv env,
+ String sql,
+ String userName,
+ String password,
+ String dataBaseName,
+ String sqlDialect) {
+ return tryExecuteNonQueriesWithRetry(
+ env, Collections.singletonList(sql), userName, password, dataBaseName, sqlDialect);
+ }
+
public static boolean tryExecuteNonQueriesWithRetry(BaseEnv env, List<String> sqlList) {
return tryExecuteNonQueriesWithRetry(
- env, sqlList, SessionConfig.DEFAULT_USER, SessionConfig.DEFAULT_PASSWORD);
+ env,
+ sqlList,
+ SessionConfig.DEFAULT_USER,
+ SessionConfig.DEFAULT_PASSWORD,
+ null,
+ BaseEnv.TREE_SQL_DIALECT);
+ }
+
+ public static boolean tryExecuteNonQueriesWithRetry(
+ String dataBase, String sqlDialect, BaseEnv env, List<String> sqlList) {
+ return tryExecuteNonQueriesWithRetry(
+ env,
+ sqlList,
+ SessionConfig.DEFAULT_USER,
+ SessionConfig.DEFAULT_PASSWORD,
+ dataBase,
+ sqlDialect);
}
// This method will not throw failure given that a failure is encountered.
// Instead, it returns a flag to indicate the result of the execution.
public static boolean tryExecuteNonQueriesWithRetry(
BaseEnv env, List<String> sqlList, String userName, String password) {
+ return tryExecuteNonQueriesWithRetry(
+ env, sqlList, userName, password, null, BaseEnv.TREE_SQL_DIALECT);
+ }
+
+ public static boolean tryExecuteNonQueriesWithRetry(
+ BaseEnv env,
+ List<String> sqlList,
+ String userName,
+ String password,
+ String dataBase,
+ String sqlDialect) {
int lastIndex = 0;
for (int retryCountLeft = 10; retryCountLeft >= 0; retryCountLeft--) {
- try (Connection connection = env.getConnection(userName, password);
+ try (Connection connection =
+ env.getConnection(
+ userName,
+ password,
+ BaseEnv.TABLE_SQL_DIALECT.equals(sqlDialect)
+ ? BaseEnv.TABLE_SQL_DIALECT
+ : BaseEnv.TREE_SQL_DIALECT);
Statement statement = connection.createStatement()) {
+ if (BaseEnv.TABLE_SQL_DIALECT.equals(sqlDialect) && dataBase != null) {
+ statement.execute("use " + dataBase);
+ }
for (int i = lastIndex; i < sqlList.size(); ++i) {
lastIndex = i;
statement.execute(sqlList.get(i));
@@ -731,6 +789,42 @@ public class TestUtils {
return false;
}
+ public static boolean tryExecuteNonQueriesOnSpecifiedDataNodeWithRetry(
+ BaseEnv env,
+ DataNodeWrapper wrapper,
+ List<String> sqlList,
+ String dataBase,
+ String sqlDialect) {
+ int lastIndex = 0;
+ for (int retryCountLeft = 10; retryCountLeft >= 0; retryCountLeft--) {
+ try (Connection connection =
+ env.getWriteOnlyConnectionWithSpecifiedDataNode(wrapper, sqlDialect);
+ Statement statement = connection.createStatement()) {
+
+ if (BaseEnv.TABLE_SQL_DIALECT.equals(sqlDialect) && dataBase != null) {
+ statement.execute("use " + dataBase);
+ }
+
+ for (int i = lastIndex; i < sqlList.size(); ++i) {
+ statement.execute(sqlList.get(i));
+ lastIndex = i;
+ }
+ return true;
+ } catch (SQLException e) {
+ if (retryCountLeft > 0) {
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException ignored) {
+ }
+ } else {
+ e.printStackTrace();
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+
public static void executeQuery(String sql) {
executeQuery(sql, SessionConfig.DEFAULT_USER, SessionConfig.DEFAULT_PASSWORD);
}
@@ -909,6 +1003,49 @@ public class TestUtils {
}
}
+ public static void assertDataEventuallyOnEnv(
+ BaseEnv env,
+ String sql,
+ String expectedHeader,
+ Set<String> expectedResSet,
+ String dataBaseName) {
+ assertDataEventuallyOnEnv(env, sql, expectedHeader, expectedResSet, 300, dataBaseName);
+ }
+
+ public static void assertDataEventuallyOnEnv(
+ BaseEnv env,
+ String sql,
+ String expectedHeader,
+ Set<String> expectedResSet,
+ long timeoutSeconds,
+ String dataBaseName) {
+ try (Connection connection = env.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ Statement statement = connection.createStatement()) {
+ // Keep retrying if there are execution failures
+ await()
+ .pollInSameThread()
+ .pollDelay(1L, TimeUnit.SECONDS)
+ .pollInterval(1L, TimeUnit.SECONDS)
+ .atMost(timeoutSeconds, TimeUnit.SECONDS)
+ .untilAsserted(
+ () -> {
+ try {
+ if (dataBaseName != null) {
+ statement.execute("use " + dataBaseName);
+ }
+ if (sql != null && !sql.equals("")) {
+ TestUtils.assertResultSetEqual(
+ executeQueryWithRetry(statement, sql), expectedHeader, expectedResSet);
+ }
+ } catch (Exception e) {
+ Assert.fail();
+ }
+ });
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+
public static void assertDataEventuallyOnEnv(
BaseEnv env, String sql, Map<String, String> expectedHeaderWithResult) {
assertDataEventuallyOnEnv(env, sql, expectedHeaderWithResult, 600);
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/AbstractPipeTableModelTestIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/AbstractPipeTableModelTestIT.java
new file mode 100644
index 00000000000..b18ac32aae5
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/AbstractPipeTableModelTestIT.java
@@ -0,0 +1,69 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.consensus.ConsensusFactory;
+import org.apache.iotdb.it.env.MultiEnvFactory;
+import org.apache.iotdb.itbase.env.BaseEnv;
+
+import org.junit.After;
+import org.junit.Before;
+
+abstract class AbstractPipeTableModelTestIT {
+
+ protected BaseEnv senderEnv;
+ protected BaseEnv receiverEnv;
+
+ @Before
+ public void setUp() {
+ MultiEnvFactory.createEnv(2);
+ senderEnv = MultiEnvFactory.getEnv(0);
+ receiverEnv = MultiEnvFactory.getEnv(1);
+ setupConfig();
+ senderEnv.initClusterEnvironment();
+ receiverEnv.initClusterEnvironment();
+ }
+
+ protected void setupConfig() {
+ // TODO: delete ratis configurations
+ senderEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS);
+ receiverEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS);
+
+ // 10 min, assert that the operations will not time out
+ senderEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+ receiverEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+ }
+
+ @After
+ public final void tearDown() {
+ senderEnv.cleanClusterEnvironment();
+ receiverEnv.cleanClusterEnvironment();
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeAlterIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeAlterIT.java
new file mode 100644
index 00000000000..0f682c7f666
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeAlterIT.java
@@ -0,0 +1,457 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TShowPipeInfo;
+import org.apache.iotdb.confignode.rpc.thrift.TShowPipeReq;
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.itbase.env.BaseEnv;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+
+import static org.junit.Assert.fail;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeAlterIT extends AbstractPipeTableModelTestIT {
+
+ @Test
+ public void testBasicAlterPipe() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ // Create pipe
+ final String sql =
+ String.format(
+ "create pipe a2b with source ('source'='iotdb-source', 'database-name'='test', 'table-name'='test1', 'mode.streaming'='true') with processor ('processor'='do-nothing-processor') with sink ('node-urls'='%s')",
+ receiverDataNode.getIpAndPortString());
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(sql);
+ } catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ // show pipe
+ long lastCreationTime;
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.size());
+ // Check status
+ Assert.assertEquals("RUNNING", showPipeResult.get(0).state);
+ // Check configurations
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("source=iotdb-source"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("database-name=test"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("table-name=test"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("mode.streaming=true"));
+ Assert.assertTrue(
+ showPipeResult.get(0).pipeProcessor.contains("processor=do-nothing-processor"));
+ Assert.assertTrue(
+ showPipeResult
+ .get(0)
+ .pipeConnector
+ .contains(String.format("node-urls=%s", receiverDataNode.getIpAndPortString())));
+ // Record last creation time
+ lastCreationTime = showPipeResult.get(0).creationTime;
+ }
+
+ // Stop pipe
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute("stop pipe a2b");
+ } catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ // Show pipe
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.size());
+ // Check status
+ Assert.assertEquals("STOPPED", showPipeResult.get(0).state);
+ }
+
+ // Alter pipe (modify)
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ "alter pipe a2b modify source ('table-name'='test1','database-name'='test1')");
+ } catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ // Show pipe
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.size());
+ // Check status
+ Assert.assertEquals("STOPPED", showPipeResult.get(0).state);
+ // Check configurations
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("source=iotdb-source"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("table-name=test1"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("database-name=test1"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("mode.streaming=true"));
+ Assert.assertTrue(
+ showPipeResult.get(0).pipeProcessor.contains("processor=do-nothing-processor"));
+ Assert.assertTrue(
+ showPipeResult
+ .get(0)
+ .pipeConnector
+ .contains(String.format("node-urls=%s", receiverDataNode.getIpAndPortString())));
+ // Check creation time and record last creation time
+ Assert.assertTrue(showPipeResult.get(0).creationTime > lastCreationTime);
+ lastCreationTime = showPipeResult.get(0).creationTime;
+ // Check exception message
+ Assert.assertEquals("", showPipeResult.get(0).exceptionMessage);
+ }
+
+ // Alter pipe (replace)
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ "alter pipe a2b replace source ('capture.table'='true','''source'='iotdb-source', 'table-name'='test','database-name'='test')");
+ } catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ // Show pipe
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.size());
+ // check status
+ Assert.assertEquals("STOPPED", showPipeResult.get(0).state);
+ // check configurations
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("source=iotdb-source"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("database-name=test"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("table-name=test"));
+ Assert.assertFalse(showPipeResult.get(0).pipeExtractor.contains("mode.streaming=true"));
+ Assert.assertTrue(
+ showPipeResult.get(0).pipeProcessor.contains("processor=do-nothing-processor"));
+ Assert.assertTrue(
+ showPipeResult
+ .get(0)
+ .pipeConnector
+ .contains(String.format("node-urls=%s", receiverDataNode.getIpAndPortString())));
+ // Check creation time and record last creation time
+ Assert.assertTrue(showPipeResult.get(0).creationTime > lastCreationTime);
+ lastCreationTime = showPipeResult.get(0).creationTime;
+ // check exception message
+ Assert.assertEquals("", showPipeResult.get(0).exceptionMessage);
+ }
+
+ // Alter pipe (modify)
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute("alter pipe a2b modify sink ('sink.batch.enable'='false')");
+ } catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ // Show pipe
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.size());
+ // Check status
+ Assert.assertEquals("STOPPED", showPipeResult.get(0).state);
+ // Check configurations
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("source=iotdb-source"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("database-name=test"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("table-name=test"));
+ Assert.assertTrue(showPipeResult.get(0).pipeConnector.contains("batch.enable=false"));
+ Assert.assertTrue(
+ showPipeResult.get(0).pipeProcessor.contains("processor=do-nothing-processor"));
+ Assert.assertTrue(
+ showPipeResult
+ .get(0)
+ .pipeConnector
+ .contains(String.format("node-urls=%s", receiverDataNode.getIpAndPortString())));
+ // Check creation time and record last creation time
+ Assert.assertTrue(showPipeResult.get(0).creationTime > lastCreationTime);
+ lastCreationTime = showPipeResult.get(0).creationTime;
+ // Check exception message
+ Assert.assertEquals("", showPipeResult.get(0).exceptionMessage);
+ }
+
+ // Start pipe
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute("start pipe a2b");
+ } catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ // Alter pipe (modify)
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute("alter pipe a2b modify sink ('connector.batch.enable'='true')");
+ } catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ // Show pipe
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.size());
+ // Check status
+ Assert.assertEquals("RUNNING", showPipeResult.get(0).state);
+ // Check configurations
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("source=iotdb-source"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("database-name=test"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("table-name=test"));
+ Assert.assertTrue(showPipeResult.get(0).pipeConnector.contains("batch.enable=true"));
+ Assert.assertTrue(
+ showPipeResult
+ .get(0)
+ .pipeConnector
+ .contains(String.format("node-urls=%s", receiverDataNode.getIpAndPortString())));
+ // Check creation time and record last creation time
+ Assert.assertTrue(showPipeResult.get(0).creationTime > lastCreationTime);
+ lastCreationTime = showPipeResult.get(0).creationTime;
+ // Check exception message
+ Assert.assertEquals("", showPipeResult.get(0).exceptionMessage);
+ }
+
+ // Alter pipe (modify empty)
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute("alter pipe a2b modify source ()");
+ } catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ // show pipe
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.size());
+ // check status
+ Assert.assertEquals("RUNNING", showPipeResult.get(0).state);
+ // check configurations
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("source=iotdb-source"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("database-name=test"));
+ Assert.assertTrue(showPipeResult.get(0).pipeExtractor.contains("table-name=test"));
+ Assert.assertTrue(showPipeResult.get(0).pipeConnector.contains("batch.enable=true"));
+ Assert.assertTrue(
+ showPipeResult
+ .get(0)
+ .pipeConnector
+ .contains(String.format("node-urls=%s", receiverDataNode.getIpAndPortString())));
+ // Check creation time and record last creation time
+ Assert.assertTrue(showPipeResult.get(0).creationTime > lastCreationTime);
+ lastCreationTime = showPipeResult.get(0).creationTime;
+ // Check exception message
+ Assert.assertEquals("", showPipeResult.get(0).exceptionMessage);
+ }
+
+ // Alter pipe (replace empty)
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute("alter pipe a2b replace source ()");
+ } catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ // show pipe
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.size());
+ // check status
+ Assert.assertEquals("RUNNING", showPipeResult.get(0).state);
+ // check configurations
+ Assert.assertFalse(showPipeResult.get(0).pipeExtractor.contains("source=iotdb-source"));
+ Assert.assertFalse(showPipeResult.get(0).pipeExtractor.contains("database-name=test"));
+ Assert.assertFalse(showPipeResult.get(0).pipeExtractor.contains("table-name=test"));
+ Assert.assertTrue(showPipeResult.get(0).pipeConnector.contains("batch.enable=true"));
+ Assert.assertTrue(
+ showPipeResult
+ .get(0)
+ .pipeConnector
+ .contains(String.format("node-urls=%s", receiverDataNode.getIpAndPortString())));
+ // Check creation time and record last creation time
+ Assert.assertTrue(showPipeResult.get(0).creationTime > lastCreationTime);
+ lastCreationTime = showPipeResult.get(0).creationTime;
+ // Check exception message
+ Assert.assertEquals("", showPipeResult.get(0).exceptionMessage);
+ }
+
+ // Alter pipe (replace empty)
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute("alter pipe a2b replace processor ()");
+ } catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ // show pipe
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.size());
+ // check status
+ Assert.assertEquals("RUNNING", showPipeResult.get(0).state);
+ // check configurations
+ Assert.assertTrue(showPipeResult.get(0).pipeConnector.contains("batch.enable=true"));
+ Assert.assertFalse(
+ showPipeResult.get(0).pipeProcessor.contains("processor=do-nothing-processor"));
+ Assert.assertTrue(
+ showPipeResult
+ .get(0)
+ .pipeConnector
+ .contains(String.format("node-urls=%s", receiverDataNode.getIpAndPortString())));
+ // Check creation time and record last creation time
+ Assert.assertTrue(showPipeResult.get(0).creationTime > lastCreationTime);
+ lastCreationTime = showPipeResult.get(0).creationTime;
+ // Check exception message
+ Assert.assertEquals("", showPipeResult.get(0).exceptionMessage);
+ }
+
+ // Alter pipe (modify empty)
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute("alter pipe a2b modify sink ()");
+ } catch (SQLException e) {
+ fail(e.getMessage());
+ }
+
+ // Show pipe
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.size());
+ // Check status
+ Assert.assertEquals("RUNNING", showPipeResult.get(0).state);
+ // Check configurations
+ Assert.assertTrue(showPipeResult.get(0).pipeConnector.contains("batch.enable=true"));
+ Assert.assertTrue(
+ showPipeResult
+ .get(0)
+ .pipeConnector
+ .contains(String.format("node-urls=%s", receiverDataNode.getIpAndPortString())));
+ // Check creation time and record last creation time
+ Assert.assertTrue(showPipeResult.get(0).creationTime > lastCreationTime);
+ // Check exception message
+ Assert.assertEquals("", showPipeResult.get(0).exceptionMessage);
+ }
+ }
+
+ @Test
+ public void testAlterPipeFailure() {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ // alter non-existed pipe
+ String sql =
+ String.format(
+ "alter pipe a2b modify sink ('node-urls'='%s', 'batch.enable'='true')",
+ receiverDataNode.getIpAndPortString());
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(sql);
+ fail();
+ } catch (SQLException ignore) {
+ }
+
+ // Create pipe
+ sql =
+ String.format(
+ "create pipe a2b with source ('source'='iotdb-source', 'database-name'='test', 'table-name'='test1', 'mode.streaming'='true') with sink ('node-urls'='%s', 'batch.enable'='false')",
+ receiverDataNode.getIpAndPortString());
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(sql);
+ } catch (SQLException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testAlterPipeSourceAndSink() {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+ boolean insertResult = true;
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ // Create pipe
+ final String sql =
+ String.format(
+ "create pipe a2b with source ('source'='iotdb-source', 'database-name'='test', 'table-name'='test', 'mode.streaming'='true') with processor ('processor'='do-nothing-processor') with sink ('node-urls'='%s', 'batch.enable'='false')",
+ receiverDataNode.getIpAndPortString());
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(sql);
+ } catch (final SQLException e) {
+ fail(e.getMessage());
+ }
+
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ insertResult = insertResult && TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ // Check data on receiver
+ TableModelUtils.assertData("test", "test", 0, 100, receiverEnv);
+
+ // Alter pipe (modify 'source.path', 'source.inclusion' and
+ // 'processor.tumbling-time.interval-seconds')
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ "alter pipe a2b modify source('source' = 'iotdb-source','database-name'='test1', 'table-name'='test1', 'mode.streaming'='true', 'source.inclusion'='data.insert') modify sink ('batch.enable'='true')");
+ } catch (final SQLException e) {
+ fail(e.getMessage());
+ }
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ insertResult =
+ insertResult && TableModelUtils.insertData("test1", "test1", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ TableModelUtils.getQuerySql("test"),
+ TableModelUtils.generateHeaderResults(),
+ TableModelUtils.generateExpectedResults(0, 100),
+ "test");
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ TableModelUtils.getQuerySql("test1"),
+ TableModelUtils.generateHeaderResults(),
+ TableModelUtils.generateExpectedResults(0, 200),
+ "test1");
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeAutoConflictIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeAutoConflictIT.java
new file mode 100644
index 00000000000..3eccb7beafd
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeAutoConflictIT.java
@@ -0,0 +1,267 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.consensus.ConsensusFactory;
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.it.env.MultiEnvFactory;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.itbase.env.BaseEnv;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.fail;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeAutoConflictIT extends AbstractPipeTableModelTestIT {
+ @Before
+ public void setUp() {
+ MultiEnvFactory.createEnv(2);
+ senderEnv = MultiEnvFactory.getEnv(0);
+ receiverEnv = MultiEnvFactory.getEnv(1);
+
+ // TODO: delete ratis configurations
+ senderEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS);
+ receiverEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS);
+
+ // 10 min, assert that the operations will not time out
+ senderEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+ receiverEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+
+ senderEnv.initClusterEnvironment();
+ receiverEnv.initClusterEnvironment();
+ }
+
+ @Test
+ public void testDoubleLivingAutoConflict() throws Exception {
+ // Double living is two clusters each with a pipe connecting to the other.
+ final DataNodeWrapper senderDataNode = senderEnv.getDataNodeWrapper(0);
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ createDataBaseAndTable(senderEnv);
+ createDataBaseAndTable(receiverEnv);
+
+ final String senderIp = senderDataNode.getIp();
+ final int senderPort = senderDataNode.getPort();
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ insertData("test", "test1", 0, 100, senderEnv);
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("source.inclusion", "data.insert");
+ extractorAttributes.put("source.inclusion.exclusion", "");
+ extractorAttributes.put("capture.table", "true");
+ // Add this property to avoid making self cycle.
+ extractorAttributes.put("source.forwarding-pipe-requests", "false");
+
+ connectorAttributes.put("sink", "iotdb-thrift-sink");
+ connectorAttributes.put("sink.batch.enable", "false");
+ connectorAttributes.put("sink.ip", receiverIp);
+ connectorAttributes.put("sink.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ }
+
+ insertData("test", "test1", 100, 200, senderEnv);
+ insertData("test", "test1", 200, 300, receiverEnv);
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) receiverEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("source.inclusion", "data.insert");
+ extractorAttributes.put("source.inclusion.exclusion", "");
+ extractorAttributes.put("capture.table", "true");
+ // Add this property to avoid to make self cycle.
+ extractorAttributes.put("source.forwarding-pipe-requests", "false");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", senderIp);
+ connectorAttributes.put("connector.port", Integer.toString(senderPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ }
+ insertData("test", "test1", 300, 400, receiverEnv);
+
+ TableModelUtils.assertData("test", "test1", 0, 400, receiverEnv);
+
+ try {
+ TestUtils.restartCluster(senderEnv);
+ TestUtils.restartCluster(receiverEnv);
+ } catch (final Throwable e) {
+ fail(e.getMessage());
+ }
+
+ insertData("test", "test1", 400, 500, senderEnv);
+ insertData("test", "test1", 500, 600, receiverEnv);
+
+ TableModelUtils.assertData("test", "test1", 0, 600, receiverEnv);
+ }
+
+ @Test
+ public void testDoubleLivingAutoConflictTemplate() throws Exception {
+ final DataNodeWrapper senderDataNode = senderEnv.getDataNodeWrapper(0);
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String senderIp = senderDataNode.getIp();
+ final int senderPort = senderDataNode.getPort();
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ createDataBaseAndTable(senderEnv);
+ createDataBaseAndTable(receiverEnv);
+ insertData("test", "test", 0, 100, senderEnv);
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("source.inclusion", "data.insert");
+ extractorAttributes.put("source.inclusion.exclusion", "");
+ extractorAttributes.put("table-name", "test.*");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("source.forwarding-pipe-requests", "false");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ }
+
+ TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ TableModelUtils.insertData("test", "test1", 200, 300, receiverEnv);
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) receiverEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("source.inclusion", "data.insert");
+ extractorAttributes.put("source.inclusion.exclusion", "");
+ extractorAttributes.put("table-name", "test.*");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("source.forwarding-pipe-requests", "false");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "true");
+ connectorAttributes.put("connector.ip", senderIp);
+ connectorAttributes.put("connector.port", Integer.toString(senderPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ }
+ insertData("test", "test1", 300, 400, senderEnv);
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ TableModelUtils.getQuerySql("test"),
+ TableModelUtils.generateHeaderResults(),
+ TableModelUtils.generateExpectedResults(0, 200),
+ "test");
+
+ TestUtils.assertDataEventuallyOnEnv(
+ senderEnv,
+ TableModelUtils.getQuerySql("test1"),
+ TableModelUtils.generateHeaderResults(),
+ TableModelUtils.generateExpectedResults(200, 400),
+ "test");
+ }
+
+ private void createDataBaseAndTable(BaseEnv baseEnv) {
+ TableModelUtils.createDataBaseAndTable(baseEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(baseEnv, "test1", "test");
+ }
+
+ private void insertData(
+ String dataBaseName, String tableName, int start, int end, BaseEnv baseEnv) {
+ TableModelUtils.insertData(dataBaseName, tableName, start, end, baseEnv);
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeAutoDropIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeAutoDropIT.java
new file mode 100644
index 00000000000..ef4f645b27f
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeAutoDropIT.java
@@ -0,0 +1,152 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeAutoDropIT extends AbstractPipeTableModelTestIT {
+
+ @Test
+ public void testAutoDropInHistoricalTransfer() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+ boolean insertResult = true;
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("mode.snapshot", "true");
+ extractorAttributes.put("capture.table", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ TableModelUtils.getQueryCountSql("test"),
+ "_col0,",
+ Collections.singleton("100,"),
+ "test");
+
+ TestUtils.assertDataEventuallyOnEnv(
+ senderEnv,
+ "show pipes",
+ "ID,CreationTime,State,PipeSource,PipeProcessor,PipeSink,ExceptionMessage,RemainingEventCount,EstimatedRemainingSeconds,",
+ Collections.emptySet());
+ }
+ }
+
+ @Test
+ public void testAutoDropInHistoricalTransferWithTimeRange() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+ boolean insertResult = true;
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("mode.snapshot", "true");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("start-time", "0");
+ extractorAttributes.put("end-time", "49");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ TableModelUtils.getQueryCountSql("test"),
+ "_col0,",
+ Collections.singleton("50,"),
+ "test");
+
+ TestUtils.assertDataEventuallyOnEnv(
+ senderEnv,
+ "show pipes",
+ "ID,CreationTime,State,PipeSource,PipeProcessor,PipeSink,ExceptionMessage,RemainingEventCount,EstimatedRemainingSeconds,",
+ Collections.emptySet());
+ }
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeClusterIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeClusterIT.java
new file mode 100644
index 00000000000..8ad449eb149
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeClusterIT.java
@@ -0,0 +1,909 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.exception.ClientManagerException;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.commons.cluster.RegionRoleType;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.confignode.rpc.thrift.TShowPipeInfo;
+import org.apache.iotdb.confignode.rpc.thrift.TShowPipeReq;
+import org.apache.iotdb.confignode.rpc.thrift.TShowRegionReq;
+import org.apache.iotdb.confignode.rpc.thrift.TShowRegionResp;
+import org.apache.iotdb.consensus.ConsensusFactory;
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.it.env.MultiEnvFactory;
+import org.apache.iotdb.it.env.cluster.env.AbstractEnv;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.apache.thrift.TException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.fail;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeClusterIT extends AbstractPipeTableModelTestIT {
+
+ @Override
+ @Before
+ public void setUp() {
+ MultiEnvFactory.createEnv(2);
+ senderEnv = MultiEnvFactory.getEnv(0);
+ receiverEnv = MultiEnvFactory.getEnv(1);
+
+ senderEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS);
+
+ receiverEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS);
+
+ // 10 min, assert that the operations will not time out
+ senderEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+ receiverEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+
+ senderEnv.initClusterEnvironment(3, 3, 180);
+ receiverEnv.initClusterEnvironment(3, 3, 180);
+ }
+
+ @Test
+ public void testWithAllParametersInStreamingMode() throws Exception {
+ testWithAllParameters("true");
+ }
+
+ @Test
+ public void testWithAllParametersInNotStreamingMode() throws Exception {
+ testWithAllParameters("false");
+ }
+
+ private void testWithAllParameters(final String realtimeMode) throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+ boolean insertResult = true;
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor", "iotdb-extractor");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("table-name", "test");
+ extractorAttributes.put("start-time", "0");
+ extractorAttributes.put("end-time", "199");
+ extractorAttributes.put("mode.streaming", realtimeMode);
+
+ processorAttributes.put("processor", "do-nothing-processor");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+ connectorAttributes.put("connector.user", "root");
+ connectorAttributes.put("connector.password", "root");
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ TableModelUtils.getQuerySql("test"),
+ TableModelUtils.generateHeaderResults(),
+ TableModelUtils.generateExpectedResults(0, 100),
+ "test");
+
+ insertResult = TableModelUtils.insertData("test", "test", 100, 300, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ TableModelUtils.getQuerySql("test"),
+ TableModelUtils.generateHeaderResults(),
+ TableModelUtils.generateExpectedResults(0, 200),
+ "test");
+ }
+ }
+
+ @Test
+ public void testPipeAfterDataRegionLeaderStop() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ insertResult =
+ insertResult && TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ extractorAttributes.put("extractor", "iotdb-extractor");
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("table-name", "test");
+ extractorAttributes.put("start-time", "0");
+ extractorAttributes.put("end-time", "300");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ insertResult =
+ insertResult && TableModelUtils.insertData("test1", "test1", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ final AtomicInteger leaderPort = new AtomicInteger(-1);
+ final TShowRegionResp showRegionResp = client.showRegion(new TShowRegionReq());
+ showRegionResp
+ .getRegionInfoList()
+ .forEach(
+ regionInfo -> {
+ if (RegionRoleType.Leader.getRoleType().equals(regionInfo.getRoleType())) {
+ leaderPort.set(regionInfo.getClientRpcPort());
+ }
+ });
+
+ int leaderIndex = -1;
+ for (int i = 0; i < 3; ++i) {
+ if (senderEnv.getDataNodeWrapper(i).getPort() == leaderPort.get()) {
+ leaderIndex = i;
+ try {
+ senderEnv.shutdownDataNode(i);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+ try {
+ TimeUnit.SECONDS.sleep(1);
+ } catch (final InterruptedException ignored) {
+ }
+ try {
+ senderEnv.startDataNode(i);
+ ((AbstractEnv) senderEnv).checkClusterStatusWithoutUnknown();
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+ }
+ }
+ if (leaderIndex == -1) { // ensure the leader is stopped
+ fail();
+ }
+
+ insertResult = TableModelUtils.insertData("test", "test", 200, 300, senderEnv);
+ insertResult =
+ insertResult && TableModelUtils.insertData("test1", "test1", 200, 300, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ TableModelUtils.assertData("test", "test", 0, 300, receiverEnv);
+ }
+
+ try {
+ TestUtils.restartCluster(senderEnv);
+ TestUtils.restartCluster(receiverEnv);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ // Create a new pipe and write new data
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("database-name", "test1");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("table-name", "test1");
+ extractorAttributes.put("start-time", "0");
+ extractorAttributes.put("end-time", "300");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p2", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p2").getCode());
+
+ insertResult = TableModelUtils.insertData("test", "test", 300, 400, senderEnv);
+ insertResult =
+ insertResult && TableModelUtils.insertData("test1", "test1", 300, 400, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertData("test", "test", 0, 301, receiverEnv);
+ TableModelUtils.assertData("test1", "test1", 0, 301, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testPipeAfterRegisterNewDataNode() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ insertResult =
+ insertResult && TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("table-name", "test");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ insertResult =
+ insertResult && TableModelUtils.insertData("test1", "test1", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ try {
+ senderEnv.registerNewDataNode(true);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+
+ final DataNodeWrapper newDataNode =
+ senderEnv.getDataNodeWrapper(senderEnv.getDataNodeWrapperList().size() - 1);
+ insertResult = TableModelUtils.insertData("test", "test", 200, 300, senderEnv, newDataNode);
+ insertResult =
+ insertResult
+ && TableModelUtils.insertData("test1", "test1", 200, 300, senderEnv, newDataNode);
+ if (!insertResult) {
+ return;
+ }
+
+ TableModelUtils.assertData("test", "test", 0, 300, receiverEnv);
+ }
+
+ try {
+ TestUtils.restartCluster(senderEnv);
+ TestUtils.restartCluster(receiverEnv);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ // create a new pipe and write new data
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("database-name", "test1");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("table-name", "test1");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p2", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p2").getCode());
+
+ insertResult = TableModelUtils.insertData("test", "test", 300, 400, senderEnv);
+ insertResult =
+ insertResult && TableModelUtils.insertData("test1", "test1", 300, 400, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertData("test1", "test1", 0, 400, receiverEnv);
+ TableModelUtils.assertData("test", "test", 0, 400, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testCreatePipeWhenRegisteringNewDataNode() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("database-name", "test1");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("table-name", "test1");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final Thread t =
+ new Thread(
+ () -> {
+ for (int i = 0; i < 30; ++i) {
+ try {
+ client.createPipe(
+ new TCreatePipeReq("p" + i, connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ } catch (final TException e) {
+ // Not sure if the "createPipe" has succeeded
+ e.printStackTrace();
+ return;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (final Exception ignored) {
+ }
+ }
+ });
+ t.start();
+ try {
+ senderEnv.registerNewDataNode(true);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+ t.join();
+ }
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(30, showPipeResult.size());
+ }
+ }
+
+ @Test
+ public void testRegisteringNewDataNodeWhenTransferringData() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("table-name", "test");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ final AtomicInteger succeedNum = new AtomicInteger(0);
+ final Thread t =
+ new Thread(
+ () -> {
+ try {
+ for (int i = 100; i < 200; ++i) {
+ if (TableModelUtils.insertDataNotThrowError(
+ "test", "test", i, i + 1, senderEnv)) {
+ succeedNum.incrementAndGet();
+ Thread.sleep(100);
+ }
+ }
+ } catch (final InterruptedException ignored) {
+ }
+ });
+ t.start();
+ try {
+ senderEnv.registerNewDataNode(true);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+ t.join();
+ if (!TestUtils.tryExecuteNonQueryWithRetry(senderEnv, "flush")) {
+ return;
+ }
+
+ TableModelUtils.assertCountData("test", "test", succeedNum.get() + 100, receiverEnv);
+
+ try {
+ senderEnv.shutdownDataNode(senderEnv.getDataNodeWrapperList().size() - 1);
+ senderEnv.getDataNodeWrapperList().remove(senderEnv.getDataNodeWrapperList().size() - 1);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Test
+ public void testRegisteringNewDataNodeAfterTransferringData() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("table-name", "test");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ int succeedNum = 0;
+ for (int i = 100; i < 200; ++i) {
+ if (TableModelUtils.insertDataNotThrowError("test", "test", i, i + 1, senderEnv)) {
+ succeedNum++;
+ }
+ }
+
+ try {
+ senderEnv.registerNewDataNode(true);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+
+ TableModelUtils.assertCountData("test", "test", succeedNum + 100, receiverEnv);
+
+ try {
+ senderEnv.shutdownDataNode(senderEnv.getDataNodeWrapperList().size() - 1);
+ senderEnv.getDataNodeWrapperList().remove(senderEnv.getDataNodeWrapperList().size() - 1);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Test
+ public void testSenderRestartWhenTransferring() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("table-name", "test");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ }
+
+ int succeedNum = 0;
+ for (int i = 100; i < 200; ++i) {
+ if (TableModelUtils.insertDataNotThrowError("test", "test", i, i + 1, senderEnv)) {
+ succeedNum++;
+ }
+ }
+ if (!TestUtils.tryExecuteNonQueryWithRetry(senderEnv, "flush")) {
+ return;
+ }
+
+ try {
+ TestUtils.restartCluster(senderEnv);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+
+ TableModelUtils.assertCountData("test", "test", succeedNum + 100, receiverEnv);
+ }
+
+ @Test
+ public void testConcurrentlyCreatePipeOfSameName() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("table-name", "test");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final AtomicInteger successCount = new AtomicInteger(0);
+ final List<Thread> threads = new ArrayList<>();
+ for (int i = 0; i < 10; ++i) {
+ final Thread t =
+ new Thread(
+ () -> {
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+ successCount.incrementAndGet();
+ }
+ } catch (final InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } catch (final TException | ClientManagerException | IOException e) {
+ e.printStackTrace();
+ } catch (final Exception e) {
+ // Fail iff pipe exception occurs
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ });
+ t.start();
+ threads.add(t);
+ }
+
+ for (Thread t : threads) {
+ t.join();
+ }
+ Assert.assertEquals(1, successCount.get());
+
+ successCount.set(0);
+ for (int i = 0; i < 10; ++i) {
+ final Thread t =
+ new Thread(
+ () -> {
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final TSStatus status = client.dropPipe("p1");
+ if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+ successCount.incrementAndGet();
+ }
+ } catch (final InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } catch (final TException | ClientManagerException | IOException e) {
+ e.printStackTrace();
+ } catch (final Exception e) {
+ // Fail iff pipe exception occurs
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ });
+ t.start();
+ threads.add(t);
+ }
+ for (final Thread t : threads) {
+ t.join();
+ }
+
+ // Assert at least 1 drop operation succeeds
+ Assert.assertTrue(successCount.get() >= 1);
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(0, showPipeResult.size());
+ }
+ }
+
+ @Test
+ public void testCreate10PipesWithSameConnector() throws Exception {
+ testCreatePipesWithSameConnector(10);
+ }
+
+ @Test
+ public void testCreate50PipesWithSameConnector() throws Exception {
+ testCreatePipesWithSameConnector(50);
+ }
+
+ @Test
+ public void testCreate100PipesWithSameConnector() throws Exception {
+ testCreatePipesWithSameConnector(100);
+ }
+
+ private void testCreatePipesWithSameConnector(final int pipeCount) throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("table-name", "test");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final AtomicInteger successCount = new AtomicInteger(0);
+ final List<Thread> threads = new ArrayList<>();
+ for (int i = 0; i < pipeCount; ++i) {
+ final int finalI = i;
+ final Thread t =
+ new Thread(
+ () -> {
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p" + finalI, connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ successCount.incrementAndGet();
+ } catch (final InterruptedException e) {
+ e.printStackTrace();
+ Thread.currentThread().interrupt();
+ } catch (final TException | ClientManagerException | IOException e) {
+ e.printStackTrace();
+ } catch (final Exception e) {
+ // Fail iff pipe exception occurs
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ });
+ t.start();
+ threads.add(t);
+ }
+ for (final Thread t : threads) {
+ t.join();
+ }
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(successCount.get(), showPipeResult.size());
+ showPipeResult =
+ client.showPipe(new TShowPipeReq().setPipeName("p1").setWhereClause(true)).pipeInfoList;
+ Assert.assertEquals(successCount.get(), showPipeResult.size());
+ }
+ }
+
+ @Test
+ public void testNegativeTimestamp() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", -100, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor", "iotdb-extractor");
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("table-name", "test");
+
+ processorAttributes.put("processor", "do-nothing-processor");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TableModelUtils.assertData("test", "test", -100, 100, receiverEnv);
+
+ insertResult = TableModelUtils.insertData("test", "test", -200, -100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertData("test", "test", -200, 100, receiverEnv);
+ }
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeConnectorCompressionIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeConnectorCompressionIT.java
new file mode 100644
index 00000000000..48af0784992
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeConnectorCompressionIT.java
@@ -0,0 +1,326 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.confignode.rpc.thrift.TShowPipeInfo;
+import org.apache.iotdb.confignode.rpc.thrift.TShowPipeReq;
+import org.apache.iotdb.consensus.ConsensusFactory;
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.it.env.MultiEnvFactory;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.fail;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeConnectorCompressionIT extends AbstractPipeTableModelTestIT {
+
+ @Override
+ @Before
+ public void setUp() {
+ // Override to enable air-gap
+ MultiEnvFactory.createEnv(2);
+ senderEnv = MultiEnvFactory.getEnv(0);
+ receiverEnv = MultiEnvFactory.getEnv(1);
+
+ senderEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS);
+ receiverEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setPipeAirGapReceiverEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS);
+
+ // 10 min, assert that the operations will not time out
+ senderEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+ receiverEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+
+ senderEnv.initClusterEnvironment();
+ receiverEnv.initClusterEnvironment();
+ }
+
+ @Test
+ public void testCompression1() throws Exception {
+ doTest("iotdb-thrift-connector", "stream", true, "snappy");
+ }
+
+ @Test
+ public void testCompression2() throws Exception {
+ doTest("iotdb-thrift-connector", "batch", true, "snappy, lzma2");
+ }
+
+ @Test
+ public void testCompression3() throws Exception {
+ doTest("iotdb-thrift-sync-connector", "stream", false, "snappy, snappy");
+ }
+
+ @Test
+ public void testCompression4() throws Exception {
+ doTest("iotdb-thrift-sync-connector", "batch", true, "gzip, zstd");
+ }
+
+ @Test
+ public void testCompression5() throws Exception {
+ doTest("iotdb-air-gap-connector", "stream", false, "lzma2, lz4");
+ }
+
+ @Test
+ public void testCompression6() throws Exception {
+ doTest("iotdb-air-gap-connector", "batch", true, "lzma2");
+ }
+
+ private void doTest(
+ String connectorType, String realtimeMode, boolean useBatchMode, String compressionTypes)
+ throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort =
+ connectorType.contains("air-gap")
+ ? receiverDataNode.getPipeAirGapReceiverPort()
+ : receiverDataNode.getPort();
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.insertData("test", "test", 0, 50, senderEnv, true);
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db.d1(time, s1) values (2010-01-01T10:00:00+08:00, 1)",
+ "insert into root.db.d1(time, s1) values (2010-01-02T10:00:00+08:00, 2)",
+ "flush"))) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor", "iotdb-extractor");
+ extractorAttributes.put("extractor.realtime.mode", realtimeMode);
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("capture.tree", "true");
+
+ processorAttributes.put("processor", "do-nothing-processor");
+
+ connectorAttributes.put("connector", connectorType);
+ connectorAttributes.put("connector.batch.enable", useBatchMode ? "true" : "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+ connectorAttributes.put("connector.user", "root");
+ connectorAttributes.put("connector.password", "root");
+ connectorAttributes.put("connector.compressor", compressionTypes);
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(*) from root.**",
+ "count(root.db.d1.s1),",
+ Collections.singleton("2,"));
+
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db.d1(time, s1) values (3, 3)",
+ "insert into root.db.d1(time, s1) values (4, 4)",
+ "insert into root.db.d1(time, s1) values (5, 5)",
+ "insert into root.db.d1(time, s1) values (6, 6)",
+ "insert into root.db.d1(time, s1) values (7, 7)",
+ "insert into root.db.d1(time, s1) values (8, 8)",
+ "flush"))) {
+ return;
+ }
+
+ TableModelUtils.insertData("test", "test", 50, 100, senderEnv, true);
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(*) from root.**",
+ "count(root.db.d1.s1),",
+ Collections.singleton("8,"));
+
+ TableModelUtils.assertCountData("test", "test", 100, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testZstdCompressorLevel() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db.d1(time, s1) values (1, 1)",
+ "insert into root.db.d1(time, s2) values (1, 1)",
+ "insert into root.db.d1(time, s3) values (1, 1)",
+ "insert into root.db.d1(time, s4) values (1, 1)",
+ "insert into root.db.d1(time, s5) values (1, 1)",
+ "flush"))) {
+ return;
+ }
+
+ // Create 5 pipes with different zstd compression levels, p4 and p5 should fail.
+
+ try (final Connection connection = senderEnv.getConnection();
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p1"
+ + " with extractor ('extractor.pattern'='root.db.d1.s1','table-name'='test1','capture.table'='true','capture.tree'='true')"
+ + " with connector ("
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.compressor'='zstd, zstd',"
+ + "'connector.compressor.zstd.level'='3',"
+ + "'connector.rate-limit-bytes-per-second'='2048.0')",
+ receiverIp, receiverPort));
+ } catch (SQLException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ try (final Connection connection = senderEnv.getConnection();
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p2"
+ + " with extractor ('extractor.pattern'='root.db.d1.s2','table-name'='test2','capture.table'='true','capture.tree'='true')"
+ + " with connector ("
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.compressor'='zstd, zstd',"
+ + "'connector.compressor.zstd.level'='22')",
+ receiverIp, receiverPort));
+ } catch (SQLException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ try (final Connection connection = senderEnv.getConnection();
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p3"
+ + " with extractor ('extractor.pattern'='root.db.d1.s3','table-name'='test3','capture.table'='true','capture.tree'='true')"
+ + " with connector ("
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.compressor'='zstd, zstd',"
+ + "'connector.compressor.zstd.level'='-131072')",
+ receiverIp, receiverPort));
+ } catch (SQLException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ try (final Connection connection = senderEnv.getConnection();
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p4"
+ + " with extractor ('extractor.pattern'='root.db.d1.s4','table-name'='test4','capture.table'='true','capture.tree'='true')"
+ + " with connector ("
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.compressor'='zstd, zstd',"
+ + "'connector.compressor.zstd.level'='-131073')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException e) {
+ // Make sure the error message in IoTDBConnector.java is returned
+ Assert.assertTrue(e.getMessage().contains("Zstd compression level should be in the range"));
+ }
+
+ try (final Connection connection = senderEnv.getConnection();
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p5"
+ + " with extractor ('extractor.pattern'='root.db.d1.s5','table-name'='test5','capture.table'='true','capture.tree'='true')"
+ + " with connector ("
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.compressor'='zstd, zstd',"
+ + "'connector.compressor.zstd.level'='23')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException e) {
+ // Make sure the error message in IoTDBConnector.java is returned
+ Assert.assertTrue(e.getMessage().contains("Zstd compression level should be in the range"));
+ }
+
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(3, showPipeResult.size());
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test");
+ TableModelUtils.insertData("test", "test1", 0, 50, senderEnv, true);
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test2", "test");
+ TableModelUtils.insertData("test", "test2", 0, 50, senderEnv, true);
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test3", "test");
+ TableModelUtils.insertData("test", "test3", 0, 50, senderEnv, true);
+
+ TableModelUtils.assertCountData("test", "test1", 50, receiverEnv);
+ }
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeConnectorParallelIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeConnectorParallelIT.java
new file mode 100644
index 00000000000..674bf03c2be
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeConnectorParallelIT.java
@@ -0,0 +1,99 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeConnectorParallelIT extends AbstractPipeTableModelTestIT {
+ @Test
+ public void testIoTConnectorParallel() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ final Set<String> expectedResSet = new HashSet<>();
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "true");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+ connectorAttributes.put("connector.parallel.tasks", "3");
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("testPipe", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("testPipe").getCode());
+
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.sg1.d1(time, s1) values (0, 1)",
+ "insert into root.sg1.d1(time, s1) values (1, 2)",
+ "insert into root.sg1.d1(time, s1) values (2, 3)",
+ "insert into root.sg1.d1(time, s1) values (3, 4)",
+ "flush"))) {
+ return;
+ }
+ TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ expectedResSet.add("0,1.0,");
+ expectedResSet.add("1,2.0,");
+ expectedResSet.add("2,3.0,");
+ expectedResSet.add("3,4.0,");
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv, "select * from root.**", "Time,root.sg1.d1.s1,", expectedResSet);
+
+ TableModelUtils.assertCountData("test", "test", 200, receiverEnv);
+ }
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeDataSinkIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeDataSinkIT.java
new file mode 100644
index 00000000000..5c936041743
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeDataSinkIT.java
@@ -0,0 +1,219 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeDataSinkIT extends AbstractPipeTableModelTestIT {
+ @Test
+ public void testThriftConnectorWithRealtimeFirstDisabled() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.insertData("test", "test", 0, 50, senderEnv, true);
+
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList("insert into root.vehicle.d0(time, s1) values (0, 1)", "flush"))) {
+ return;
+ }
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor.realtime.mode", "log");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("capture.tree", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "true");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+ connectorAttributes.put("connector.realtime-first", "false");
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("testPipe", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("testPipe").getCode());
+
+ TableModelUtils.insertData("test", "test", 50, 100, senderEnv, true);
+
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList("insert into root.vehicle.d0(time, s1) values (1, 1)", "flush"))) {
+ return;
+ }
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select * from root.**",
+ "Time,root.vehicle.d0.s1,",
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList("0,1.0,", "1,1.0,"))));
+
+ TableModelUtils.assertCountData("test", "test", 100, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testSinkTabletFormat() throws Exception {
+ testSinkFormat("tablet");
+ }
+
+ // table model not support
+ @Ignore
+ @Test
+ public void testSinkTsFileFormat() throws Exception {
+ testSinkFormat("tsfile");
+ }
+
+ @Test
+ public void testSinkHybridFormat() throws Exception {
+ testSinkFormat("hybrid");
+ }
+
+ private void testSinkFormat(final String format) throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.insertData("test", "test", 0, 50, senderEnv, true);
+
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList("insert into root.vehicle.d0(time, s1) values (1, 1)", "flush"))) {
+ return;
+ }
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor.realtime.mode", "forced-log");
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("capture.tree", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "true");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+ connectorAttributes.put("connector.format", format);
+ connectorAttributes.put("connector.realtime-first", "false");
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+ client
+ .createPipe(
+ new TCreatePipeReq("testPipe", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes))
+ .getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("testPipe").getCode());
+
+ TableModelUtils.insertData("test", "test", 100, 150, senderEnv, true);
+
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList("insert into root.vehicle.d0(time, s1) values (2, 1)", "flush"))) {
+ return;
+ }
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select * from root.**",
+ "Time,root.vehicle.d0.s1,",
+ Collections.unmodifiableSet(new HashSet<>(Arrays.asList("1,1.0,", "2,1.0,"))));
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.dropPipe("testPipe").getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+ client
+ .createPipe(
+ new TCreatePipeReq("testPipe", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes))
+ .getCode());
+
+ // Do not fail if the failure has nothing to do with pipe
+ // Because the failures will randomly generate due to resource limitation
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.vehicle.d0(time, s1) values (4, 1)",
+ "insert into root.vehicle.d0(time, s1) values (3, 1), (0, 1)",
+ "flush"))) {
+ return;
+ }
+
+ TableModelUtils.insertData("test", "test", 150, 200, senderEnv, true);
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select * from root.**",
+ "Time,root.vehicle.d0.s1,",
+ Collections.unmodifiableSet(
+ new HashSet<>(Arrays.asList("0,1.0,", "1,1.0,", "2,1.0,", "3,1.0,", "4,1.0,"))));
+
+ TableModelUtils.assertCountData("test", "test", 150, receiverEnv);
+ }
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeExtractorIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeExtractorIT.java
new file mode 100644
index 00000000000..71389c7957f
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeExtractorIT.java
@@ -0,0 +1,921 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.consensus.ConsensusFactory;
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.it.env.MultiEnvFactory;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.itbase.env.BaseEnv;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.fail;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeExtractorIT extends AbstractPipeTableModelTestIT {
+
+ @Before
+ public void setUp() {
+ MultiEnvFactory.createEnv(2);
+ senderEnv = MultiEnvFactory.getEnv(0);
+ receiverEnv = MultiEnvFactory.getEnv(1);
+
+ // TODO: delete ratis configurations
+ senderEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ // Disable sender compaction for tsfile determination in loose range test
+ .setEnableSeqSpaceCompaction(false)
+ .setEnableUnseqSpaceCompaction(false)
+ .setEnableCrossSpaceCompaction(false);
+ receiverEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS);
+
+ // 10 min, assert that the operations will not time out
+ senderEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+ receiverEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+
+ senderEnv.initClusterEnvironment();
+ receiverEnv.initClusterEnvironment();
+ }
+
+ @Test
+ public void testMatchingMultipleDatabases() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+ boolean insertResult = true;
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor.capture.table", "true");
+ extractorAttributes.put("extractor.capture.tree", "true");
+ extractorAttributes.put("extractor.database-name", "test");
+ extractorAttributes.put("extractor.table-name", "test");
+ extractorAttributes.put("extractor.pattern", "root.db1");
+ extractorAttributes.put("extractor.inclusion", "data.insert");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ assertTimeseriesCountOnReceiver(receiverEnv, 0);
+
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db1.d1 (time, at1) values (1, 10)",
+ "insert into root.db2.d1 (time, at1) values (1, 20)",
+ "flush"))) {
+ return;
+ }
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ insertResult =
+ insertResult && TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ extractorAttributes.replace("extractor.pattern", "root.db2");
+ extractorAttributes.replace("extractor.table-name", "test1");
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p2", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p2").getCode());
+ assertTimeseriesCountOnReceiver(receiverEnv, 2);
+
+ Thread.sleep(10000);
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.dropPipe("p1").getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.dropPipe("p2").getCode());
+
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db1.d1 (time, at1) values (2, 11)",
+ "insert into root.db2.d1 (time, at1) values (2, 21)",
+ "flush"))) {
+ return;
+ }
+
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ insertResult =
+ insertResult && TableModelUtils.insertData("test1", "test1", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ extractorAttributes.remove("extractor.pattern"); // no pattern, will match all databases
+ extractorAttributes.remove("extractor.table-name");
+ extractorAttributes.remove("extractor.database-name");
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p3", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p3").getCode());
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(*) from root.**",
+ "count(root.db1.d1.at1),count(root.db2.d1.at1),",
+ Collections.singleton("2,2,"));
+
+ TableModelUtils.assertCountData("test", "test", 200, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testHistoryAndRealtime() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db.d1 (time, at1) values (1, 10)",
+ "insert into root.db.d2 (time, at1) values (1, 20)",
+ "insert into root.db.d3 (time, at1) values (1, 30)",
+ "insert into root.db.d4 (time, at1) values (1, 40)",
+ "flush"))) {
+ return;
+ }
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test");
+ insertResult = TableModelUtils.insertData("test", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test2", "test");
+ insertResult = TableModelUtils.insertData("test", "test2", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test3", "test3");
+ insertResult = TableModelUtils.insertData("test", "test3", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test4", "test");
+ insertResult = TableModelUtils.insertData("test", "test4", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ extractorAttributes.put("extractor.capture.table", "true");
+ extractorAttributes.put("extractor.capture.tree", "true");
+ extractorAttributes.put("extractor.database", "test");
+ extractorAttributes.put("extractor.table", "test2");
+ extractorAttributes.put("extractor.inclusion", "data.insert");
+ extractorAttributes.put("extractor.pattern", "root.db.d2");
+ extractorAttributes.put("extractor.history.enable", "false");
+ extractorAttributes.put("extractor.realtime.enable", "true");
+ TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p2", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p2").getCode());
+
+ extractorAttributes.replace("extractor.table-name", "test3");
+ extractorAttributes.replace("extractor.pattern", "root.db.d3");
+ extractorAttributes.replace("extractor.history.enable", "true");
+ extractorAttributes.replace("extractor.realtime.enable", "false");
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p3", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p3").getCode());
+
+ extractorAttributes.replace("extractor.table-name", "test4");
+ extractorAttributes.replace("extractor.pattern", "root.db.d4");
+ extractorAttributes.replace("extractor.history.enable", "true");
+ extractorAttributes.replace("extractor.realtime.enable", "true");
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p4", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p4").getCode());
+
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db.d1 (time, at1) values (2, 11)",
+ "insert into root.db.d2 (time, at1) values (2, 21)",
+ "insert into root.db.d3 (time, at1) values (2, 31)",
+ "insert into root.db.d4 (time, at1) values (2, 41), (3, 51)"))) {
+ return;
+ }
+
+ insertResult = TableModelUtils.insertData("test", "test2", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test", "test3", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test", "test4", 0, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(*) from root.** where time <= 1",
+ "count(root.db.d4.at1),count(root.db.d2.at1),count(root.db.d3.at1),",
+ Collections.singleton("1,0,1,"));
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(*) from root.** where time >= 2",
+ "count(root.db.d4.at1),count(root.db.d2.at1),count(root.db.d3.at1),",
+ Collections.singleton("2,1,0,"));
+
+ TableModelUtils.assertData("test", "test2", 100, 200, receiverEnv);
+
+ TableModelUtils.assertData("test", "test3", 100, 200, receiverEnv);
+
+ TableModelUtils.assertData("test", "test4", 100, 200, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testHistoryStartTimeAndEndTimeWorkingWithOrWithoutPattern() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db.d1 (time, at1)"
+ + " values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)",
+ "insert into root.db.d2 (time, at1)"
+ + " values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)",
+ "flush"))) {
+ return;
+ }
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test");
+ insertResult = TableModelUtils.insertData("test", "test1", 0, 10, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test2", "test");
+ insertResult = TableModelUtils.insertData("test", "test2", 0, 10, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ // wait for flush to complete
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(senderEnv, Collections.singletonList("flush"))) {
+ return;
+ }
+ Thread.sleep(10000);
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor.capture.table", "true");
+ extractorAttributes.put("extractor.capture.tree", "true");
+ extractorAttributes.put("extractor.database-name", "test");
+ extractorAttributes.put("extractor.table-name", "test1");
+ extractorAttributes.put("extractor.pattern", "root.db.d1");
+ extractorAttributes.put("extractor.inclusion", "data.insert");
+ extractorAttributes.put("extractor.history.enable", "true");
+ // 1970-01-01T08:00:02+08:00
+ extractorAttributes.put("extractor.history.start-time", "2");
+ extractorAttributes.put("extractor.history.end-time", "3");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(*) from root.**",
+ "count(root.db.d1.at1),",
+ Collections.singleton("2,"));
+
+ extractorAttributes.remove("extractor.table-name");
+ extractorAttributes.remove("extractor.pattern");
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p2", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p2").getCode());
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(*) from root.**",
+ "count(root.db.d1.at1),count(root.db.d2.at1),",
+ Collections.singleton("2,2,"));
+
+ TableModelUtils.assertData("test", "test1", 2, 4, receiverEnv);
+ TableModelUtils.assertData("test", "test2", 2, 4, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testExtractorTimeRangeMatch() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ // insert history data
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db.d1 (time, at1)"
+ + " values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)",
+ "insert into root.db.d2 (time, at1)"
+ + " values (6, 6), (7, 7), (8, 8), (9, 9), (10, 10)",
+ "flush"))) {
+ return;
+ }
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test");
+ insertResult = TableModelUtils.insertData("test", "test1", 0, 10, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test2", "test");
+ insertResult = TableModelUtils.insertData("test", "test2", 0, 10, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ extractorAttributes.put("extractor.capture.table", "true");
+ extractorAttributes.put("extractor.capture.tree", "true");
+ extractorAttributes.put("source.inclusion", "data.insert");
+ extractorAttributes.put("source.start-time", "2");
+ extractorAttributes.put("source.end-time", "4");
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(*) from root.**",
+ "count(root.db.d1.at1),",
+ Collections.singleton("3,"));
+
+ TableModelUtils.assertCountData("test", "test1", 3, receiverEnv);
+
+ // Insert realtime data that overlapped with time range
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db.d3 (time, at1)"
+ + " values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)",
+ "flush"))) {
+ return;
+ }
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test3", "test");
+ insertResult = TableModelUtils.insertData("test", "test3", 0, 5, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(*) from root.**",
+ "count(root.db.d1.at1),count(root.db.d3.at1),",
+ Collections.singleton("3,3,"));
+
+ TableModelUtils.assertCountData("test", "test1", 3, receiverEnv);
+ TableModelUtils.assertCountData("test", "test3", 3, receiverEnv);
+
+ // Insert realtime data that does not overlap with time range
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db.d4 (time, at1)"
+ + " values (6, 6), (7, 7), (8, 8), (9, 9), (10, 10)",
+ "flush"))) {
+ return;
+ }
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test4", "test");
+ insertResult = TableModelUtils.insertData("test", "test4", 6, 10, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TestUtils.assertDataAlwaysOnEnv(
+ receiverEnv,
+ "select count(*) from root.**",
+ "count(root.db.d1.at1),count(root.db.d3.at1),",
+ Collections.singleton("3,3,"));
+
+ TableModelUtils.assertCountData("test", "test1", 3, receiverEnv);
+ TableModelUtils.assertCountData("test", "test3", 3, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testSourceStartTimeAndEndTimeWorkingWithOrWithoutPattern() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db.d1 (time, at1)"
+ + " values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)",
+ "insert into root.db.d2 (time, at1)"
+ + " values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)",
+ "flush"))) {
+ return;
+ }
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test");
+ insertResult = TableModelUtils.insertData("test", "test1", 0, 5, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test2", "test");
+ insertResult = TableModelUtils.insertData("test", "test2", 0, 5, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("source.capture.table", "true");
+ extractorAttributes.put("source.capture.tree", "true");
+ extractorAttributes.put("source.pattern", "root.db.d1");
+ extractorAttributes.put("source.table-name", "test1");
+ extractorAttributes.put("source.inclusion", "data.insert");
+ extractorAttributes.put("source.start-time", "2");
+ // 1970-01-01T08:00:04+08:00
+ extractorAttributes.put("source.end-time", "4");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(*) from root.**",
+ "count(root.db.d1.at1),",
+ Collections.singleton("3,"));
+ TableModelUtils.assertCountData("test", "test1", 3, receiverEnv);
+
+ extractorAttributes.remove("source.pattern");
+ extractorAttributes.remove("source.table-name");
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p2", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p2").getCode());
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(*) from root.**",
+ "count(root.db.d1.at1),count(root.db.d2.at1),",
+ Collections.singleton("3,3,"));
+
+ TableModelUtils.assertCountData("test", "test1", 3, receiverEnv);
+ TableModelUtils.assertCountData("test", "test2", 3, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testHistoryLooseRange() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test");
+ insertResult = TableModelUtils.insertData("test", "test1", 0, 2, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test2", "test");
+ insertResult = TableModelUtils.insertData("test", "test2", 0, 2, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ // TsFile 1, extracted without parse
+ "insert into root.db.d1 (time, at1, at2)" + " values (1, 1, 2), (2, 3, 4)",
+ // TsFile 2, not extracted because pattern not overlapped
+ "insert into root.db1.d1 (time, at1, at2)" + " values (1, 1, 2), (2, 3, 4)",
+ "flush"))) {
+ return;
+ }
+
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ // TsFile 3, not extracted because time range not overlapped
+ "insert into root.db.d1 (time, at1, at2)" + " values (3, 1, 2), (4, 3, 4)",
+ "flush"))) {
+ return;
+ }
+
+ insertResult = TableModelUtils.insertData("test", "test1", 2, 5, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ // wait for flush to complete
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(senderEnv, Collections.singletonList("flush"))) {
+ return;
+ }
+ Thread.sleep(10000);
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("source.capture.table", "true");
+ extractorAttributes.put("source.capture.tree", "true");
+ extractorAttributes.put("source.table-name", "test1");
+ extractorAttributes.put("source.path", "root.db.d1.at1");
+ extractorAttributes.put("source.inclusion", "data.insert");
+ extractorAttributes.put("source.history.start-time", "1");
+ extractorAttributes.put("source.history.end-time", "2");
+ extractorAttributes.put("source.history.loose-range", "time, path");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(*) from root.** group by level=0",
+ "count(root.*.*.*),",
+ Collections.singleton("4,"));
+
+ TableModelUtils.assertCountData("test", "test1", 3, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testRealtimeLooseRange() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ boolean insertResult = true;
+ insertResult = TableModelUtils.insertData("test", "test2", 0, 20, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("source.capture.table", "true");
+ extractorAttributes.put("source.capture.tree", "true");
+ extractorAttributes.put("source.table-name", "test1");
+ extractorAttributes.put("source.path", "root.db.d1.at1");
+ extractorAttributes.put("source.inclusion", "data.insert");
+ extractorAttributes.put("source.realtime.loose-range", "time, path");
+ extractorAttributes.put("source.start-time", "2");
+ extractorAttributes.put("source.end-time", "10");
+ extractorAttributes.put("source.realtime.mode", "batch");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db.d1 (time, at1, at2)" + " values (1, 1, 2), (3, 3, 4)",
+ "flush"))) {
+ return;
+ }
+
+ if (!insertResult) {
+ return;
+ }
+
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ senderEnv,
+ Arrays.asList(
+ "insert into root.db.d1 (time, at1)" + " values (5, 1), (16, 3)",
+ "insert into root.db.d1 (time, at1, at2)" + " values (5, 1, 2), (6, 3, 4)",
+ "flush"))) {
+ return;
+ }
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "select count(at1) from root.db.d1 where time >= 2 and time <= 10",
+ new HashMap<String, String>() {
+ {
+ put("count(root.db.d1.at1)", "3");
+ }
+ });
+
+ insertResult = TableModelUtils.insertData("test", "test1", 10, 20, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test", "test2", 10, 20, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ TableModelUtils.assertCountData("test", "test1", 20, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testTableModeSQLSupportNowFunc() {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ final String p1 =
+ String.format(
+ "create pipe p1"
+ + " with extractor ("
+ + "'capture.table'='true',"
+ + "'extractor.history.enable'='true',"
+ + "'source.start-time'='now',"
+ + "'source.end-time'='now',"
+ + "'source.history.start-time'='now',"
+ + "'source.history.end-time'='now')"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort);
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(p1);
+ } catch (final SQLException e) {
+ fail(e.getMessage());
+ }
+
+ final String p2 =
+ String.format(
+ "create pipe p2"
+ + " with extractor ("
+ + "'capture.table'='true',"
+ + "'extractor.history.enable'='true',"
+ + "'start-time'='now',"
+ + "'end-time'='now',"
+ + "'history.start-time'='now',"
+ + "'history.end-time'='now')"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort);
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(p2);
+ } catch (final SQLException e) {
+ fail(e.getMessage());
+ }
+
+ final String p3 =
+ String.format(
+ "create pipe p3"
+ + " with extractor ("
+ + "'capture.table'='true',"
+ + "'extractor.history.enable'='true',"
+ + "'extractor.start-time'='now',"
+ + "'extractor.end-time'='now',"
+ + "'extractor.history.start-time'='now',"
+ + "'extractor.history.end-time'='now')"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort);
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(p3);
+ } catch (final SQLException e) {
+ fail(e.getMessage());
+ }
+
+ String alterP3 =
+ "alter pipe p3"
+ + " modify extractor ("
+ + "'history.enable'='true',"
+ + "'start-time'='now',"
+ + "'end-time'='now',"
+ + "'history.start-time'='now',"
+ + "'history.end-time'='now')";
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(alterP3);
+ } catch (final SQLException e) {
+ fail(e.getMessage());
+ }
+
+ alterP3 =
+ "alter pipe p3"
+ + " modify extractor ("
+ + "'extractor.history.enable'='true',"
+ + "'extractor.start-time'='now',"
+ + "'extractor.end-time'='now',"
+ + "'extractor.history.start-time'='now',"
+ + "'extractor.history.end-time'='now')";
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(alterP3);
+ } catch (final SQLException e) {
+ fail(e.getMessage());
+ }
+
+ alterP3 =
+ "alter pipe p3"
+ + " modify source ("
+ + "'extractor.history.enable'='true',"
+ + "'source.start-time'='now',"
+ + "'source.end-time'='now',"
+ + "'source.history.start-time'='now',"
+ + "'source.history.end-time'='now')";
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(alterP3);
+ } catch (final SQLException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ private void assertTimeseriesCountOnReceiver(BaseEnv receiverEnv, int count) {
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv, "count timeseries", "count(timeseries),", Collections.singleton(count + ","));
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeLifeCycleIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeLifeCycleIT.java
new file mode 100644
index 00000000000..3c52a1de726
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeLifeCycleIT.java
@@ -0,0 +1,642 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeLifeCycleIT extends AbstractPipeTableModelTestIT {
+ @Test
+ public void testLifeCycleWithHistoryEnabled() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 200, receiverEnv);
+
+ insertResult = TableModelUtils.insertData("test", "test", 200, 300, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 300, receiverEnv);
+
+ insertResult = TableModelUtils.insertData("test", "test", 300, 400, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 400, receiverEnv);
+
+ insertResult = TableModelUtils.insertData("test", "test", 400, 500, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 500, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testLifeCycleWithHistoryDisabled() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ // wait for flush to complete
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(senderEnv, Collections.singletonList("flush"))) {
+ return;
+ }
+ Thread.sleep(10000);
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("extractor.inclusion", "data.insert");
+ extractorAttributes.put("extractor.inclusion.exclusion", "");
+
+ extractorAttributes.put("extractor.history.enable", "false");
+ // start-time and end-time should not work
+ extractorAttributes.put("extractor.history.start-time", "0");
+ extractorAttributes.put("extractor.history.end-time", "50");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ insertResult = TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ TableModelUtils.assertCountData("test1", "test1", 100, receiverEnv);
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test2", "test2");
+ insertResult = TableModelUtils.insertData("test2", "test2", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test1", "test1", 100, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testLifeCycleLogMode() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("extractor.mode", "forced-log");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 100, senderEnv);
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ insertResult = TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test1", "test1", 100, senderEnv);
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test2", "test2");
+ insertResult = TableModelUtils.insertData("test2", "test2", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test2", "test2", 100, senderEnv);
+ }
+ }
+
+ @Test
+ public void testLifeCycleFileMode() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("mode.streaming", "false");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 100, receiverEnv);
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ insertResult = TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test1", "test1", 100, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testLifeCycleHybridMode() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("extractor.mode", "hybrid");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 200, receiverEnv);
+
+ insertResult = TableModelUtils.insertData("test", "test", 200, 300, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 300, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testLifeCycleWithClusterRestart() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 200, receiverEnv);
+ }
+
+ try {
+ TestUtils.restartCluster(senderEnv);
+ TestUtils.restartCluster(receiverEnv);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+
+ try (final SyncConfigNodeIServiceClient ignored =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ insertResult = TableModelUtils.insertData("test", "test", 200, 300, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 300, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testReceiverRestartWhenTransferring() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ final AtomicInteger succeedNum = new AtomicInteger(0);
+ final Thread t =
+ new Thread(
+ () -> {
+ try {
+ for (int i = 100; i < 200; ++i) {
+ if (TableModelUtils.insertDataNotThrowError(
+ "test", "test", i, i + 1, senderEnv)) {
+ succeedNum.incrementAndGet();
+ }
+ Thread.sleep(100);
+ }
+ } catch (InterruptedException ignored) {
+ }
+ });
+ t.start();
+
+ try {
+ TestUtils.restartCluster(receiverEnv);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ try {
+ t.interrupt();
+ t.join();
+ } catch (Throwable ignored) {
+ }
+ return;
+ }
+
+ t.join();
+ client.stopPipe("p1");
+ client.startPipe("p1");
+ if (!TestUtils.tryExecuteNonQueryWithRetry(senderEnv, "flush")) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 100 + succeedNum.get(), receiverEnv);
+ }
+ }
+
+ @Test
+ public void testReceiverAlreadyHaveTimeSeries() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(receiverEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, receiverEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 200, receiverEnv);
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.stopPipe("p1").getCode());
+
+ insertResult = TableModelUtils.insertData("test", "test", 200, 300, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 200, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testDoubleLiving() throws Exception {
+ boolean insertResult = true;
+ // Double living is two clusters with pipes connecting each other.
+ final DataNodeWrapper senderDataNode = senderEnv.getDataNodeWrapper(0);
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String senderIp = senderDataNode.getIp();
+ final int senderPort = senderDataNode.getPort();
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ if (!TestUtils.tryExecuteNonQueryWithRetry(senderEnv, "flush")) {
+ return;
+ }
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ // Add this property to avoid to make self cycle.
+ extractorAttributes.put("capture.table", "true");
+ connectorAttributes.put("source.forwarding-pipe-requests", "false");
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ }
+
+ if (!TestUtils.tryExecuteNonQueryWithRetry(senderEnv, "flush")) {
+ return;
+ }
+
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ insertResult = TableModelUtils.insertData("test", "test", 200, 300, receiverEnv);
+ if (!insertResult) {
+ return;
+ }
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) receiverEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ // Add this property to avoid to make self cycle.
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("capture.tree", "true");
+ connectorAttributes.put("source.forwarding-pipe-requests", "false");
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", senderIp);
+ connectorAttributes.put("connector.port", Integer.toString(senderPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ }
+
+ if (!TestUtils.tryExecuteNonQueryWithRetry(receiverEnv, "flush")) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test", "test", 300, 400, receiverEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertData("test", "test", 0, 400, receiverEnv);
+
+ try {
+ TestUtils.restartCluster(senderEnv);
+ TestUtils.restartCluster(receiverEnv);
+ } catch (final Throwable e) {
+ e.printStackTrace();
+ return;
+ }
+
+ insertResult = TableModelUtils.insertData("test", "test", 400, 500, receiverEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ TableModelUtils.assertData("test", "test", 0, 500, receiverEnv);
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeNullValueIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeNullValueIT.java
new file mode 100644
index 00000000000..fb63d43d8d3
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeNullValueIT.java
@@ -0,0 +1,169 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeNullValueIT extends AbstractPipeTableModelTestIT {
+
+ private enum InsertType {
+ SESSION_INSERT_RECORD,
+ SESSION_INSERT_TABLET,
+ SQL_INSERT,
+ }
+
+ private void testInsertNullValueTemplate(
+ final InsertType insertType, final boolean withParsing, final String realtime)
+ throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+
+ if (insertType == InsertType.SESSION_INSERT_TABLET) {
+ TableModelUtils.insertDataByTablet("test", "test", 0, 200, senderEnv, true);
+ } else if (insertType == InsertType.SQL_INSERT) {
+ TableModelUtils.insertData("test", "test", 0, 200, senderEnv, true);
+ }
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("realtime-mode", realtime);
+ if (withParsing) {
+ extractorAttributes.put("start-time", "150");
+ extractorAttributes.put("end-time", "249");
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("table-name", "test");
+ }
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("test", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ }
+
+ if (insertType == InsertType.SESSION_INSERT_TABLET) {
+ TableModelUtils.insertDataByTablet("test", "test", 200, 400, senderEnv, true);
+ } else if (insertType == InsertType.SQL_INSERT) {
+ TableModelUtils.insertData("test", "test", 200, 400, senderEnv, true);
+ }
+
+ if (withParsing) {
+ TableModelUtils.assertCountData("test", "test", 100, receiverEnv);
+ return;
+ }
+ TableModelUtils.assertCountData("test", "test", 400, receiverEnv);
+ }
+
+ // ---------------------- //
+ // Scenario 1: SQL Insert //
+ // ---------------------- //
+ @Test
+ public void testSQLInsertWithParsingForcedLog() throws Exception {
+ testInsertNullValueTemplate(InsertType.SQL_INSERT, true, "forced-log");
+ }
+
+ @Test
+ public void testSQLInsertWithoutParsingForcedLog() throws Exception {
+ testInsertNullValueTemplate(InsertType.SQL_INSERT, false, "forced-log");
+ }
+
+ @Test
+ public void testSQLInsertWithParsingFile() throws Exception {
+ testInsertNullValueTemplate(InsertType.SQL_INSERT, true, "file");
+ }
+
+ @Test
+ public void testSQLInsertWithoutParsingFile() throws Exception {
+ testInsertNullValueTemplate(InsertType.SQL_INSERT, false, "file");
+ }
+
+ @Test
+ public void testSQLInsertWithParsingStream() throws Exception {
+ testInsertNullValueTemplate(InsertType.SQL_INSERT, true, "stream");
+ }
+
+ @Test
+ public void testSQLInsertWithoutParsingStream() throws Exception {
+ testInsertNullValueTemplate(InsertType.SQL_INSERT, false, "stream");
+ }
+
+ // --------------------------------- //
+ // Scenario 2: Session Insert Tablet //
+ // --------------------------------- //
+ @Test
+ public void testSessionInsertTabletWithParsingForcedLog() throws Exception {
+ testInsertNullValueTemplate(InsertType.SESSION_INSERT_TABLET, true, "forced-log");
+ }
+
+ @Test
+ public void testSessionInsertTabletWithoutParsingForcedLog() throws Exception {
+ testInsertNullValueTemplate(InsertType.SESSION_INSERT_TABLET, false, "forced-log");
+ }
+
+ @Test
+ public void testSessionInsertTabletWithParsingFile() throws Exception {
+ testInsertNullValueTemplate(InsertType.SESSION_INSERT_TABLET, true, "file");
+ }
+
+ @Test
+ public void testSessionInsertTabletWithoutParsingFile() throws Exception {
+ testInsertNullValueTemplate(InsertType.SESSION_INSERT_TABLET, false, "file");
+ }
+
+ @Test
+ public void testSessionInsertTabletWithParsingStream() throws Exception {
+ testInsertNullValueTemplate(InsertType.SESSION_INSERT_TABLET, true, "stream");
+ }
+
+ @Test
+ public void testSessionInsertTabletWithoutParsingStream() throws Exception {
+ testInsertNullValueTemplate(InsertType.SESSION_INSERT_TABLET, false, "stream");
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeProtocolIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeProtocolIT.java
new file mode 100644
index 00000000000..fc78f59d826
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeProtocolIT.java
@@ -0,0 +1,466 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.commons.pipe.agent.plugin.builtin.BuiltinPipePlugin;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.consensus.ConsensusFactory;
+import org.apache.iotdb.it.env.MultiEnvFactory;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/** Test pipe's basic functionalities under multiple cluster and consensus protocol settings. */
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeProtocolIT extends AbstractPipeTableModelTestIT {
+ @Override
+ @Before
+ public void setUp() {
+ MultiEnvFactory.createEnv(2);
+ senderEnv = MultiEnvFactory.getEnv(0);
+ receiverEnv = MultiEnvFactory.getEnv(1);
+ }
+
+ private void innerSetUp(
+ final String configNodeConsensus,
+ final String schemaRegionConsensus,
+ final String dataRegionConsensus,
+ final int configNodesNum,
+ final int dataNodesNum,
+ int schemaRegionReplicationFactor,
+ int dataRegionReplicationFactor) {
+ schemaRegionReplicationFactor = Math.min(schemaRegionReplicationFactor, dataNodesNum);
+ dataRegionReplicationFactor = Math.min(dataRegionReplicationFactor, dataNodesNum);
+
+ // TODO: delete ratis configurations
+ senderEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setDataRegionConsensusProtocolClass(dataRegionConsensus)
+ .setSchemaReplicationFactor(schemaRegionReplicationFactor)
+ .setDataReplicationFactor(dataRegionReplicationFactor);
+ receiverEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(configNodeConsensus)
+ .setSchemaRegionConsensusProtocolClass(schemaRegionConsensus)
+ .setDataRegionConsensusProtocolClass(dataRegionConsensus)
+ .setSchemaReplicationFactor(schemaRegionReplicationFactor)
+ .setDataReplicationFactor(dataRegionReplicationFactor);
+
+ // 10 min, assert that the operations will not time out
+ senderEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+ receiverEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+
+ senderEnv.initClusterEnvironment(configNodesNum, dataNodesNum);
+ receiverEnv.initClusterEnvironment(configNodesNum, dataNodesNum);
+ }
+
+ @Test
+ public void test1C1DWithRatisRatisIot() throws Exception {
+ innerSetUp(
+ ConsensusFactory.RATIS_CONSENSUS,
+ ConsensusFactory.RATIS_CONSENSUS,
+ ConsensusFactory.IOT_CONSENSUS,
+ 1,
+ 1,
+ 1,
+ 1);
+ doTest();
+ }
+
+ @Test
+ public void test1C1DWithSimpleSimpleIot() throws Exception {
+ innerSetUp(
+ ConsensusFactory.SIMPLE_CONSENSUS,
+ ConsensusFactory.SIMPLE_CONSENSUS,
+ ConsensusFactory.IOT_CONSENSUS,
+ 1,
+ 1,
+ 1,
+ 1);
+ doTest();
+ }
+
+ @Test
+ public void test1C1DWithRatisRatisSimple() throws Exception {
+ innerSetUp(
+ ConsensusFactory.RATIS_CONSENSUS,
+ ConsensusFactory.RATIS_CONSENSUS,
+ ConsensusFactory.SIMPLE_CONSENSUS,
+ 1,
+ 1,
+ 1,
+ 1);
+ doTest();
+ }
+
+ @Test
+ public void test3C3DWith3SchemaRegionFactor3DataRegionFactor() throws Exception {
+ innerSetUp(
+ ConsensusFactory.RATIS_CONSENSUS,
+ ConsensusFactory.RATIS_CONSENSUS,
+ ConsensusFactory.IOT_CONSENSUS,
+ 3,
+ 3,
+ 3,
+ 3);
+ doTest();
+ }
+
+ @Test
+ public void test3C3DWith3SchemaRegionFactor2DataRegionFactor() throws Exception {
+ innerSetUp(
+ ConsensusFactory.RATIS_CONSENSUS,
+ ConsensusFactory.RATIS_CONSENSUS,
+ ConsensusFactory.IOT_CONSENSUS,
+ 3,
+ 3,
+ 3,
+ 2);
+ doTest();
+ }
+
+ @Test
+ public void testPipeOnBothSenderAndReceiver() throws Exception {
+ senderEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS)
+ .setSchemaReplicationFactor(3)
+ .setDataReplicationFactor(2);
+ receiverEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS)
+ .setSchemaReplicationFactor(1)
+ .setDataReplicationFactor(1);
+
+ // 10 min, assert that the operations will not time out
+ senderEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+ receiverEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+
+ senderEnv.initClusterEnvironment(3, 3);
+ receiverEnv.initClusterEnvironment(1, 1);
+
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("table-name", "test.*");
+ extractorAttributes.put("inclusion", "data.insert");
+ extractorAttributes.put("mode.streaming", "true");
+ extractorAttributes.put("mode.snapshot", "false");
+ extractorAttributes.put("mode.strict", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TableModelUtils.assertCountData("test", "test", 100, receiverEnv);
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.stopPipe("p1").getCode());
+ }
+
+ final DataNodeWrapper senderDataNode = senderEnv.getDataNodeWrapper(0);
+ final String senderIp = senderDataNode.getIp();
+ final int senderPort = senderDataNode.getPort();
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) receiverEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(receiverEnv, "test1", "test1");
+ insertResult = TableModelUtils.insertData("test1", "test1", 0, 100, receiverEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("database-name", "test.*");
+ extractorAttributes.put("table-name", "test.*");
+ extractorAttributes.put("inclusion", "data.insert");
+ extractorAttributes.put("mode.streaming", "true");
+ extractorAttributes.put("mode.snapshot", "false");
+ extractorAttributes.put("mode.strict", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", senderIp);
+ connectorAttributes.put("connector.port", Integer.toString(senderPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TableModelUtils.assertCountData("test1", "test1", 100, senderEnv);
+ }
+ }
+
+ private void doTest() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("database-name", "test.*");
+ extractorAttributes.put("table-name", "test.*");
+ extractorAttributes.put("inclusion", "data.insert");
+ extractorAttributes.put("mode.streaming", "true");
+ extractorAttributes.put("mode.snapshot", "false");
+ extractorAttributes.put("mode.strict", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TableModelUtils.assertData("test", "test", 0, 100, receiverEnv);
+
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertData("test", "test", 0, 200, receiverEnv);
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.stopPipe("p1").getCode());
+
+ insertResult = TableModelUtils.insertData("test", "test", 200, 300, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertData("test", "test", 0, 200, receiverEnv);
+ }
+ }
+
+ @Test
+ public void testSyncConnectorUseNodeUrls() throws Exception {
+ doTestUseNodeUrls(BuiltinPipePlugin.IOTDB_THRIFT_SYNC_CONNECTOR.getPipePluginName());
+ }
+
+ @Test
+ public void testAsyncConnectorUseNodeUrls() throws Exception {
+ doTestUseNodeUrls(BuiltinPipePlugin.IOTDB_THRIFT_ASYNC_CONNECTOR.getPipePluginName());
+ }
+
+ @Test
+ public void testAirGapConnectorUseNodeUrls() throws Exception {
+ doTestUseNodeUrls(BuiltinPipePlugin.IOTDB_AIR_GAP_CONNECTOR.getPipePluginName());
+ }
+
+ private void doTestUseNodeUrls(String connectorName) throws Exception {
+ senderEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setPipeAirGapReceiverEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS)
+ .setSchemaReplicationFactor(1)
+ .setDataReplicationFactor(1)
+ .setEnableSeqSpaceCompaction(false)
+ .setEnableUnseqSpaceCompaction(false)
+ .setEnableCrossSpaceCompaction(false);
+ receiverEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setPipeAirGapReceiverEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS)
+ .setSchemaReplicationFactor(3)
+ .setDataReplicationFactor(2);
+
+ // 10 min, assert that the operations will not time out
+ senderEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+ receiverEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+
+ senderEnv.initClusterEnvironment(1, 1);
+ receiverEnv.initClusterEnvironment(1, 3);
+
+ final StringBuilder nodeUrlsBuilder = new StringBuilder();
+ boolean insertResult = true;
+ for (final DataNodeWrapper wrapper : receiverEnv.getDataNodeWrapperList()) {
+ if (connectorName.equals(BuiltinPipePlugin.IOTDB_AIR_GAP_CONNECTOR.getPipePluginName())) {
+ // Use default port for convenience
+ nodeUrlsBuilder
+ .append(wrapper.getIp())
+ .append(":")
+ .append(wrapper.getPipeAirGapReceiverPort())
+ .append(",");
+ } else {
+ nodeUrlsBuilder.append(wrapper.getIpAndPortString()).append(",");
+ }
+ }
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ connectorAttributes.put("connector", connectorName);
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.batch.max-delay-seconds", "1");
+ connectorAttributes.put("connector.batch.size-bytes", "2048");
+ connectorAttributes.put("connector.node-urls", nodeUrlsBuilder.toString());
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("database-name", "test.*");
+ extractorAttributes.put("table-name", "test.*");
+ extractorAttributes.put("inclusion", "data.insert");
+ extractorAttributes.put("mode.streaming", "true");
+ extractorAttributes.put("mode.snapshot", "false");
+ extractorAttributes.put("mode.strict", "true");
+
+ // Test forced-log mode, in open releases this might be "file"
+ extractorAttributes.put("realtime.mode", "forced-log");
+
+ TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertData("test", "test", 0, 200, receiverEnv);
+
+ insertResult = TableModelUtils.insertData("test", "test", 200, 300, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertData("test", "test", 0, 300, receiverEnv);
+
+ extractorAttributes.replace("realtime.mode", "file");
+
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p2", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p2").getCode());
+
+ insertResult = TableModelUtils.insertData("test", "test", 300, 400, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertData("test", "test", 0, 400, receiverEnv);
+ }
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeSwitchStatusIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeSwitchStatusIT.java
new file mode 100644
index 00000000000..1420b3cec5d
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeSwitchStatusIT.java
@@ -0,0 +1,348 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.confignode.rpc.thrift.TShowPipeInfo;
+import org.apache.iotdb.confignode.rpc.thrift.TShowPipeReq;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeSwitchStatusIT extends AbstractPipeTableModelTestIT {
+ @Test
+ public void testPipeSwitchStatus() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("table-name", "test.*");
+ extractorAttributes.put("inclusion", "data.insert");
+ extractorAttributes.put("mode.streaming", "true");
+ extractorAttributes.put("mode.snapshot", "false");
+ extractorAttributes.put("mode.strict", "true");
+ extractorAttributes.put("start-time", "1");
+ extractorAttributes.put("end-time", "2");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p2", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p3", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("RUNNING")));
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p2") && o.state.equals("RUNNING")));
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p3") && o.state.equals("RUNNING")));
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p2").getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.dropPipe("p3").getCode());
+
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("RUNNING")));
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p2") && o.state.equals("RUNNING")));
+ Assert.assertFalse(showPipeResult.stream().anyMatch((o) -> o.id.equals("p3")));
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.stopPipe("p1").getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.dropPipe("p2").getCode());
+
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("STOPPED")));
+ Assert.assertFalse(showPipeResult.stream().anyMatch((o) -> o.id.equals("p2")));
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.dropPipe("p1").getCode());
+
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertFalse(showPipeResult.stream().anyMatch((o) -> o.id.equals("p1")));
+ }
+ }
+
+ @Test
+ public void testPipeIllegallySwitchStatus() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("table-name", "test.*");
+ extractorAttributes.put("inclusion", "data.insert");
+ extractorAttributes.put("mode.streaming", "true");
+ extractorAttributes.put("mode.snapshot", "false");
+ extractorAttributes.put("mode.strict", "true");
+ extractorAttributes.put("start-time", "1");
+ extractorAttributes.put("end-time", "2");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("RUNNING")));
+
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.PIPE_ERROR.getStatusCode(), status.getCode());
+
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.stream().filter((o) -> o.id.equals("p1")).count());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("RUNNING")));
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.stopPipe("p1").getCode());
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("STOPPED")));
+ Assert.assertEquals(1, showPipeResult.stream().filter((o) -> o.id.equals("p1")).count());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.stopPipe("p1").getCode());
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("STOPPED")));
+ Assert.assertEquals(1, showPipeResult.stream().filter((o) -> o.id.equals("p1")).count());
+ }
+ }
+
+ @Test
+ public void testDropPipeAndCreateAgain() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("table-name", "test.*");
+ extractorAttributes.put("inclusion", "data.insert");
+ extractorAttributes.put("mode.streaming", "true");
+ extractorAttributes.put("mode.snapshot", "false");
+ extractorAttributes.put("mode.strict", "true");
+ extractorAttributes.put("start-time", "0");
+ extractorAttributes.put("end-time", "200");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("RUNNING")));
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.dropPipe("p1").getCode());
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(0, showPipeResult.size());
+
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.stream().filter((o) -> o.id.equals("p1")).count());
+ }
+ }
+
+ @Test
+ public void testWrongPipeName() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("capture.table", "true");
+ extractorAttributes.put("database-name", "test");
+ extractorAttributes.put("table-name", "test.*");
+ extractorAttributes.put("inclusion", "data.insert");
+ extractorAttributes.put("mode.streaming", "true");
+ extractorAttributes.put("mode.snapshot", "false");
+ extractorAttributes.put("mode.strict", "true");
+ extractorAttributes.put("start-time", "0");
+ extractorAttributes.put("end-time", "200");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(TSStatusCode.PIPE_ERROR.getStatusCode(), client.startPipe("").getCode());
+ Assert.assertEquals(
+ TSStatusCode.PIPE_ERROR.getStatusCode(), client.startPipe("p0").getCode());
+ Assert.assertEquals(TSStatusCode.PIPE_ERROR.getStatusCode(), client.startPipe("p").getCode());
+ Assert.assertEquals(TSStatusCode.PIPE_ERROR.getStatusCode(), client.startPipe("*").getCode());
+ List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("RUNNING")));
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("RUNNING")));
+
+ Assert.assertEquals(TSStatusCode.PIPE_ERROR.getStatusCode(), client.stopPipe("").getCode());
+ Assert.assertEquals(TSStatusCode.PIPE_ERROR.getStatusCode(), client.stopPipe("p0").getCode());
+ Assert.assertEquals(TSStatusCode.PIPE_ERROR.getStatusCode(), client.stopPipe("p").getCode());
+ Assert.assertEquals(TSStatusCode.PIPE_ERROR.getStatusCode(), client.stopPipe("*").getCode());
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("RUNNING")));
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.stopPipe("p1").getCode());
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("STOPPED")));
+
+ Assert.assertEquals(
+ TSStatusCode.PIPE_NOT_EXIST_ERROR.getStatusCode(), client.dropPipe("").getCode());
+ Assert.assertEquals(
+ TSStatusCode.PIPE_NOT_EXIST_ERROR.getStatusCode(), client.dropPipe("p0").getCode());
+ Assert.assertEquals(
+ TSStatusCode.PIPE_NOT_EXIST_ERROR.getStatusCode(), client.dropPipe("p").getCode());
+ Assert.assertEquals(
+ TSStatusCode.PIPE_NOT_EXIST_ERROR.getStatusCode(), client.dropPipe("*").getCode());
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertTrue(
+ showPipeResult.stream().anyMatch((o) -> o.id.equals("p1") && o.state.equals("STOPPED")));
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.dropPipe("p1").getCode());
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertFalse(showPipeResult.stream().anyMatch((o) -> o.id.equals("p1")));
+ }
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeSyntaxIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeSyntaxIT.java
new file mode 100644
index 00000000000..24b1c92563b
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeSyntaxIT.java
@@ -0,0 +1,685 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.confignode.rpc.thrift.TShowPipeInfo;
+import org.apache.iotdb.confignode.rpc.thrift.TShowPipeReq;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.itbase.env.BaseEnv;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.fail;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeSyntaxIT extends AbstractPipeTableModelTestIT {
+ @Test
+ public void testValidPipeName() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ final List<String> validPipeNames =
+ Arrays.asList("Pipe_1", "\"`33`\"", "root", "\"`䏿–‡`\"", "\"`with`\"");
+ final List<String> expectedPipeNames =
+ Arrays.asList("Pipe_1", "`33`", "root", "`䏿–‡`", "`with`");
+ for (final String pipeName : validPipeNames) {
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe %s"
+ + " with source ( "
+ + "'capture.table'='true',"
+ + "'database-name'='test',"
+ + "'table-name'='test',"
+ + "'mode.streaming'='true',"
+ + "'mode.strict'='true',"
+ + "'mode.snapshot'='false',"
+ + "'start-time'='1',"
+ + "'end-time'='2')"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ pipeName, receiverIp, receiverPort));
+ } catch (SQLException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ for (final String pipeName : expectedPipeNames) {
+ Assert.assertTrue(
+ showPipeResult.stream()
+ .anyMatch((o) -> o.id.equals(pipeName) && o.state.equals("RUNNING")));
+ }
+
+ for (final String pipeName : validPipeNames) {
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(String.format("drop pipe %s", pipeName));
+ } catch (SQLException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+ }
+
+ showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(0, showPipeResult.size());
+ }
+ }
+
+ @Test
+ public void testRevertParameterOrder() {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p1"
+ + " with source ( "
+ + "'capture.table'='test',"
+ + "'database-name'='test',"
+ + "'table-name'='test',"
+ + "'mode.streaming'='true',"
+ + "'mode.strict'='true',"
+ + "'mode.snapshot'='false',"
+ + "'start-time'='1',"
+ + "'end-time'='2')"
+ + " with connector ("
+ + "'connector.batch.enable'='false', "
+ + "'connector.port'='%s',"
+ + "'connector.ip'='%s',"
+ + "'connector'='iotdb-thrift-connector')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException ignore) {
+ }
+ }
+
+ @Test
+ public void testRevertStageOrder() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p1"
+ + " with connector ("
+ + "'connector.batch.enable'='false', "
+ + "'connector.port'='%s',"
+ + "'connector.ip'='%s',"
+ + "'connector'='iotdb-thrift-connector') "
+ + " with source ( "
+ + "'capture.table'='true',"
+ + "'database-name'='test',"
+ + "'table-name'='test',"
+ + "'mode.streaming'='true',"
+ + "'mode.strict'='true',"
+ + "'mode.snapshot'='false',"
+ + "'start-time'='1',"
+ + "'end-time'='2')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException ignored) {
+ }
+
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(0, showPipeResult.size());
+ }
+ }
+
+ @Test
+ public void testMissingStage() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute("create pipe p1");
+ fail();
+ } catch (SQLException ignored) {
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute("create pipe p2 with extractor ('extractor'='iotdb-extractor')");
+ fail();
+ } catch (SQLException ignored) {
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ "create pipe p3"
+ + " with extractor ('extractor'='iotdb-extractor')"
+ + " with processor ('processor'='do-nothing-processor')");
+ fail();
+ } catch (SQLException ignored) {
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p4"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ } catch (SQLException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p5"
+ + " with extractor ('extractor'='iotdb-extractor')"
+ + " with processor ('processor'='do-nothing-processor')"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ } catch (SQLException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(2, showPipeResult.size());
+ }
+ }
+
+ @Test
+ public void testInvalidParameter() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p1"
+ + " with extractor ()"
+ + " with processor ()"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ } catch (SQLException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p3"
+ + " with extractor ()"
+ + " with processor ('processor'='invalid-param')"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException ignored) {
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p4"
+ + " with extractor ()"
+ + " with processor ()"
+ + " with connector ("
+ + "'connector'='invalid-param',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException ignored) {
+ }
+
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.size());
+ }
+ }
+
+ @Test
+ public void testBrackets() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe extractor1"
+ + " with extractor ('extractor'='iotdb-extractor')"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ } catch (SQLException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe source2"
+ + " with extractor (\"extractor\"=\"iotdb-extractor\")"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe extractor3"
+ + " with extractor ("
+ + "'capture.table'='true',"
+ + "'database-name'='test',"
+ + "'table-name'='test',"
+ + "'mode.streaming'='true',"
+ + "'mode.strict'='true',"
+ + "'mode.snapshot'='false',"
+ + "'start-time'='1',"
+ + "'end-time'='2',"
+ + "'extractor'=\"iotdb-extractor\")"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe extractor4"
+ + " with extractor ("
+ + "'extractor'=iotdb-extractor,"
+ + "'capture.table'='true',"
+ + "'database-name'='test',"
+ + "'table-name'='test',"
+ + "'mode.streaming'='true',"
+ + "'mode.strict'='true',"
+ + "'mode.snapshot'='false',"
+ + "'start-time'='1',"
+ + "'end-time'='2')"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException ignored) {
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe extractor5"
+ + " with extractor ("
+ + "'extractor'=`iotdb-extractor`,"
+ + "'capture.table'='true',"
+ + "'database-name'='test',"
+ + "'table-name'='test',"
+ + "'mode.streaming'='true',"
+ + "'mode.strict'='true',"
+ + "'mode.snapshot'='false',"
+ + "'start-time'='1',"
+ + "'end-time'='2')"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException ignored) {
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe processor1"
+ + " with processor ('processor'='do-nothing-processor')"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe processor2"
+ + " with processor (\"processor\"=\"do-nothing-processor\")"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe processor3"
+ + " with processor ('processor'=\"do-nothing-processor\")"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe processor4"
+ + " with processor (processor=do-nothing-processor)"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (Exception ignored) {
+ }
+
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe processor5"
+ + " with processor ('processor'=`do-nothing-processor`)"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (Exception ignored) {
+ }
+
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(2, showPipeResult.size());
+ }
+ }
+
+ @Test
+ public void testShowPipeWithWrongPipeName() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor.database-name", "test");
+ extractorAttributes.put("extractor.table-name", "test.*");
+ extractorAttributes.put("extractor.inclusion", "data.insert");
+ extractorAttributes.put("extractor.capture.table", "true");
+ extractorAttributes.put("extractor.mode.streaming", "true");
+ extractorAttributes.put("extractor.mode.snapshot", "true");
+ extractorAttributes.put("extractor.mode.strict", "true");
+ extractorAttributes.put("extractor.start-time", "1");
+ extractorAttributes.put("extractor.end-time", "2");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p2", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ connectorAttributes.replace("connector.batch.enable", "true");
+
+ status =
+ client.createPipe(
+ new TCreatePipeReq("p3", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(3, showPipeResult.size());
+
+ showPipeResult = client.showPipe(new TShowPipeReq().setPipeName("p1")).pipeInfoList;
+ Assert.assertTrue(showPipeResult.stream().anyMatch((o) -> o.id.equals("p1")));
+ Assert.assertFalse(showPipeResult.stream().anyMatch((o) -> o.id.equals("p2")));
+ Assert.assertFalse(showPipeResult.stream().anyMatch((o) -> o.id.equals("p3")));
+
+ // Show all pipes whose connector is also used by p1.
+ // p1 and p2 share the same connector parameters, so they have the same connector.
+ showPipeResult =
+ client.showPipe(new TShowPipeReq().setPipeName("p1").setWhereClause(true)).pipeInfoList;
+ Assert.assertTrue(showPipeResult.stream().anyMatch((o) -> o.id.equals("p1")));
+ Assert.assertTrue(showPipeResult.stream().anyMatch((o) -> o.id.equals("p2")));
+ Assert.assertFalse(showPipeResult.stream().anyMatch((o) -> o.id.equals("p3")));
+ }
+ }
+
+ @Test
+ public void testInclusionPattern() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ // Empty inclusion
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p2"
+ + " with extractor ('extractor.inclusion'='schema, auth.role', 'extractor.inclusion.exclusion'='all')"
+ + " with processor ()"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException ignored) {
+ }
+
+ // Invalid inclusion
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p3"
+ + " with extractor ('extractor.inclusion'='wrong')"
+ + " with processor ()"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException ignored) {
+ }
+
+ // Invalid exclusion
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p4"
+ + " with extractor ('extractor.inclusion.exclusion'='wrong')"
+ + " with processor ()"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ fail();
+ } catch (SQLException ignored) {
+ }
+
+ // Valid
+ try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute(
+ String.format(
+ "create pipe p4"
+ + " with extractor ('extractor.inclusion'='all', 'extractor.inclusion.exclusion'='schema, auth')"
+ + " with processor ()"
+ + " with connector ("
+ + "'connector'='iotdb-thrift-connector',"
+ + "'connector.ip'='%s',"
+ + "'connector.port'='%s',"
+ + "'connector.batch.enable'='false')",
+ receiverIp, receiverPort));
+ } catch (SQLException e) {
+ e.printStackTrace();
+ fail(e.getMessage());
+ }
+
+ final List<TShowPipeInfo> showPipeResult = client.showPipe(new TShowPipeReq()).pipeInfoList;
+ Assert.assertEquals(1, showPipeResult.size());
+ }
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeWithLoadIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeWithLoadIT.java
new file mode 100644
index 00000000000..77dffd9f6db
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBPipeWithLoadIT.java
@@ -0,0 +1,120 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.consensus.ConsensusFactory;
+import org.apache.iotdb.it.env.MultiEnvFactory;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBPipeWithLoadIT extends AbstractPipeTableModelTestIT {
+
+ @Before
+ public void setUp() {
+ MultiEnvFactory.createEnv(2);
+ senderEnv = MultiEnvFactory.getEnv(0);
+ receiverEnv = MultiEnvFactory.getEnv(1);
+
+ // TODO: delete ratis configurations
+ senderEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ // Disable sender compaction to test mods
+ .setEnableSeqSpaceCompaction(false)
+ .setEnableUnseqSpaceCompaction(false)
+ .setEnableCrossSpaceCompaction(false);
+ receiverEnv
+ .getConfig()
+ .getCommonConfig()
+ .setAutoCreateSchemaEnabled(true)
+ .setConfigNodeConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS)
+ .setSchemaRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS);
+
+ // 10 min, assert that the operations will not time out
+ senderEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+ receiverEnv.getConfig().getCommonConfig().setCnConnectionTimeoutMs(600000);
+
+ senderEnv.initClusterEnvironment();
+ receiverEnv.initClusterEnvironment();
+ }
+
+ @Ignore // not support
+ @Test
+ public void testReceiverNotLoadDeletedTimeseries() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ // Enable mods transfer
+ extractorAttributes.put("modes", "true");
+ extractorAttributes.put("capture.table", "true");
+
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+ // Generate TsFile
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.deleteData("test", "test", 50, 100, senderEnv);
+
+ TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TableModelUtils.assertCountData("test", "test", 50, receiverEnv);
+ }
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBTablePatternFormatIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBTablePatternFormatIT.java
new file mode 100644
index 00000000000..62b66aba5fe
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/IoTDBTablePatternFormatIT.java
@@ -0,0 +1,572 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient;
+import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq;
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.MultiClusterIT2TableModel;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({MultiClusterIT2TableModel.class})
+public class IoTDBTablePatternFormatIT extends AbstractPipeTableModelTestIT {
+
+ @Test
+ public void testTableNamePattern() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern", "pattern");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern1", "pattern1");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern", "pattern", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern1", "pattern1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor.table-name", "test.*");
+ extractorAttributes.put("extractor.inclusion", "data.insert");
+ extractorAttributes.put("extractor.capture.table", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test1", "test1", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern", "pattern", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern1", "pattern1", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ TableModelUtils.assertData("test", "test", 0, 200, receiverEnv);
+ TableModelUtils.assertData("test1", "test1", 0, 200, receiverEnv);
+ if (!TableModelUtils.hasDataBase("test", receiverEnv)) {
+ Assert.fail();
+ }
+ if (!TableModelUtils.hasDataBase("test1", receiverEnv)) {
+ Assert.fail();
+ }
+ }
+ }
+
+ @Test
+ public void testTableNamePatternByHistoryTSFile() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern", "pattern");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern1", "pattern1");
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern", "pattern", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern1", "pattern1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ // extractorAttributes.put("extractor.database-name", "test.*");
+ extractorAttributes.put("extractor.table-name", "test.*");
+ extractorAttributes.put("extractor.inclusion", "data.insert");
+ extractorAttributes.put("extractor.capture.table", "true");
+ extractorAttributes.put("extractor.start-time", "0");
+ extractorAttributes.put("extractor.end-time", "49");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ TableModelUtils.assertData("test", "test", 0, 50, receiverEnv);
+ TableModelUtils.assertData("test1", "test1", 0, 50, receiverEnv);
+ HashSet<String> expectedResults = new HashSet();
+ if (!TableModelUtils.hasDataBase("test", receiverEnv)) {
+ Assert.fail();
+ }
+ if (!TableModelUtils.hasDataBase("test1", receiverEnv)) {
+ Assert.fail();
+ }
+ }
+ }
+
+ @Test
+ public void testTableNamePatternByRealTime() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern", "pattern");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern1", "pattern1");
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ // extractorAttributes.put("extractor.database-name", "test.*");
+ extractorAttributes.put("extractor.table-name", "test.*");
+ extractorAttributes.put("extractor.inclusion", "data.insert");
+ extractorAttributes.put("extractor.capture.table", "true");
+ extractorAttributes.put("extractor.start-time", "100");
+ extractorAttributes.put("extractor.end-time", "149");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test1", "test1", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern", "pattern", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern1", "pattern1", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ TableModelUtils.assertData("test", "test", 100, 150, receiverEnv);
+ TableModelUtils.assertData("test1", "test1", 100, 150, receiverEnv);
+ if (!TableModelUtils.hasDataBase("test", receiverEnv)) {
+ Assert.fail();
+ }
+ if (!TableModelUtils.hasDataBase("test1", receiverEnv)) {
+ Assert.fail();
+ }
+ }
+ }
+
+ @Test
+ public void testDataBasePattern() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern", "pattern");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern1", "pattern1");
+
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern", "pattern", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern1", "pattern1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor.database-name", "pattern.*");
+ extractorAttributes.put("extractor.inclusion", "data.insert");
+ extractorAttributes.put("extractor.capture.table", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test1", "test1", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern", "pattern", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern1", "pattern1", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ TableModelUtils.assertData("pattern", "pattern", 0, 200, receiverEnv);
+ TableModelUtils.assertData("pattern1", "pattern1", 0, 200, receiverEnv);
+
+ HashSet<String> expectedResults = new HashSet();
+ if (!TableModelUtils.hasDataBase("pattern", receiverEnv)) {
+ Assert.fail();
+ }
+ if (!TableModelUtils.hasDataBase("pattern1", receiverEnv)) {
+ Assert.fail();
+ }
+ }
+ }
+
+ @Test
+ public void testDataBasePatternByHistoryTSFile() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern", "pattern");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern1", "pattern1");
+
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern", "pattern", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern1", "pattern1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor.database-name", "pattern.*");
+ extractorAttributes.put("extractor.inclusion", "data.insert");
+ extractorAttributes.put("extractor.capture.table", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ TableModelUtils.assertData("pattern", "pattern", 0, 100, receiverEnv);
+ TableModelUtils.assertData("pattern1", "pattern1", 0, 100, receiverEnv);
+
+ if (!TableModelUtils.hasDataBase("pattern1", receiverEnv)) {
+ Assert.fail();
+ }
+ if (!TableModelUtils.hasDataBase("pattern", receiverEnv)) {
+ Assert.fail();
+ }
+ }
+ }
+
+ @Test
+ public void testDataBasePatternByRealtime() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern", "pattern");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern1", "pattern1");
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor.database-name", "pattern.*");
+ extractorAttributes.put("extractor.inclusion", "data.insert");
+ extractorAttributes.put("extractor.capture.table", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+
+ insertResult = TableModelUtils.insertData("pattern", "pattern", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern1", "pattern1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ TableModelUtils.assertData("pattern", "pattern", 0, 100, receiverEnv);
+ TableModelUtils.assertData("pattern1", "pattern1", 0, 100, receiverEnv);
+
+ if (!TableModelUtils.hasDataBase("pattern", receiverEnv)) {
+ Assert.fail();
+ }
+ if (!TableModelUtils.hasDataBase("pattern1", receiverEnv)) {
+ Assert.fail();
+ }
+ }
+ }
+
+ @Test
+ public void testIoTDBPatternWithDataBaseAndTable() throws Exception {
+ final DataNodeWrapper receiverDataNode = receiverEnv.getDataNodeWrapper(0);
+
+ final String receiverIp = receiverDataNode.getIp();
+ final int receiverPort = receiverDataNode.getPort();
+ boolean insertResult = true;
+
+ try (final SyncConfigNodeIServiceClient client =
+ (SyncConfigNodeIServiceClient) senderEnv.getLeaderConfigNodeConnection()) {
+
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test", "test");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "test1", "test1");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern", "pattern");
+ TableModelUtils.createDataBaseAndTable(senderEnv, "pattern1", "pattern1");
+
+ insertResult = TableModelUtils.insertData("test", "test", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test1", "test1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern", "pattern", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern1", "pattern1", 0, 100, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ final Map<String, String> extractorAttributes = new HashMap<>();
+ final Map<String, String> processorAttributes = new HashMap<>();
+ final Map<String, String> connectorAttributes = new HashMap<>();
+
+ extractorAttributes.put("extractor.database-name", "pattern.*");
+ extractorAttributes.put("extractor.table-name", "test.*");
+ extractorAttributes.put("extractor.inclusion", "data.insert");
+ extractorAttributes.put("extractor.capture.table", "true");
+
+ connectorAttributes.put("connector", "iotdb-thrift-connector");
+ connectorAttributes.put("connector.batch.enable", "false");
+ connectorAttributes.put("connector.ip", receiverIp);
+ connectorAttributes.put("connector.port", Integer.toString(receiverPort));
+
+ final TSStatus status =
+ client.createPipe(
+ new TCreatePipeReq("p1", connectorAttributes)
+ .setExtractorAttributes(extractorAttributes)
+ .setProcessorAttributes(processorAttributes));
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(), client.startPipe("p1").getCode());
+ insertResult = TableModelUtils.insertData("test", "test", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("test1", "test1", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern", "pattern", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+ insertResult = TableModelUtils.insertData("pattern1", "pattern1", 100, 200, senderEnv);
+ if (!insertResult) {
+ return;
+ }
+
+ TestUtils.assertDataEventuallyOnEnv(
+ receiverEnv,
+ "show databases",
+ "Database,SchemaReplicationFactor,DataReplicationFactor,TimePartitionInterval,",
+ Collections.emptySet(),
+ null);
+ }
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/TableModelUtils.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/TableModelUtils.java
new file mode 100644
index 00000000000..4c8ff89b024
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/tablemodel/TableModelUtils.java
@@ -0,0 +1,395 @@
+/*
+ * 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.iotdb.pipe.it.tablemodel;
+
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.isession.IPooledSession;
+import org.apache.iotdb.isession.pool.ISessionPool;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.itbase.env.BaseEnv;
+import org.apache.iotdb.rpc.RpcUtils;
+
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.utils.Binary;
+import org.apache.tsfile.utils.BitMap;
+import org.apache.tsfile.utils.BytesUtils;
+import org.apache.tsfile.utils.DateUtils;
+import org.apache.tsfile.write.record.Tablet;
+import org.apache.tsfile.write.schema.IMeasurementSchema;
+import org.apache.tsfile.write.schema.MeasurementSchema;
+
+import java.nio.charset.StandardCharsets;
+import java.sql.Connection;
+import java.sql.Statement;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import static org.junit.Assert.fail;
+
+/**
+ * This class is specifically designed for table model testing and is used for data insertion.
+ * Please note that the Table pattern defined by this class is fixed, and it is recommended to use
+ * all methods together to ensure data consistency and integrity.
+ *
+ * <p>This class provides a structured approach to inserting data, suitable for scenarios where
+ * batch data insertion is required. Due to the fixed pattern, users should ensure that the data
+ * format matches the pattern defined by the class.
+ */
+public class TableModelUtils {
+
+ public static void createDataBaseAndTable(BaseEnv baseEnv, String table, String database) {
+ try (Connection connection = baseEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ Statement statement = connection.createStatement()) {
+ statement.execute("create database if not exists " + database);
+ statement.execute("use " + database);
+ statement.execute(
+ "CREATE TABLE "
+ + table
+ + "(s0 string id, s1 int64 measurement, s2 float measurement, s3 string measurement, s4 timestamp measurement, s5 int32 measurement, s6 double measurement, s7 date measurement, s8 text measurement )");
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+
+ public static void createDataBase(BaseEnv baseEnv, String database) {
+ try (Connection connection = baseEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ Statement statement = connection.createStatement()) {
+ statement.execute("create database if not exists " + database);
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+
+ public static boolean insertData(
+ String dataBaseName, String tableName, int start, int end, BaseEnv baseEnv) {
+ List<String> list = new ArrayList<>(end - start + 1);
+ for (int i = start; i < end; ++i) {
+ list.add(
+ String.format(
+ "insert into %s (s0, s3, s2, s1, s4, s5, s6, s7, s8, time) values ('t%s','%s', %s.0, %s, %s, %d, %d.0, '%s', '%s', %s)",
+ tableName, i, i, i, i, i, i, i, getDateStr(i), i, i));
+ }
+ list.add("flush");
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ dataBaseName, BaseEnv.TABLE_SQL_DIALECT, baseEnv, list)) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean insertData(
+ String dataBaseName,
+ String tableName,
+ int start,
+ int end,
+ BaseEnv baseEnv,
+ boolean allowNullValue) {
+ List<String> list = new ArrayList<>(end - start + 1);
+ Object[] values = new Object[9];
+ Random random = new Random();
+ // s0 string, s1 int64, s2 float, s3 string, s4 timestamp, s5 int32, s6 double, s7 date, s8 text
+ for (int i = start; i < end; ++i) {
+ Arrays.fill(values, i);
+ values[0] = String.format("'t%s'", i);
+ values[2] = String.format("%s.0", i);
+ values[3] = String.format("%s", i);
+ values[6] = String.format("%s.0", i);
+ values[7] = String.format("'%s'", getDateStr(i));
+ values[8] = String.format("'%s'", i);
+ if (allowNullValue) {
+ values[random.nextInt(9)] = "null";
+ }
+ list.add(
+ String.format(
+ "insert into %s (s0, s1, s2, s3, s4, s5, s6, s7, s8, time) values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
+ tableName, values[0], values[1], values[2], values[3], values[4], values[5],
+ values[6], values[7], values[8], i));
+ }
+ list.add("flush");
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ dataBaseName, BaseEnv.TABLE_SQL_DIALECT, baseEnv, list)) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean insertDataNotThrowError(
+ String dataBaseName, String tableName, int start, int end, BaseEnv baseEnv) {
+ List<String> list = new ArrayList<>(end - start + 1);
+ for (int i = start; i < end; ++i) {
+ list.add(
+ String.format(
+ "insert into %s (s0, s3, s2, s1, s4, s5, s6, s7, s8, time) values ('t%s','%s', %s.0, %s, %s, %d, %d.0, '%s', '%s', %s)",
+ tableName, i, i, i, i, i, i, i, getDateStr(i), i, i));
+ }
+ return TestUtils.tryExecuteNonQueriesWithRetry(
+ dataBaseName, BaseEnv.TABLE_SQL_DIALECT, baseEnv, list);
+ }
+
+ public static boolean insertData(
+ String dataBaseName,
+ String tableName,
+ int start,
+ int end,
+ BaseEnv baseEnv,
+ DataNodeWrapper wrapper) {
+ List<String> list = new ArrayList<>(end - start + 1);
+ for (int i = start; i < end; ++i) {
+ list.add(
+ String.format(
+ "insert into %s (s0, s3, s2, s1, s4, s5, s6, s7, s8, time) values ('t%s','%s', %s.0, %s, %s, %d, %d.0, '%s', '%s', %s)",
+ tableName, i, i, i, i, i, i, i, getDateStr(i), i, i));
+ }
+ list.add("flush");
+ if (!TestUtils.tryExecuteNonQueriesOnSpecifiedDataNodeWithRetry(
+ baseEnv, wrapper, list, dataBaseName, BaseEnv.TABLE_SQL_DIALECT)) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean insertDataByTablet(
+ String dataBaseName,
+ String tableName,
+ int start,
+ int end,
+ BaseEnv baseEnv,
+ boolean allowNullValue) {
+ final Tablet tablet = generateTablet(tableName, start, end, allowNullValue);
+ ISessionPool sessionPool = baseEnv.getSessionPool(1, "table");
+ try (final IPooledSession session = sessionPool.getPooledSession()) {
+ session.executeNonQueryStatement("use " + dataBaseName);
+ session.insertTablet(tablet);
+ session.executeNonQueryStatement("flush");
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public static void deleteData(
+ String dataBaseName, String tableName, int start, int end, BaseEnv baseEnv) {
+ List<String> list = new ArrayList<>(end - start + 1);
+ list.add(
+ String.format("delete from %s where time >= %s and time <= %s", tableName, start, end));
+ list.add("flush");
+ if (!TestUtils.tryExecuteNonQueriesWithRetry(
+ dataBaseName, BaseEnv.TABLE_SQL_DIALECT, baseEnv, list)) {
+ fail();
+ }
+ }
+
+ public static Set<String> generateExpectedResults(int start, int end) {
+ Set<String> expectedResSet = new HashSet<>();
+ for (int i = start; i < end; ++i) {
+ final String time = RpcUtils.formatDatetime("default", "ms", i, ZoneOffset.UTC);
+ expectedResSet.add(
+ String.format(
+ "t%d,%d,%d.0,%d,%s,%d,%d.0,%s,%s,%s,",
+ i, i, i, i, time, i, i, getDateStr(i), i, time));
+ }
+ return expectedResSet;
+ }
+
+ public static Set<String> generateExpectedResults(Tablet tablet) {
+ Set<String> expectedResSet = new HashSet<>();
+ List<IMeasurementSchema> schemas = tablet.getSchemas();
+ for (int i = 0; i < tablet.rowSize; i++) {
+ StringBuilder stringBuffer = new StringBuilder();
+ for (int j = 0; j < tablet.getSchemas().size(); j++) {
+ BitMap bitMap = tablet.bitMaps[j];
+ if (bitMap.isMarked(i)) {
+ stringBuffer.append("null,");
+ continue;
+ }
+ switch (schemas.get(j).getType()) {
+ case TIMESTAMP:
+ final String time =
+ RpcUtils.formatDatetime(
+ "default", "ms", ((long[]) tablet.values[j])[i], ZoneOffset.UTC);
+ stringBuffer.append(time);
+ stringBuffer.append(",");
+ break;
+ case DATE:
+ stringBuffer.append(((LocalDate[]) tablet.values[j])[i].toString());
+ stringBuffer.append(",");
+ break;
+ case BLOB:
+ stringBuffer.append(
+ BytesUtils.parseBlobByteArrayToString(
+ ((Binary[]) tablet.values[j])[i].getValues()));
+ stringBuffer.append(",");
+ break;
+ case TEXT:
+ case STRING:
+ stringBuffer.append(
+ new String(((Binary[]) tablet.values[j])[i].getValues(), StandardCharsets.UTF_8));
+ stringBuffer.append(",");
+ break;
+ case DOUBLE:
+ stringBuffer.append(((double[]) tablet.values[j])[i]);
+ stringBuffer.append(",");
+ break;
+ case FLOAT:
+ stringBuffer.append(((float[]) tablet.values[j])[i]);
+ stringBuffer.append(",");
+ case INT32:
+ stringBuffer.append(((int[]) tablet.values[j])[i]);
+ stringBuffer.append(",");
+ case INT64:
+ stringBuffer.append(((long[]) tablet.values[j])[i]);
+ stringBuffer.append(",");
+ }
+ }
+ String time = RpcUtils.formatDatetime("default", "ms", tablet.timestamps[i], ZoneOffset.UTC);
+ stringBuffer.append(time);
+ stringBuffer.append(",");
+ expectedResSet.add(stringBuffer.toString());
+ }
+
+ return expectedResSet;
+ }
+
+ public static String generateHeaderResults() {
+ return "s0,s3,s2,s1,s4,s5,s6,s7,s8,time,";
+ }
+
+ public static String getQuerySql(String table) {
+ return "select s0,s3,s2,s1,s4,s5,s6,s7,s8,time from " + table;
+ }
+
+ public static String getQueryCountSql(String table) {
+ return "select count(*) from " + table;
+ }
+
+ public static void assertData(
+ String database, String table, int start, int end, BaseEnv baseEnv) {
+ TestUtils.assertDataEventuallyOnEnv(
+ baseEnv,
+ TableModelUtils.getQuerySql(table),
+ TableModelUtils.generateHeaderResults(),
+ TableModelUtils.generateExpectedResults(start, end),
+ database);
+ }
+
+ public static void assertData(String database, String table, Tablet tablet, BaseEnv baseEnv) {
+ TestUtils.assertDataEventuallyOnEnv(
+ baseEnv,
+ TableModelUtils.getQuerySql(table),
+ TableModelUtils.generateHeaderResults(),
+ TableModelUtils.generateExpectedResults(tablet),
+ database);
+ }
+
+ public static boolean hasDataBase(String database, BaseEnv baseEnv) {
+ TestUtils.assertDataEventuallyOnEnv(baseEnv, "", "", Collections.emptySet(), database);
+ return true;
+ }
+
+ public static void assertCountData(String database, String table, int count, BaseEnv baseEnv) {
+ TestUtils.assertDataEventuallyOnEnv(
+ baseEnv, getQueryCountSql(table), "_col0,", Collections.singleton(count + ","), database);
+ }
+
+ public static String getDateStr(int value) {
+ Date date = new Date(value);
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ try {
+ return dateFormat.format(date);
+ } catch (Exception e) {
+ return "1970-01-01";
+ }
+ }
+
+ public static LocalDate getDate(int value) {
+ Date date = new Date(value);
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ try {
+ return DateUtils.parseIntToLocalDate(
+ DateUtils.parseDateExpressionToInt(dateFormat.format(date)));
+ } catch (Exception e) {
+ return DateUtils.parseIntToLocalDate(DateUtils.parseDateExpressionToInt("1970-01-01"));
+ }
+ }
+
+ public static Tablet generateTablet(
+ String tableName, int start, int end, boolean allowNullValue) {
+ List<IMeasurementSchema> schemaList = new ArrayList<>();
+ schemaList.add(new MeasurementSchema("s0", TSDataType.STRING));
+ schemaList.add(new MeasurementSchema("s1", TSDataType.INT64));
+ schemaList.add(new MeasurementSchema("s2", TSDataType.FLOAT));
+ schemaList.add(new MeasurementSchema("s3", TSDataType.STRING));
+ schemaList.add(new MeasurementSchema("s4", TSDataType.TIMESTAMP));
+ schemaList.add(new MeasurementSchema("s5", TSDataType.INT32));
+ schemaList.add(new MeasurementSchema("s6", TSDataType.DOUBLE));
+ schemaList.add(new MeasurementSchema("s7", TSDataType.DATE));
+ schemaList.add(new MeasurementSchema("s8", TSDataType.TEXT));
+
+ final List<Tablet.ColumnType> columnTypes =
+ Arrays.asList(
+ Tablet.ColumnType.ID,
+ Tablet.ColumnType.MEASUREMENT,
+ Tablet.ColumnType.MEASUREMENT,
+ Tablet.ColumnType.MEASUREMENT,
+ Tablet.ColumnType.MEASUREMENT,
+ Tablet.ColumnType.MEASUREMENT,
+ Tablet.ColumnType.MEASUREMENT,
+ Tablet.ColumnType.MEASUREMENT,
+ Tablet.ColumnType.MEASUREMENT);
+ Tablet tablet = new Tablet(tableName, schemaList, columnTypes, end - start);
+ tablet.initBitMaps();
+ Random random = new Random();
+
+ // s2 float, s3 string, s4 timestamp, s5 int32, s6 double, s7 date, s8 text
+ for (long row = 0; row < end - start; row++) {
+ int randomNumber = allowNullValue ? random.nextInt(9) : 9;
+ long value = start + row;
+ int rowIndex = tablet.rowSize++;
+ tablet.addTimestamp(rowIndex, value);
+ tablet.addValue(
+ "s0", rowIndex, new Binary(String.valueOf(value).getBytes(StandardCharsets.UTF_8)));
+ tablet.addValue("s1", rowIndex, value);
+ tablet.addValue("s2", rowIndex, (value * 1.0f));
+ tablet.addValue(
+ "s3", rowIndex, new Binary(String.valueOf(value).getBytes(StandardCharsets.UTF_8)));
+ tablet.addValue("s4", rowIndex, value);
+ tablet.addValue("s5", rowIndex, (int) value);
+ tablet.addValue("s6", rowIndex, value * 0.1);
+ tablet.addValue("s7", rowIndex, getDate((int) value));
+ tablet.addValue(
+ "s8", rowIndex, new Binary(String.valueOf(value).getBytes(StandardCharsets.UTF_8)));
+ if (randomNumber < 9) {
+ tablet.addValue("s" + randomNumber, rowIndex, null);
+ }
+ }
+
+ return tablet;
+ }
+}