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

tkalkirill pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new e503475b3df IGNITE-26095 Add partition Raft log compatibility test 
(#6627)
e503475b3df is described below

commit e503475b3df1d6663f53bb4a233539127e23a93d
Author: Kirill Tkalenko <[email protected]>
AuthorDate: Fri Sep 19 17:33:05 2025 +0300

    IGNITE-26095 Add partition Raft log compatibility test (#6627)
---
 ...tionRaftLogOnAnotherNodesCompatibilityTest.java | 82 +++++++++++++++++++
 ...ItPartitionRaftLogOneNodeCompatibilityTest.java | 94 ++++++++++++++++++++++
 .../ignite/internal/CompatibilityTestBase.java     |  7 +-
 .../org/apache/ignite/internal/IgniteCluster.java  |  5 ++
 .../org/apache/ignite/internal/RunnerNode.java     |  7 +-
 5 files changed, 190 insertions(+), 5 deletions(-)

diff --git 
a/modules/compatibility-tests/src/integrationTest/java/org/apache/ignite/internal/ItApplyPartitionRaftLogOnAnotherNodesCompatibilityTest.java
 
b/modules/compatibility-tests/src/integrationTest/java/org/apache/ignite/internal/ItApplyPartitionRaftLogOnAnotherNodesCompatibilityTest.java
new file mode 100644
index 00000000000..7976df6edfa
--- /dev/null
+++ 
b/modules/compatibility-tests/src/integrationTest/java/org/apache/ignite/internal/ItApplyPartitionRaftLogOnAnotherNodesCompatibilityTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.ignite.internal;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+
+import org.apache.ignite.Ignite;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/** Partition Raft log compatibility tests when changing replicas. */
+@ParameterizedClass
+@MethodSource("baseVersions")
+public class ItApplyPartitionRaftLogOnAnotherNodesCompatibilityTest extends 
CompatibilityTestBase {
+    private static final String ZONE_NAME = "TEST_ZONE";
+
+    private static final String TABLE_NAME = "TEST_TABLE";
+
+    @Override
+    protected int nodesCount() {
+        return 3;
+    }
+
+    @Override
+    protected boolean restartWithCurrentEmbeddedVersion() {
+        return false;
+    }
+
+    @Override
+    protected void setupBaseVersion(Ignite baseIgnite) {
+        String createZoneDdl = String.format(
+                "CREATE ZONE %s WITH PARTITIONS=1, REPLICAS=1, 
STORAGE_PROFILES='default', DATA_NODES_FILTER='$[?(@.nodeIndex == \"0\")]'",
+                ZONE_NAME
+        );
+
+        sql(baseIgnite, createZoneDdl);
+
+        sql(baseIgnite, String.format("CREATE TABLE %s(ID INT PRIMARY KEY, VAL 
VARCHAR) ZONE %s", TABLE_NAME, ZONE_NAME));
+
+        String insertDml = String.format("INSERT INTO %s (ID, VAL) VALUES (?, 
?) ", TABLE_NAME);
+
+        baseIgnite.transactions().runInTransaction(tx -> {
+            for (int i = 0; i < 10; i++) {
+                sql(baseIgnite, tx, insertDml, i, "str_" + i);
+            }
+        });
+    }
+
+    @Disabled("https://issues.apache.org/jira/browse/IGNITE-26479";)
+    @Test
+    void testIncreaseReplicas() throws Exception {
+        cluster.stop();
+
+        cluster.startEmbedded(nodesCount(), false);
+
+        sql(String.format("ALTER ZONE %s SET REPLICAS=3, 
DATA_NODES_FILTER='$..*'", ZONE_NAME));
+
+        // Let's wait for replication to complete on other nodes.
+        Thread.sleep(3_000);
+
+        assertThat(sql(cluster.node(1), String.format("SELECT * FROM %s", 
TABLE_NAME)), hasSize(10));
+        assertThat(sql(cluster.node(2), String.format("SELECT * FROM %s", 
TABLE_NAME)), hasSize(10));
+    }
+}
diff --git 
a/modules/compatibility-tests/src/integrationTest/java/org/apache/ignite/internal/ItPartitionRaftLogOneNodeCompatibilityTest.java
 
b/modules/compatibility-tests/src/integrationTest/java/org/apache/ignite/internal/ItPartitionRaftLogOneNodeCompatibilityTest.java
new file mode 100644
index 00000000000..a08095729fb
--- /dev/null
+++ 
b/modules/compatibility-tests/src/integrationTest/java/org/apache/ignite/internal/ItPartitionRaftLogOneNodeCompatibilityTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.ignite.internal;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.stream.Stream;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedClass;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/** Partition raft log compatibility tests for one node. */
+@ParameterizedClass
+@MethodSource("baseVersions")
+public class ItPartitionRaftLogOneNodeCompatibilityTest extends 
CompatibilityTestBase {
+    private static final String TABLE_NAME = "TEST_TABLE";
+
+    @Override
+    protected int nodesCount() {
+        return 1;
+    }
+
+    @Override
+    protected boolean restartWithCurrentEmbeddedVersion() {
+        return false;
+    }
+
+    @Override
+    protected void setupBaseVersion(Ignite baseIgnite) {
+        sql(baseIgnite, String.format("CREATE TABLE %s(ID INT PRIMARY KEY, VAL 
VARCHAR)", TABLE_NAME));
+
+        String insertDml = String.format("INSERT INTO %s (ID, VAL) VALUES (?, 
?)", TABLE_NAME);
+
+        baseIgnite.transactions().runInTransaction(tx -> {
+            for (int i = 0; i < 10; i++) {
+                sql(baseIgnite, tx, insertDml, i, "str_" + i);
+            }
+        });
+    }
+
+    /** Tests a simple node restart scenario with reapplying a partition raft 
log. */
+    @Test
+    void testSimpleRestart() {
+        cluster.stop();
+
+        cleanTableStoragesDir();
+
+        cluster.startEmbedded(nodesCount(), false);
+
+        assertThat(sql(String.format("SELECT * FROM %s", TABLE_NAME)), 
hasSize(10));
+    }
+
+    private void cleanTableStoragesDir() {
+        Path dbDir = 
workDir.resolve(cluster.clusterName()).resolve(cluster.nodeName(0)).resolve("partitions").resolve("db");
+
+        assertThat(dbDir.toString(), isDirectoryExistsAndNotEmpty(dbDir), 
is(true));
+
+        assertThat(dbDir.toString(), IgniteUtils.deleteIfExists(dbDir), 
is(true));
+    }
+
+    private static boolean isDirectoryExistsAndNotEmpty(Path path) {
+        if (!Files.exists(path) || !Files.isDirectory(path)) {
+            return false;
+        }
+
+        try (Stream<Path> list = Files.list(path)) {
+            return list.findAny().isPresent();
+        } catch (IOException e) {
+            return false;
+        }
+    }
+}
diff --git 
a/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/CompatibilityTestBase.java
 
b/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/CompatibilityTestBase.java
index 6115334d3d8..cf261726a63 100644
--- 
a/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/CompatibilityTestBase.java
+++ 
b/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/CompatibilityTestBase.java
@@ -75,7 +75,10 @@ public abstract class CompatibilityTestBase extends 
BaseIgniteAbstractTest {
             + "  clientConnector.port: {},\n"
             + "  clientConnector.sendServerExceptionStackTraceToClient: 
true,\n"
             + "  rest.port: {},\n"
-            + "  failureHandler.dumpThreadsOnFailure: false\n"
+            + "  failureHandler.dumpThreadsOnFailure: false,\n"
+            + "  nodeAttributes: {\n"
+            + "    nodeAttributes: {nodeName: \"{}\", nodeIndex: \"{}\"}\n"
+            + "  }\n"
             + "}";
 
     // If there are no fields annotated with @Parameter, constructor injection 
will be used, which is incompatible with the
@@ -86,7 +89,7 @@ public abstract class CompatibilityTestBase extends 
BaseIgniteAbstractTest {
 
     // Force per class template work directory so that non-static field 
doesn't get overwritten by the BeforeEach callback.
     @WorkDirectory(forcePerClassTemplate = true)
-    private Path workDir;
+    protected Path workDir;
 
     protected IgniteCluster cluster;
 
diff --git 
a/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/IgniteCluster.java
 
b/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/IgniteCluster.java
index a56c8889ca0..0f46446a02e 100644
--- 
a/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/IgniteCluster.java
+++ 
b/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/IgniteCluster.java
@@ -348,4 +348,9 @@ public class IgniteCluster {
             throw new RuntimeException(e);
         }
     }
+
+    /** Returns cluster name. */
+    public String clusterName() {
+        return clusterConfiguration.clusterName();
+    }
 }
diff --git 
a/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/RunnerNode.java
 
b/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/RunnerNode.java
index 9031d92ad11..6efa40e8541 100644
--- 
a/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/RunnerNode.java
+++ 
b/modules/compatibility-tests/src/testFixtures/java/org/apache/ignite/internal/RunnerNode.java
@@ -81,7 +81,7 @@ public class RunnerNode {
     ) throws IOException {
         String nodeName = 
clusterConfiguration.nodeNamingStrategy().nodeName(clusterConfiguration, 
nodeIndex);
         Path workDir = 
clusterConfiguration.workDir().resolve(clusterConfiguration.clusterName()).resolve(nodeName);
-        String configStr = formatConfig(clusterConfiguration, nodeIndex, 
nodesCount);
+        String configStr = formatConfig(clusterConfiguration, nodeName, 
nodeIndex, nodesCount);
 
         Files.createDirectories(workDir);
         Path configPath = workDir.resolve(DEFAULT_CONFIG_NAME);
@@ -179,14 +179,15 @@ public class RunnerNode {
                 .collect(joining(", "));
     }
 
-    private static String formatConfig(ClusterConfiguration 
clusterConfiguration, int nodeIndex, int nodesCount) {
+    private static String formatConfig(ClusterConfiguration 
clusterConfiguration, String nodeName, int nodeIndex, int nodesCount) {
         return IgniteStringFormatter.format(
                 clusterConfiguration.defaultNodeBootstrapConfigTemplate(),
                 clusterConfiguration.basePort() + nodeIndex,
                 seedAddressesString(clusterConfiguration, nodesCount),
                 clusterConfiguration.baseClientPort() + nodeIndex,
                 clusterConfiguration.baseHttpPort() + nodeIndex,
-                clusterConfiguration.baseHttpsPort() + nodeIndex
+                nodeName,
+                nodeIndex
         );
     }
 

Reply via email to