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 198d6d8a079 IGNITE-26665 Add unit tests for serialization
compatibility of all CMG Raft commands (#6732)
198d6d8a079 is described below
commit 198d6d8a0796f91621b09f6c6734782968b4612f
Author: Kirill Tkalenko <[email protected]>
AuthorDate: Fri Oct 10 11:53:05 2025 +0300
IGNITE-26665 Add unit tests for serialization compatibility of all CMG Raft
commands (#6732)
---
modules/cluster-management/build.gradle | 1 +
.../commands/CmgCommandsCompatibilityTest.java | 252 +++++++++++++++++++++
2 files changed, 253 insertions(+)
diff --git a/modules/cluster-management/build.gradle
b/modules/cluster-management/build.gradle
index 267f9452c43..af06255f148 100644
--- a/modules/cluster-management/build.gradle
+++ b/modules/cluster-management/build.gradle
@@ -51,6 +51,7 @@ dependencies {
testImplementation project(':ignite-configuration')
testImplementation project(':ignite-network')
testImplementation project(':ignite-system-disaster-recovery')
+ testImplementation project(':ignite-raft')
testImplementation testFixtures(project(':ignite-core'))
testImplementation testFixtures(project(':ignite-configuration'))
testImplementation testFixtures(project(':ignite-network'))
diff --git
a/modules/cluster-management/src/test/java/org/apache/ignite/internal/cluster/management/raft/commands/CmgCommandsCompatibilityTest.java
b/modules/cluster-management/src/test/java/org/apache/ignite/internal/cluster/management/raft/commands/CmgCommandsCompatibilityTest.java
new file mode 100644
index 00000000000..cc6313d0fa9
--- /dev/null
+++
b/modules/cluster-management/src/test/java/org/apache/ignite/internal/cluster/management/raft/commands/CmgCommandsCompatibilityTest.java
@@ -0,0 +1,252 @@
+/*
+ * 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.cluster.management.raft.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+
+import java.util.Base64;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import org.apache.ignite.internal.cluster.management.ClusterState;
+import org.apache.ignite.internal.cluster.management.ClusterTag;
+import
org.apache.ignite.internal.cluster.management.network.messages.CmgMessagesFactory;
+import
org.apache.ignite.internal.cluster.management.network.messages.CmgMessagesSerializationRegistryInitializer;
+import org.apache.ignite.internal.network.MessageSerializationRegistryImpl;
+import
org.apache.ignite.internal.network.serialization.MessageSerializationRegistry;
+import org.apache.ignite.internal.raft.Command;
+import org.apache.ignite.internal.raft.Marshaller;
+import org.apache.ignite.internal.raft.util.ThreadLocalOptimizedMarshaller;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Compatibility testing for serialization/deserialization of CMG raft
commands. It is verified that deserialization of commands that were
+ * created on earlier versions of the product will be error-free.
+ *
+ * <p>For MAC users with aarch64 architecture, you will need to add {@code ||
"aarch64".equals(arch)} to the
+ * {@code GridUnsafe#unaligned()} for the tests to pass. For more details, see
+ * <a
href="https://lists.apache.org/thread/67coyvm8mo7106mkndt24yqwtbvb7590">discussion</a>.</p>
+ *
+ * <p>To serialize commands, use {@link #serializeAll()} and insert the result
into the appropriate tests.</p>
+ */
+public class CmgCommandsCompatibilityTest extends BaseIgniteAbstractTest {
+ private final MessageSerializationRegistry registry = new
MessageSerializationRegistryImpl();
+
+ private final Marshaller marshaller = new
ThreadLocalOptimizedMarshaller(registry);
+
+ private final CmgMessagesFactory factory = new CmgMessagesFactory();
+
+ @BeforeEach
+ void setUp() {
+ new
CmgMessagesSerializationRegistryInitializer().registerFactories(registry);
+ }
+
+ @Test
+ void testChangeMetaStorageInfoCommand() {
+ ChangeMetaStorageInfoCommand command =
decodeCommand("CDADCG1zTm9kZTIIbXNOb2RlMQEr");
+
+ assertEquals(Set.of("msNode1", "msNode2"), command.metaStorageNodes());
+ assertEquals(42, command.metastorageRepairingConfigIndex());
+ }
+
+ @Test
+ void testInitCmgStateCommand() {
+ InitCmgStateCommand command = decodeCommand(
+
"CCkIPgg/AAAAAAAAAAAqAAAAAAAAAEUNY2x1c3Rlck5hbWUxAwljbWdOb2RlMQljbWdOb2RlMgIAAAAAAAAAACoAAAAAAAAARQxpbml0Q29uZmlnMQMIbXNOb"
+ +
"2RlMQhtc05vZGUyCXZlcnNpb24xCD0GaG9zdDEAAAAAAAAAACoAAAAAAAAARQZuYW1lMekHAglwcm9maWxlMQIIc3lzS2V5MQhzeXNWYWwxAgl1"
+ + "c2VyS2V5MQl1c2VyVmFsMQ=="
+ );
+
+ assertEquals(createClusterNodeMessage(), command.node());
+ assertEquals(createClusterState(), command.clusterState());
+ }
+
+ @Test
+ void testJoinReadyCommand() {
+ JoinReadyCommand command = decodeCommand(
+
"CC0IPQZob3N0MQAAAAAAAAAAKgAAAAAAAABFBm5hbWUx6QcCCXByb2ZpbGUxAghzeXNLZXkxCHN5c1ZhbDECCXVzZXJLZXkxCXVzZXJWYWwx"
+ );
+
+ assertEquals(createClusterNodeMessage(), command.node());
+ }
+
+ @Test
+ void testJoinRequestCommand() {
+ JoinRequestCommand command = decodeCommand(
+
"CCwIPwAAAAAAAAAAKgAAAAAAAABFDWNsdXN0ZXJOYW1lMQg9Bmhvc3QxAAAAAAAAAAAqAAAAAAAAAEUGbmFtZTHpBwIJcHJvZmlsZTECCHN5c0tleTEIc3lzV"
+ + "mFsMQIJdXNlcktleTEJdXNlclZhbDEJdmVyc2lvbjE="
+ );
+
+ assertEquals(createClusterNodeMessage(), command.node());
+ assertEquals("version1", command.version());
+ assertEquals(ClusterTag.clusterTag(factory, "clusterName1", uuid()),
command.clusterTag());
+ }
+
+ @Test
+ void testNodesLeaveCommand() {
+ NodesLeaveCommand command = decodeCommand(
+
"CC4CCD0GaG9zdDEAAAAAAAAAACoAAAAAAAAARQZuYW1lMekHAglwcm9maWxlMQIIc3lzS2V5MQhzeXNWYWwxAgl1c2VyS2V5MQl1c2VyVmFsMQ=="
+ );
+
+ assertEquals(Set.of(createClusterNodeMessage()), command.nodes());
+ }
+
+ @Test
+ void testReadLogicalTopologyCommand() {
+ Command command = decodeCommand("CCs=");
+
+ assertInstanceOf(ReadLogicalTopologyCommand.class, command);
+ }
+
+ @Test
+ void testReadMetaStorageInfoCommand() {
+ Command command = decodeCommand("CEM=");
+
+ assertInstanceOf(ReadMetaStorageInfoCommand.class, command);
+ }
+
+ @Test
+ void testReadStateCommand() {
+ Command command = decodeCommand("CCo=");
+
+ assertInstanceOf(ReadStateCommand.class, command);
+ }
+
+ @Test
+ void testReadValidatedNodesCommand() {
+ Command command = decodeCommand("CC8=");
+
+ assertInstanceOf(ReadValidatedNodesCommand.class, command);
+ }
+
+ private static UUID uuid() {
+ return new UUID(42, 69);
+ }
+
+ private ClusterNodeMessage createClusterNodeMessage() {
+ return factory.clusterNodeMessage()
+ .id(uuid())
+ .name("name1")
+ .host("host1")
+ .port(1000)
+ .userAttributes(Map.of("userKey1", "userVal1"))
+ .systemAttributes(Map.of("sysKey1", "sysVal1"))
+ .storageProfiles(List.of("profile1"))
+ .build();
+ }
+
+ private ClusterState createClusterState() {
+ return factory.clusterState()
+ .cmgNodes(Set.of("cmgNode1", "cmgNode2"))
+ .metaStorageNodes(Set.of("msNode1", "msNode2"))
+ .version("version1")
+ .clusterTag(ClusterTag.clusterTag(factory, "clusterName1",
uuid()))
+ .initialClusterConfiguration("initConfig1")
+ .formerClusterIds(List.of(uuid()))
+ .build();
+ }
+
+ private <T extends Command> T deserializeCommand(byte[] bytes) {
+ return marshaller.unmarshall(bytes);
+ }
+
+ private <T extends Command> T decodeCommand(String base64) {
+ return deserializeCommand(Base64.getDecoder().decode(base64));
+ }
+
+ @SuppressWarnings("unused")
+ private void serializeAll() {
+ List<Command> commands = List.of(
+ createChangeMetaStorageInfoCommand(),
+ createInitCmgStateCommand(),
+ createJoinReadyCommand(),
+ createJoinRequestCommand(),
+ createNodesLeaveCommand(),
+ createReadLogicalTopologyCommand(),
+ createReadMetaStorageInfoCommand(),
+ createReadStateCommand(),
+ createReadValidatedNodesCommand()
+ );
+
+ for (Command c : commands) {
+ log.info(">>>>> Serialized command: [c={}, base64='{}']",
c.getClass().getSimpleName(), encodeCommand(c));
+ }
+ }
+
+ private ReadValidatedNodesCommand createReadValidatedNodesCommand() {
+ return factory.readValidatedNodesCommand().build();
+ }
+
+ private ReadStateCommand createReadStateCommand() {
+ return factory.readStateCommand().build();
+ }
+
+ private ReadMetaStorageInfoCommand createReadMetaStorageInfoCommand() {
+ return factory.readMetaStorageInfoCommand().build();
+ }
+
+ private ReadLogicalTopologyCommand createReadLogicalTopologyCommand() {
+ return factory.readLogicalTopologyCommand().build();
+ }
+
+ private NodesLeaveCommand createNodesLeaveCommand() {
+ return factory.nodesLeaveCommand()
+ .nodes(Set.of(createClusterNodeMessage()))
+ .build();
+ }
+
+ private JoinRequestCommand createJoinRequestCommand() {
+ return factory.joinRequestCommand()
+ .node(createClusterNodeMessage())
+ .version("version1")
+ .clusterTag(ClusterTag.clusterTag(factory, "clusterName1",
uuid()))
+ .build();
+ }
+
+ private JoinReadyCommand createJoinReadyCommand() {
+ return factory.joinReadyCommand()
+ .node(createClusterNodeMessage())
+ .build();
+ }
+
+ private InitCmgStateCommand createInitCmgStateCommand() {
+ return factory.initCmgStateCommand()
+ .node(createClusterNodeMessage())
+ .clusterState(createClusterState())
+ .build();
+ }
+
+ private ChangeMetaStorageInfoCommand createChangeMetaStorageInfoCommand() {
+ return factory.changeMetaStorageInfoCommand()
+ .metaStorageNodes(Set.of("msNode1", "msNode2"))
+ .metastorageRepairingConfigIndex(42L)
+ .build();
+ }
+
+ private byte[] serializeCommand(Command c) {
+ return marshaller.marshall(c);
+ }
+
+ private String encodeCommand(Command c) {
+ return Base64.getEncoder().encodeToString(serializeCommand(c));
+ }
+}