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 7af25e726ff IGNITE-26676 Add unit tests for serialization
compatibility of all partition Raft commands (#6743)
7af25e726ff is described below
commit 7af25e726ff62741ee2986b0cf7a77bd1feadb7d
Author: Kirill Tkalenko <[email protected]>
AuthorDate: Mon Oct 13 09:34:36 2025 +0300
IGNITE-26676 Add unit tests for serialization compatibility of all
partition Raft commands (#6743)
---
.../PartitionCommandsCompatibilityTest.java | 438 +++++++++++++++++++++
1 file changed, 438 insertions(+)
diff --git
a/modules/partition-replicator/src/test/java/org/apache/ignite/internal/partition/replicator/network/command/PartitionCommandsCompatibilityTest.java
b/modules/partition-replicator/src/test/java/org/apache/ignite/internal/partition/replicator/network/command/PartitionCommandsCompatibilityTest.java
new file mode 100644
index 00000000000..e442a23a928
--- /dev/null
+++
b/modules/partition-replicator/src/test/java/org/apache/ignite/internal/partition/replicator/network/command/PartitionCommandsCompatibilityTest.java
@@ -0,0 +1,438 @@
+/*
+ * 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.partition.replicator.network.command;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.ByteBuffer;
+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.hlc.HybridTimestamp;
+import org.apache.ignite.internal.network.MessageSerializationRegistryImpl;
+import
org.apache.ignite.internal.network.serialization.MessageSerializationRegistry;
+import
org.apache.ignite.internal.partition.replicator.network.PartitionReplicationMessagesFactory;
+import
org.apache.ignite.internal.partition.replicator.network.PartitionReplicationMessagesSerializationRegistryInitializer;
+import
org.apache.ignite.internal.partition.replicator.network.replication.BinaryRowMessage;
+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.message.ReplicaMessagesFactory;
+import
org.apache.ignite.internal.replicator.message.ReplicaMessagesSerializationRegistryInitializer;
+import org.apache.ignite.internal.replicator.message.TablePartitionIdMessage;
+import org.apache.ignite.internal.replicator.message.ZonePartitionIdMessage;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.apache.ignite.internal.tx.message.EnlistedPartitionGroupMessage;
+import org.apache.ignite.internal.tx.message.TxMessagesFactory;
+import
org.apache.ignite.internal.tx.message.TxMessagesSerializationRegistryInitializer;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Compatibility testing for serialization/deserialization of partition 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 PartitionCommandsCompatibilityTest extends BaseIgniteAbstractTest
{
+ private final MessageSerializationRegistry registry = new
MessageSerializationRegistryImpl();
+
+ private final Marshaller marshaller = new
ThreadLocalOptimizedMarshaller(registry);
+
+ private final PartitionReplicationMessagesFactory commandFactory = new
PartitionReplicationMessagesFactory();
+
+ private final ReplicaMessagesFactory replicaFactory = new
ReplicaMessagesFactory();
+
+ private final TxMessagesFactory txFactory = new TxMessagesFactory();
+
+ @BeforeEach
+ void setUp() {
+ new
PartitionReplicationMessagesSerializationRegistryInitializer().registerFactories(registry);
+ new
ReplicaMessagesSerializationRegistryInitializer().registerFactories(registry);
+ new
TxMessagesSerializationRegistryInitializer().registerFactories(registry);
+ }
+
+ @Test
+ void testBuildIndexCommand() {
+ BuildIndexCommand command =
decodeCommand("Ci0BRgIAAAAAAAAAACoAAAAAAAAARQ==");
+
+ assertEquals(69, command.indexId());
+ assertEquals(List.of(uuid()), command.rowIds());
+ assertTrue(command.finish());
+ }
+
+ @Test
+ void testBuildIndexCommandV2() {
+ BuildIndexCommandV2 command =
decodeCommand("CjIBRgIAAAAAAAAAACoAAAAAAAAARQg=");
+
+ assertEquals(69, command.indexId());
+ assertEquals(List.of(uuid()), command.rowIds());
+ assertTrue(command.finish());
+ assertEquals(7, command.tableId());
+ }
+
+ @Test
+ void testFinishTxCommandV1() {
+ FinishTxCommandV1 command =
decodeCommand("CikBSAFHAgkrLSJGAAAAAAAAAAAqAAAAAAAAAEU=");
+
+ assertEquals(initiatorTime(), command.initiatorTime());
+ assertEquals(safeTime(), command.safeTime());
+ assertEquals(uuid(), command.txId());
+ assertTrue(command.commit());
+ assertTrue(command.full());
+ assertEquals(commitTimestamp(), command.commitTimestamp());
+ assertEquals(List.of(tablePartitionId()), command.partitionIds());
+ }
+
+ @Test
+ void testFinishTxCommandV2() {
+ FinishTxCommandV2 command =
decodeCommand("CjMBSAFHAgYVCSwXDAMtIkYAAAAAAAAAACoAAAAAAAAARQ==");
+
+ assertEquals(initiatorTime(), command.initiatorTime());
+ assertEquals(safeTime(), command.safeTime());
+ assertEquals(uuid(), command.txId());
+ assertTrue(command.full());
+ assertTrue(command.commit());
+ assertEquals(commitTimestamp(), command.commitTimestamp());
+ assertEquals(List.of(enlistedPartitionGroup()), command.partitions());
+ }
+
+ @Test
+ void testUpdateAllCommand() {
+ UpdateAllCommand command = decodeCommand(
+
"CisBRwErAgAAAAAAAAAAKgAAAAAAAABFChkKEwMEAQIDAdMJRgkrLSIAAAAAAAAAACoAAAAAAAAARQAAAAAAAAAAKgAAAAAAAABF"
+ );
+
+ assertEquals(initiatorTime(), command.initiatorTime());
+ assertEquals(safeTime(), command.safeTime());
+ assertEquals(uuid(), command.txId());
+ assertTrue(command.full());
+ assertEquals(tablePartitionId(), command.commitPartitionId());
+ assertEquals(Map.of(uuid(), timedBinaryRowMessage()),
command.messageRowsToUpdate());
+ assertEquals(uuid(), command.txCoordinatorId());
+ assertEquals(42L, command.leaseStartTime());
+ }
+
+ @Test
+ void testUpdateAllCommandV2() {
+ UpdateAllCommandV2 command = decodeCommand(
+
"CjEBRwErAgAAAAAAAAAAKgAAAAAAAABFChkKEwMEAQIDAdMJRggJKy0iAAAAAAAAAAAqAAAAAAAAAEUAAAAAAAAAACoAAAAAAAAARQ=="
+ );
+
+ assertEquals(initiatorTime(), command.initiatorTime());
+ assertEquals(safeTime(), command.safeTime());
+ assertEquals(uuid(), command.txId());
+ assertTrue(command.full());
+ assertEquals(tablePartitionId(), command.commitPartitionId());
+ assertEquals(Map.of(uuid(), timedBinaryRowMessage()),
command.messageRowsToUpdate());
+ assertEquals(uuid(), command.txCoordinatorId());
+ assertEquals(42L, command.leaseStartTime());
+ assertEquals(7, command.tableId());
+ }
+
+ @Test
+ void testUpdateCommand() {
+ UpdateCommand command = decodeCommand(
+
"CiwBRwErChkKEwMEAQIDAdMJAAAAAAAAAAAqAAAAAAAAAEVGCSstIgAAAAAAAAAAKgAAAAAAAABFAAAAAAAAAAAqAAAAAAAAAEU="
+ );
+
+ assertEquals(initiatorTime(), command.initiatorTime());
+ assertEquals(safeTime(), command.safeTime());
+ assertEquals(uuid(), command.txId());
+ assertTrue(command.full());
+ assertEquals(tablePartitionId(), command.commitPartitionId());
+ assertEquals(uuid(), command.rowUuid());
+ assertEquals(timedBinaryRowMessage(), command.messageRowToUpdate());
+ assertEquals(uuid(), command.txCoordinatorId());
+ assertEquals(42L, command.leaseStartTime());
+ }
+
+ @Test
+ void testUpdateCommandV2() {
+ UpdateCommandV2 command = decodeCommand(
+
"CjABRwErChkKEwMEAQIDAdMJAAAAAAAAAAAqAAAAAAAAAEVGCAkrLSIAAAAAAAAAACoAAAAAAAAARQAAAAAAAAAAKgAAAAAAAABF"
+ );
+
+ assertEquals(initiatorTime(), command.initiatorTime());
+ assertEquals(safeTime(), command.safeTime());
+ assertEquals(uuid(), command.txId());
+ assertTrue(command.full());
+ assertEquals(tablePartitionId(), command.commitPartitionId());
+ assertEquals(uuid(), command.rowUuid());
+ assertEquals(timedBinaryRowMessage(), command.messageRowToUpdate());
+ assertEquals(uuid(), command.txCoordinatorId());
+ assertEquals(42L, command.leaseStartTime());
+ assertEquals(7, command.tableId());
+ }
+
+ @Test
+ void testUpdateMinimumActiveTxBeginTimeCommand() {
+ UpdateMinimumActiveTxBeginTimeCommand command =
decodeCommand("Ci5HRtMJ");
+
+ assertEquals(initiatorTime(), command.initiatorTime());
+ assertEquals(safeTime(), command.safeTime());
+ assertEquals(1234L, command.timestamp());
+ }
+
+ @Test
+ void testWriteIntentSwitchCommand() {
+ WriteIntentSwitchCommand command =
decodeCommand("CioBSAFHRgAAAAAAAAAAKgAAAAAAAABF");
+
+ assertEquals(initiatorTime(), command.initiatorTime());
+ assertEquals(safeTime(), command.safeTime());
+ assertEquals(uuid(), command.txId());
+ assertTrue(command.full());
+ assertTrue(command.commit());
+ assertEquals(commitTimestamp(), command.commitTimestamp());
+ }
+
+ @Test
+ void testWriteIntentSwitchCommandV2() {
+ WriteIntentSwitchCommandV2 command =
decodeCommand("Ci8BSAFHRgMJCAAAAAAAAAAAKgAAAAAAAABF");
+
+ assertEquals(initiatorTime(), command.initiatorTime());
+ assertEquals(safeTime(), command.safeTime());
+ assertEquals(uuid(), command.txId());
+ assertTrue(command.full());
+ assertTrue(command.commit());
+ assertEquals(commitTimestamp(), command.commitTimestamp());
+ assertEquals(Set.of(7, 8), command.tableIds());
+ }
+
+ private static HybridTimestamp initiatorTime() {
+ return HybridTimestamp.hybridTimestamp(70);
+ }
+
+ private static HybridTimestamp safeTime() {
+ return HybridTimestamp.hybridTimestamp(69);
+ }
+
+ private static HybridTimestamp commitTimestamp() {
+ return HybridTimestamp.hybridTimestamp(71);
+ }
+
+ private static UUID uuid() {
+ return new UUID(42, 69);
+ }
+
+ private TablePartitionIdMessage tablePartitionId() {
+ return replicaFactory.tablePartitionIdMessage()
+ .tableId(33)
+ .partitionId(44)
+ .build();
+ }
+
+ private ZonePartitionIdMessage zonePartitionId() {
+ return replicaFactory.zonePartitionIdMessage()
+ .zoneId(11)
+ .partitionId(22)
+ .build();
+ }
+
+ private EnlistedPartitionGroupMessage enlistedPartitionGroup() {
+ return txFactory.enlistedPartitionGroupMessage()
+ .groupId(zonePartitionId())
+ .tableIds(Set.of(33, 44))
+ .build();
+ }
+
+ private TimedBinaryRowMessage timedBinaryRowMessage() {
+ return commandFactory.timedBinaryRowMessage()
+ .binaryRowMessage(binaryRow())
+ .timestamp(HybridTimestamp.hybridTimestamp(1234))
+ .build();
+ }
+
+ private BinaryRowMessage binaryRow() {
+ return commandFactory.binaryRowMessage()
+ .binaryTuple(ByteBuffer.wrap(new byte[]{1, 2, 3}))
+ .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(
+ createBuildIndexCommand(),
+ createBuildIndexCommandV2(),
+ createFinishTxCommandV1(),
+ createFinishTxCommandV2(),
+ createUpdateAllCommand(),
+ createUpdateAllCommandV2(),
+ createUpdateCommand(),
+ createUpdateCommandV2(),
+ createUpdateMinimumActiveTxBeginTimeCommand(),
+ createWriteIntentSwitchCommand(),
+ createWriteIntentSwitchCommandV2()
+ );
+
+ for (Command c : commands) {
+ log.info(">>>>> Serialized command: [command={}, base64='{}']",
c.getClass().getSimpleName(), encodeCommand(c));
+ }
+ }
+
+ private WriteIntentSwitchCommandV2 createWriteIntentSwitchCommandV2() {
+ return commandFactory.writeIntentSwitchCommandV2()
+ .initiatorTime(initiatorTime())
+ .safeTime(safeTime())
+ .txId(uuid())
+ .full(true)
+ .commit(true)
+ .commitTimestamp(commitTimestamp())
+ .tableIds(Set.of(7, 8))
+ .build();
+ }
+
+ private WriteIntentSwitchCommand createWriteIntentSwitchCommand() {
+ return commandFactory.writeIntentSwitchCommand()
+ .initiatorTime(initiatorTime())
+ .safeTime(safeTime())
+ .txId(uuid())
+ .full(true)
+ .commit(true)
+ .commitTimestamp(commitTimestamp())
+ .build();
+ }
+
+ private UpdateMinimumActiveTxBeginTimeCommand
createUpdateMinimumActiveTxBeginTimeCommand() {
+ return commandFactory.updateMinimumActiveTxBeginTimeCommand()
+ .initiatorTime(initiatorTime())
+ .safeTime(safeTime())
+ .timestamp(1234L)
+ .build();
+ }
+
+ private UpdateCommandV2 createUpdateCommandV2() {
+ return commandFactory.updateCommandV2()
+ .initiatorTime(initiatorTime())
+ .safeTime(safeTime())
+ .txId(uuid())
+ .full(true)
+ .commitPartitionId(tablePartitionId())
+ .rowUuid(uuid())
+ .messageRowToUpdate(timedBinaryRowMessage())
+ .txCoordinatorId(uuid())
+ .leaseStartTime(42L)
+ .tableId(7)
+ .build();
+ }
+
+ private UpdateCommand createUpdateCommand() {
+ return commandFactory.updateCommand()
+ .initiatorTime(initiatorTime())
+ .safeTime(safeTime())
+ .txId(uuid())
+ .full(true)
+ .commitPartitionId(tablePartitionId())
+ .rowUuid(uuid())
+ .messageRowToUpdate(timedBinaryRowMessage())
+ .txCoordinatorId(uuid())
+ .leaseStartTime(42L)
+ .build();
+ }
+
+ private UpdateAllCommandV2 createUpdateAllCommandV2() {
+ return commandFactory.updateAllCommandV2()
+ .initiatorTime(initiatorTime())
+ .safeTime(safeTime())
+ .txId(uuid())
+ .full(true)
+ .commitPartitionId(tablePartitionId())
+ .messageRowsToUpdate(Map.of(uuid(), timedBinaryRowMessage()))
+ .txCoordinatorId(uuid())
+ .leaseStartTime(42L)
+ .tableId(7)
+ .build();
+ }
+
+ private UpdateAllCommand createUpdateAllCommand() {
+ return commandFactory.updateAllCommand()
+ .initiatorTime(initiatorTime())
+ .safeTime(safeTime())
+ .txId(uuid())
+ .full(true)
+ .commitPartitionId(tablePartitionId())
+ .messageRowsToUpdate(Map.of(uuid(), timedBinaryRowMessage()))
+ .txCoordinatorId(uuid())
+ .leaseStartTime(42L)
+ .build();
+ }
+
+ private FinishTxCommandV2 createFinishTxCommandV2() {
+ return commandFactory.finishTxCommandV2()
+ .initiatorTime(initiatorTime())
+ .safeTime(safeTime())
+ .txId(uuid())
+ .full(true)
+ .commit(true)
+ .commitTimestamp(commitTimestamp())
+ .partitions(List.of(enlistedPartitionGroup()))
+ .build();
+ }
+
+ private FinishTxCommandV1 createFinishTxCommandV1() {
+ return commandFactory.finishTxCommandV1()
+ .initiatorTime(initiatorTime())
+ .safeTime(safeTime())
+ .txId(uuid())
+ .full(true)
+ .commit(true)
+ .commitTimestamp(commitTimestamp())
+ .partitionIds(List.of(tablePartitionId()))
+ .build();
+ }
+
+ private BuildIndexCommandV2 createBuildIndexCommandV2() {
+ return commandFactory.buildIndexCommandV2()
+ .indexId(69)
+ .rowIds(List.of(uuid()))
+ .finish(true)
+ .tableId(7)
+ .build();
+ }
+
+ private BuildIndexCommand createBuildIndexCommand() {
+ return commandFactory.buildIndexCommand()
+ .indexId(69)
+ .rowIds(List.of(uuid()))
+ .finish(true)
+ .build();
+ }
+
+ private byte[] serializeCommand(Command c) {
+ return marshaller.marshall(c);
+ }
+
+ private String encodeCommand(Command c) {
+ return Base64.getEncoder().encodeToString(serializeCommand(c));
+ }
+}