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 745f0456c6a IGNITE-26110 Add unit tests for serialization
compatibility of all Raft commands (#6744)
745f0456c6a is described below
commit 745f0456c6afb5fce32ca10e16807b03535d3425
Author: Kirill Tkalenko <[email protected]>
AuthorDate: Fri Oct 10 14:54:04 2025 +0300
IGNITE-26110 Add unit tests for serialization compatibility of all Raft
commands (#6744)
---
modules/replicator/build.gradle | 1 +
.../ReplicatorCommandsCompatibilityTest.java | 126 +++++++++++++++++++++
modules/transactions/build.gradle | 1 +
.../tx/message/TxCommandsCompatibilityTest.java | 100 ++++++++++++++++
4 files changed, 228 insertions(+)
diff --git a/modules/replicator/build.gradle b/modules/replicator/build.gradle
index c54ad928f5c..1ab45a03a14 100644
--- a/modules/replicator/build.gradle
+++ b/modules/replicator/build.gradle
@@ -57,6 +57,7 @@ dependencies {
integrationTestImplementation testFixtures(project(':ignite-metrics'))
integrationTestImplementation testFixtures(project(':ignite-raft'))
+ testImplementation project(':ignite-network')
testImplementation testFixtures(project(':ignite-core'))
testImplementation testFixtures(project(':ignite-placement-driver-api'))
testImplementation testFixtures(project(':ignite-failure-handler'))
diff --git
a/modules/replicator/src/test/java/org/apache/ignite/internal/replicator/ReplicatorCommandsCompatibilityTest.java
b/modules/replicator/src/test/java/org/apache/ignite/internal/replicator/ReplicatorCommandsCompatibilityTest.java
new file mode 100644
index 00000000000..290180b1fef
--- /dev/null
+++
b/modules/replicator/src/test/java/org/apache/ignite/internal/replicator/ReplicatorCommandsCompatibilityTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.replicator;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Base64;
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
+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.replicator.command.SafeTimeSyncCommand;
+import
org.apache.ignite.internal.replicator.message.PrimaryReplicaChangeCommand;
+import org.apache.ignite.internal.replicator.message.ReplicaMessagesFactory;
+import
org.apache.ignite.internal.replicator.message.ReplicaMessagesSerializationRegistryInitializer;
+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 replicator 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 ReplicatorCommandsCompatibilityTest extends
BaseIgniteAbstractTest {
+ private final MessageSerializationRegistry registry = new
MessageSerializationRegistryImpl();
+
+ private final Marshaller marshaller = new
ThreadLocalOptimizedMarshaller(registry);
+
+ private final ReplicaMessagesFactory factory = new
ReplicaMessagesFactory();
+
+ @BeforeEach
+ void setUp() {
+ new
ReplicaMessagesSerializationRegistryInitializer().registerFactories(registry);
+ }
+
+ @Test
+ void testSafeTimeSyncCommand() {
+ SafeTimeSyncCommand command = decodeCommand("CSlH");
+
+ assertEquals(initiatorTime(), command.initiatorTime());
+ }
+
+ @Test
+ void testPrimaryReplicaChangeCommand() {
+ PrimaryReplicaChangeCommand command =
decodeCommand("CSorAAAAAAAAAAAqAAAAAAAAAEUGbm9kZTE=");
+
+ assertEquals(42, command.leaseStartTime());
+ assertEquals(uuid(), command.primaryReplicaNodeId());
+ assertEquals("node1", command.primaryReplicaNodeName());
+ }
+
+ private static HybridTimestamp initiatorTime() {
+ return HybridTimestamp.hybridTimestamp(70);
+ }
+
+ private static UUID uuid() {
+ return new UUID(42, 69);
+ }
+
+ 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(
+ createSafeTimeSyncCommand(),
+ createPrimaryReplicaChangeCommand()
+ );
+
+ for (Command c : commands) {
+ log.info(">>>>> Serialized command: [c={}, base64='{}']",
c.getClass().getSimpleName(), encodeCommand(c));
+ }
+ }
+
+ private PrimaryReplicaChangeCommand createPrimaryReplicaChangeCommand() {
+ return factory.primaryReplicaChangeCommand()
+ .leaseStartTime(42)
+ .primaryReplicaNodeId(uuid())
+ .primaryReplicaNodeName("node1")
+ .build();
+ }
+
+ private SafeTimeSyncCommand createSafeTimeSyncCommand() {
+ return factory.safeTimeSyncCommand()
+ .initiatorTime(initiatorTime())
+ .build();
+ }
+
+ private byte[] serializeCommand(Command c) {
+ return marshaller.marshall(c);
+ }
+
+ private String encodeCommand(Command c) {
+ return Base64.getEncoder().encodeToString(serializeCommand(c));
+ }
+}
diff --git a/modules/transactions/build.gradle
b/modules/transactions/build.gradle
index 76189930ad0..416d2640ff0 100644
--- a/modules/transactions/build.gradle
+++ b/modules/transactions/build.gradle
@@ -51,6 +51,7 @@ dependencies {
implementation libs.fastutil.core
testImplementation project(':ignite-core')
+ testImplementation project(':ignite-raft')
testImplementation testFixtures(project(':ignite-core'))
testImplementation testFixtures(project(':ignite-configuration'))
testImplementation testFixtures(project(':ignite-configuration-system'))
diff --git
a/modules/transactions/src/test/java/org/apache/ignite/internal/tx/message/TxCommandsCompatibilityTest.java
b/modules/transactions/src/test/java/org/apache/ignite/internal/tx/message/TxCommandsCompatibilityTest.java
new file mode 100644
index 00000000000..f5902a7655f
--- /dev/null
+++
b/modules/transactions/src/test/java/org/apache/ignite/internal/tx/message/TxCommandsCompatibilityTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.tx.message;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Base64;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+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 tx 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 TxCommandsCompatibilityTest extends BaseIgniteAbstractTest {
+ private final MessageSerializationRegistry registry = new
MessageSerializationRegistryImpl();
+
+ private final Marshaller marshaller = new
ThreadLocalOptimizedMarshaller(registry);
+
+ private final TxMessagesFactory factory = new TxMessagesFactory();
+
+ @BeforeEach
+ void setUp() {
+ new
TxMessagesSerializationRegistryInitializer().registerFactories(registry);
+ }
+
+ @Test
+ void testVacuumTxStatesCommand() {
+ VacuumTxStatesCommand command =
decodeCommand("Bg4CAAAAAAAAAAAqAAAAAAAAAEU=");
+
+ assertEquals(Set.of(uuid()), command.txIds());
+ }
+
+ private static UUID uuid() {
+ return new UUID(42, 69);
+ }
+
+ 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(
+ createVacuumTxStatesCommand()
+ );
+
+ for (Command c : commands) {
+ log.info(">>>>> Serialized command: [c={}, base64='{}']",
c.getClass().getSimpleName(), encodeCommand(c));
+ }
+ }
+
+ private VacuumTxStatesCommand createVacuumTxStatesCommand() {
+ return factory.vacuumTxStatesCommand()
+ .txIds(Set.of(uuid()))
+ .build();
+ }
+
+ private byte[] serializeCommand(Command c) {
+ return marshaller.marshall(c);
+ }
+
+ private String encodeCommand(Command c) {
+ return Base64.getEncoder().encodeToString(serializeCommand(c));
+ }
+}