This is an automated email from the ASF dual-hosted git repository. Caideyipi pushed a commit to branch daily-it-logical-backup-18623 in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 7249a7f31110234fad831c4c9188bb740a4240e3 Author: Caideyipi <[email protected]> AuthorDate: Fri Oct 23 12:53:06 2026 +0800 [Test] Add daily IT coverage for logical backup --- .github/workflows/logical-backup-daily-it.yml | 48 +++++++ .../treemodel/manual/IoTDBPipeLogicalBackupIT.java | 141 +++++++++++++++++++++ 2 files changed, 189 insertions(+) diff --git a/.github/workflows/logical-backup-daily-it.yml b/.github/workflows/logical-backup-daily-it.yml new file mode 100644 index 00000000000..21be30edbc3 --- /dev/null +++ b/.github/workflows/logical-backup-daily-it.yml @@ -0,0 +1,48 @@ +name: Logical Backup Daily IT + +on: + pull_request: + branches: + - feat/pipe-logical-backup-sink + paths: + - .github/workflows/logical-backup-daily-it.yml + - integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeLogicalBackupIT.java + +concurrency: + group: logical-backup-daily-it-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + logical-backup: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Set up JDK 17 + uses: actions/setup-java@v5 + with: + distribution: corretto + java-version: 17 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Cache Maven packages + uses: actions/cache@v5 + with: + path: ~/.m2 + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: ${{ runner.os }}-m2- + - name: Run logical backup daily IT + run: >- + mvn -B -ntp clean verify + -Pwith-integration-tests + -DskipUTs + -Dit.test=IoTDBPipeLogicalBackupIT + -Dfailsafe.failIfNoSpecifiedTests=false + -DintegrationTest.forkCount=1 + -DConfigNodeMaxHeapSize=256 + -DDataNodeMaxHeapSize=1024 + -DDataNodeMaxDirectMemorySize=768 + -DClusterConfigurations=ScalableSingleNodeMode,ScalableSingleNodeMode + -Drat.skip=true + -pl integration-test + -am + -PMultiClusterIT2DualTreeManual diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeLogicalBackupIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeLogicalBackupIT.java index 4629bcb8875..5b1d9ab0e39 100644 --- a/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeLogicalBackupIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeLogicalBackupIT.java @@ -39,6 +39,7 @@ import org.junit.runner.RunWith; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; import java.sql.Connection; import java.sql.ResultSet; @@ -258,6 +259,146 @@ public class IoTDBPipeLogicalBackupIT extends AbstractPipeDualTreeModelManualIT } } + @Test + public void testTreeContinuousCapturePreservesFilteredDeletionRange() throws Exception { + TestUtils.executeNonQueries( + senderEnv, + Arrays.asList( + "create database root.logical_backup_delete", + "create timeseries root.logical_backup_delete.d.s with datatype=INT32,encoding=PLAIN", + "insert into root.logical_backup_delete.d(time,s) values(1,10)", + "flush"), + null); + final String pipeName = "logical_backup_tree_delete"; + final CompletableFuture<Void> writes = + CompletableFuture.runAsync( + () -> { + try { + Awaitility.await() + .atMost(30, TimeUnit.SECONDS) + .until( + () -> { + try (final Connection connection = senderEnv.getConnection(); + final Statement statement = connection.createStatement(); + final ResultSet result = + statement.executeQuery("show pipe " + pipeName)) { + return result.next(); + } + }); + try (final Connection connection = senderEnv.getConnection(); + final Statement statement = connection.createStatement()) { + statement.execute( + "insert into root.logical_backup_delete.d(time,s) values(2,20)"); + statement.execute( + "insert into root.logical_backup_delete.d(time,s) values(3,30)"); + statement.execute("delete from root.logical_backup_delete.d.s where time <= 2"); + } + } catch (final Exception e) { + throw new CompletionException(e); + } + }); + final Path parent = temporaryFolder.getRoot().toPath().toAbsolutePath(); + runConnected( + senderEnv, + "dump", + "--backup-dir", + parent.toString(), + "--backup-id", + "tree-delete", + "--pipe-name", + pipeName, + "--scope", + "schema,data", + "--model", + "tree", + "--include", + "root.logical_backup_delete.**", + "--start-time", + "1", + "--end-time", + "3", + "--mode", + "continuous", + "--until", + Instant.now().plusSeconds(20).toString(), + "--timeout-seconds", + "180", + "--poll-interval-seconds", + "1"); + writes.get(30, TimeUnit.SECONDS); + final Path backup = parent.resolve("tree-delete"); + assertLogicalRequests(backup); + runConnected(receiverEnv, "restore", "--input", backup.toString()); + TestUtils.assertDataEventuallyOnEnv( + receiverEnv, + "select * from root.logical_backup_delete.d", + "Time,root.logical_backup_delete.d.s,", + new HashSet<>(Arrays.asList("3,30,"))); + } + + @Test + public void testInspectPackAndDeprecatedAliasesKeepBackupPortable() throws Exception { + TestUtils.executeNonQueries( + senderEnv, + Arrays.asList( + "create database root.logical_backup_alias", + "create timeseries root.logical_backup_alias.d.s with datatype=INT32,encoding=PLAIN", + "insert into root.logical_backup_alias.d(time,s) values(1,10)", + "flush"), + null); + final Path parent = temporaryFolder.getRoot().toPath().toAbsolutePath(); + runConnected( + senderEnv, + "dump", + "--backup-dir", + parent.toString(), + "--backup-id", + "portable", + "--scope", + "config,schema,data", + "--model", + "tree", + "--include", + "root.logical_backup_alias.**", + "--timeout-seconds", + "180", + "--poll-interval-seconds", + "1"); + final Path backup = parent.resolve("portable"); + final byte[] manifestBeforePack = Files.readAllBytes(backup.resolve("manifest.json")); + Assert.assertEquals( + 0, + PipeLogicalBackupTool.run( + new String[] {"inspect", "--input", backup.toString(), "--json"})); + Assert.assertEquals( + 0, + PipeLogicalBackupTool.run(new String[] {"verify", "--input", backup.toString(), "--json"})); + final Path archive = parent.resolve("portable.zip"); + Assert.assertEquals( + 0, + PipeLogicalBackupTool.run( + new String[] {"pack", "--input", backup.toString(), "--output", archive.toString()})); + final Path aliasArchive = parent.resolve("portable-alias.zip"); + Assert.assertEquals( + 0, + PipeLogicalBackupTool.run( + new String[] { + "export", "--input", backup.toString(), "--output", aliasArchive.toString() + })); + Assert.assertArrayEquals( + manifestBeforePack, Files.readAllBytes(backup.resolve("manifest.json"))); + Assert.assertEquals( + 0, + PipeLogicalBackupTool.run( + new String[] {"verify", "--input", archive.toString(), "--json"})); + runConnected(receiverEnv, "import", "--input", aliasArchive.toString()); + TestUtils.assertDataEventuallyOnEnv( + receiverEnv, + "select * from root.logical_backup_alias.d", + "Time,root.logical_backup_alias.d.s,", + new HashSet<>(Arrays.asList("1,10,"))); + } + private static void assertLogicalRequests(final Path backup) throws Exception { final Set<Short> logicalRequests = Set.of(
