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

rpuch 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 3f91738073 IGNITE-23583 Optimize HybridTimestamp serialization (#4664)
3f91738073 is described below

commit 3f9173807350886c6fd72a90c8aa57433450a64b
Author: Roman Puchkovskiy <[email protected]>
AuthorDate: Thu Oct 31 23:11:42 2024 +0400

    IGNITE-23583 Optimize HybridTimestamp serialization (#4664)
---
 .../ignite/internal/hlc/HybridTimestamp.java       | 80 ++++++++++++++++++++--
 .../apache/ignite/internal/util/GridUnsafe.java    |  2 +-
 .../ignite/internal/hlc/HybridTimestampTest.java   | 49 +++++++++++++
 .../AssignmentsSerializer.java                     | 10 +--
 .../AssignmentsSerializerTest.java                 | 43 +++++++++---
 .../ManualGroupRestartRequestSerializer.java       |  9 +--
 .../distributed/index/IndexMetaSerializer.java     | 10 +--
 .../DisasterRecoveryRequestSerializerTest.java     | 22 +++++-
 .../distributed/index/IndexMetaSerializerTest.java | 14 +++-
 .../ignite/internal/tx/TxMetaSerializer.java       |  6 +-
 .../ignite/internal/tx/TxMetaSerializerTest.java   | 15 +++-
 11 files changed, 226 insertions(+), 34 deletions(-)

diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/hlc/HybridTimestamp.java
 
b/modules/core/src/main/java/org/apache/ignite/internal/hlc/HybridTimestamp.java
index c309c9e68f..a7947892da 100644
--- 
a/modules/core/src/main/java/org/apache/ignite/internal/hlc/HybridTimestamp.java
+++ 
b/modules/core/src/main/java/org/apache/ignite/internal/hlc/HybridTimestamp.java
@@ -19,10 +19,13 @@ package org.apache.ignite.internal.hlc;
 
 import static 
org.apache.ignite.internal.lang.JavaLoggerFormatter.DATE_FORMATTER;
 
+import java.io.IOException;
 import java.io.Serializable;
 import java.time.Instant;
-import java.time.ZoneId;
+import java.time.ZoneOffset;
 import org.apache.ignite.internal.util.ByteUtils;
+import org.apache.ignite.internal.util.io.IgniteDataInput;
+import org.apache.ignite.internal.util.io.IgniteDataOutput;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -39,10 +42,10 @@ public final class HybridTimestamp implements 
Comparable<HybridTimestamp>, Seria
     public static final int LOGICAL_TIME_BITS_SIZE = 2 * Byte.SIZE;
 
     /** Mask to extract logical time. */
-    public static final long LOGICAL_TIME_MASK = (1L << 
LOGICAL_TIME_BITS_SIZE) - 1;
+    private static final long LOGICAL_TIME_MASK = (1L << 
LOGICAL_TIME_BITS_SIZE) - 1;
 
     /** Number of bits in "physical time" part. */
-    public static final int PHYSICAL_TIME_BITS_SIZE = 6 * Byte.SIZE;
+    static final int PHYSICAL_TIME_BITS_SIZE = 6 * Byte.SIZE;
 
     /** Timestamp size in bytes. */
     public static final int HYBRID_TIMESTAMP_SIZE = Long.BYTES;
@@ -201,7 +204,7 @@ public final class HybridTimestamp implements 
Comparable<HybridTimestamp>, Seria
 
     @Override
     public String toString() {
-        String formattedTime = 
DATE_FORMATTER.format(Instant.ofEpochMilli(getPhysical()).atZone(ZoneId.systemDefault()));
+        String formattedTime = 
DATE_FORMATTER.format(Instant.ofEpochMilli(getPhysical()).atOffset(ZoneOffset.UTC));
 
         return String.format("HybridTimestamp [physical=%s, logical=%d, 
composite=%d]", formattedTime, getLogical(), time);
     }
@@ -256,4 +259,73 @@ public final class HybridTimestamp implements 
Comparable<HybridTimestamp>, Seria
     public byte[] toBytes() {
         return ByteUtils.longToBytes(longValue());
     }
+
+    /**
+     * Writes this timestamp to the output.
+     *
+     * @param out Output to write to.
+     * @throws IOException If something goes wrong.
+     */
+    public void writeTo(IgniteDataOutput out) throws IOException {
+        long physical = getPhysical();
+
+        //noinspection NumericCastThatLosesPrecision
+        out.writeInt((int) (physical >> Short.SIZE));
+        out.writeShort((int) (physical & 0xFFFF));
+
+        out.writeVarInt(getLogical());
+    }
+
+    /**
+     * Writes a nullable timestamp to an output.
+     *
+     * @param timestamp Timestamp to write
+     * @param out Output to write to.
+     * @throws IOException If something goes wrong.
+     */
+    public static void write(@Nullable HybridTimestamp timestamp, 
IgniteDataOutput out) throws IOException {
+        if (timestamp == null) {
+            out.writeInt(0);
+            out.writeShort(0);
+            out.writeVarInt(0);
+        } else {
+            timestamp.writeTo(out);
+        }
+    }
+
+    /**
+     * Reads a timestamp written with {@link #writeTo(IgniteDataOutput)}.
+     *
+     * @param in Input from which to read.
+     * @throws IOException If something goes wrong.
+     */
+    public static @Nullable HybridTimestamp readNullableFrom(IgniteDataInput 
in) throws IOException {
+        long physicalHigh = in.readInt();
+        int physicalLow = in.readShort() & 0xFFFF;
+
+        long physical = (physicalHigh << Short.SIZE) | physicalLow;
+        int logical = in.readVarIntAsInt();
+
+        if (physical == 0 && logical == 0) {
+            return null;
+        }
+
+        return new HybridTimestamp(physical, logical);
+    }
+
+    /**
+     * Reads a timestamp written with {@link #writeTo(IgniteDataOutput)}.
+     *
+     * @param in Input from which to read.
+     * @throws IOException If something goes wrong.
+     */
+    public static HybridTimestamp readFrom(IgniteDataInput in) throws 
IOException {
+        HybridTimestamp ts = readNullableFrom(in);
+
+        if (ts == null) {
+            throw new IOException("A non-null timestamp is expected");
+        }
+
+        return ts;
+    }
 }
diff --git 
a/modules/core/src/main/java/org/apache/ignite/internal/util/GridUnsafe.java 
b/modules/core/src/main/java/org/apache/ignite/internal/util/GridUnsafe.java
index da35b01065..c1fa4c35ff 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/GridUnsafe.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/GridUnsafe.java
@@ -45,7 +45,7 @@ import sun.misc.Unsafe;
  * {@code getXxx(byte[] arr, long off)} and corresponding methods with {@code 
LE} suffix are alignment aware
  * and can be safely used with unaligned pointers.</li>
  * <li>All {@code putXxxField(Object obj, long fieldOff, xxx val)} and {@code 
getXxxField(Object obj, long fieldOff)}
- * methods are not alignment aware and can't be safely used with unaligned 
pointers. This methods can be safely used
+ * methods are not alignment aware and can't be safely used with unaligned 
pointers. These methods can be safely used
  * for object field values access because all object fields addresses are 
aligned.</li>
  * <li>All {@code putXxxLE(...)} and {@code getXxxLE(...)} methods assumes 
that the byte order is fixed as little-endian
  * while native byte order is big-endian. So it is client code responsibility 
to check native byte order before
diff --git 
a/modules/core/src/test/java/org/apache/ignite/internal/hlc/HybridTimestampTest.java
 
b/modules/core/src/test/java/org/apache/ignite/internal/hlc/HybridTimestampTest.java
index 0df7b04a58..13739bf28d 100644
--- 
a/modules/core/src/test/java/org/apache/ignite/internal/hlc/HybridTimestampTest.java
+++ 
b/modules/core/src/test/java/org/apache/ignite/internal/hlc/HybridTimestampTest.java
@@ -23,10 +23,16 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.nullValue;
 import static org.hamcrest.Matchers.startsWith;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+import java.io.IOException;
+import org.apache.ignite.internal.util.io.IgniteDataInput;
+import org.apache.ignite.internal.util.io.IgniteDataOutput;
+import org.apache.ignite.internal.util.io.IgniteUnsafeDataInput;
+import org.apache.ignite.internal.util.io.IgniteUnsafeDataOutput;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -158,4 +164,47 @@ class HybridTimestampTest {
 
         assertThat(ts.roundUpToPhysicalTick(), is(new HybridTimestamp(2, 0)));
     }
+
+    @Test
+    void serializationAndDeserializationForNonNull() throws Exception {
+        HybridTimestamp originalTs = new 
HybridTimestamp(System.currentTimeMillis(), 2);
+
+        IgniteDataOutput out = new IgniteUnsafeDataOutput(100);
+
+        originalTs.writeTo(out);
+
+        IgniteDataInput in = new IgniteUnsafeDataInput(out.array());
+
+        HybridTimestamp restoredTs = HybridTimestamp.readFrom(in);
+
+        assertThat(restoredTs, is(originalTs));
+
+        assertThat(in.available(), is(0));
+    }
+
+    @Test
+    void serializationAndDeserializationForNull() throws Exception {
+        IgniteDataOutput out = new IgniteUnsafeDataOutput(100);
+
+        HybridTimestamp.write(null, out);
+
+        IgniteDataInput in = new IgniteUnsafeDataInput(out.array());
+
+        assertThat(HybridTimestamp.readNullableFrom(in), is(nullValue()));
+
+        assertThat(in.available(), is(0));
+    }
+
+    @Test
+    void readFromFailsWhenDeserializingNull() throws Exception {
+        IgniteDataOutput out = new IgniteUnsafeDataOutput(100);
+        HybridTimestamp.write(null, out);
+
+        IgniteDataInput in = new IgniteUnsafeDataInput(out.array());
+
+        IOException ex = assertThrows(IOException.class, () -> 
HybridTimestamp.readFrom(in));
+        assertThat(ex.getMessage(), is("A non-null timestamp is expected"));
+
+        assertThat(in.available(), is(0));
+    }
 }
diff --git 
a/modules/partition-distribution/src/main/java/org/apache/ignite/internal/partitiondistribution/AssignmentsSerializer.java
 
b/modules/partition-distribution/src/main/java/org/apache/ignite/internal/partitiondistribution/AssignmentsSerializer.java
index c3d2620787..d5ee560ed6 100644
--- 
a/modules/partition-distribution/src/main/java/org/apache/ignite/internal/partitiondistribution/AssignmentsSerializer.java
+++ 
b/modules/partition-distribution/src/main/java/org/apache/ignite/internal/partitiondistribution/AssignmentsSerializer.java
@@ -17,9 +17,12 @@
 
 package org.apache.ignite.internal.partitiondistribution;
 
+import static org.apache.ignite.internal.hlc.HybridTimestamp.hybridTimestamp;
+
 import java.io.IOException;
 import java.util.HashSet;
 import java.util.Set;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.util.io.IgniteDataInput;
 import org.apache.ignite.internal.util.io.IgniteDataOutput;
 import org.apache.ignite.internal.versioned.VersionedSerializer;
@@ -39,8 +42,7 @@ public class AssignmentsSerializer extends 
VersionedSerializer<Assignments> {
         }
 
         out.writeBoolean(assignments.force());
-        // Writing long and not varlong as the latter will take 9 bytes for 
timestamps.
-        out.writeLong(assignments.timestamp());
+        hybridTimestamp(assignments.timestamp()).writeTo(out);
     }
 
     private static void writeAssignment(Assignment assignment, 
IgniteDataOutput out) throws IOException {
@@ -52,9 +54,9 @@ public class AssignmentsSerializer extends 
VersionedSerializer<Assignments> {
     protected Assignments readExternalData(byte protoVer, IgniteDataInput in) 
throws IOException {
         Set<Assignment> nodes = readNodes(in);
         boolean force = in.readBoolean();
-        long timestamp = in.readLong();
+        HybridTimestamp timestamp = HybridTimestamp.readFrom(in);
 
-        return force ? Assignments.forced(nodes, timestamp) : 
Assignments.of(nodes, timestamp);
+        return force ? Assignments.forced(nodes, timestamp.longValue()) : 
Assignments.of(nodes, timestamp.longValue());
     }
 
     private static Set<Assignment> readNodes(IgniteDataInput in) throws 
IOException {
diff --git 
a/modules/partition-distribution/src/test/java/org/apache/ignite/internal/partitiondistribution/AssignmentsSerializerTest.java
 
b/modules/partition-distribution/src/test/java/org/apache/ignite/internal/partitiondistribution/AssignmentsSerializerTest.java
index 50692a61f9..d1f9cc1cec 100644
--- 
a/modules/partition-distribution/src/test/java/org/apache/ignite/internal/partitiondistribution/AssignmentsSerializerTest.java
+++ 
b/modules/partition-distribution/src/test/java/org/apache/ignite/internal/partitiondistribution/AssignmentsSerializerTest.java
@@ -24,25 +24,37 @@ import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
 
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.time.ZoneOffset;
 import java.util.Base64;
 import java.util.List;
 import java.util.Set;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.versioned.VersionedSerialization;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 
 class AssignmentsSerializerTest {
-    private static final String NOT_FORCED_ASSIGNMENTS_SERIALIZED_WITH_V1 = 
"Ae++QwMCYQECYgAA6AMAAAAAAAA=";
-    private static final String FORCED_ASSIGNMENTS_SERIALIZED_WITH_V1 = 
"Ae++QwMCYQECYgAB6AMAAAAAAAA=";
+    private static final String NOT_FORCED_ASSIGNMENTS_SERIALIZED_WITH_V1 = 
"Ae++QwMEYWJjAQRkZWYAAFHCjAEA9AY=";
+    private static final String FORCED_ASSIGNMENTS_SERIALIZED_WITH_V1 = 
"Ae++QwMEYWJjAQRkZWYAAVHCjAEA9AY=";
 
     private final AssignmentsSerializer serializer = new 
AssignmentsSerializer();
 
+    private static final long BASE_PHYSICAL_TIME = LocalDateTime.of(2024, 
Month.JANUARY, 1, 0, 0)
+            .atOffset(ZoneOffset.UTC)
+            .toInstant()
+            .toEpochMilli();
+
+    private static long baseTimestamp(int logical) {
+        return new HybridTimestamp(BASE_PHYSICAL_TIME, logical).longValue();
+    }
+
     @ParameterizedTest
     @ValueSource(booleans = {true, false})
     void serializationAndDeserialization(boolean force) {
-        Set<Assignment> nodes = Set.of(Assignment.forPeer("abc"), 
Assignment.forLearner("def"));
-        Assignments originalAssignments = force ? Assignments.forced(nodes, 
1000L) : Assignments.of(nodes, 1000L);
+        Assignments originalAssignments = testAssignments(force);
 
         byte[] bytes = VersionedSerialization.toBytes(originalAssignments, 
serializer);
         Assignments restoredAssignments = 
VersionedSerialization.fromBytes(bytes, serializer);
@@ -50,6 +62,14 @@ class AssignmentsSerializerTest {
         assertThat(restoredAssignments, equalTo(originalAssignments));
     }
 
+    private static Assignments testAssignments(boolean force) {
+        Set<Assignment> nodes = Set.of(Assignment.forPeer("abc"), 
Assignment.forLearner("def"));
+
+        return force
+                ? Assignments.forced(nodes, baseTimestamp(5))
+                : Assignments.of(nodes, baseTimestamp(5));
+    }
+
     @Test
     void v1NotForcedCanBeDeserialized() {
         byte[] bytes = 
Base64.getDecoder().decode(NOT_FORCED_ASSIGNMENTS_SERIALIZED_WITH_V1);
@@ -58,7 +78,7 @@ class AssignmentsSerializerTest {
         assertNodesFromV1(restoredAssignments);
 
         assertThat(restoredAssignments.force(), is(false));
-        assertThat(restoredAssignments.timestamp(), is(1000L));
+        assertThat(restoredAssignments.timestamp(), is(baseTimestamp(5)));
     }
 
     @Test
@@ -69,7 +89,14 @@ class AssignmentsSerializerTest {
         assertNodesFromV1(restoredAssignments);
 
         assertThat(restoredAssignments.force(), is(true));
-        assertThat(restoredAssignments.timestamp(), is(1000L));
+        assertThat(restoredAssignments.timestamp(), is(baseTimestamp(5)));
+    }
+
+    @SuppressWarnings("unused")
+    private String v1Base64(boolean force) {
+        Assignments originalAssignments = testAssignments(force);
+        byte[] v1Bytes = VersionedSerialization.toBytes(originalAssignments, 
serializer);
+        return Base64.getEncoder().encodeToString(v1Bytes);
     }
 
     private static void assertNodesFromV1(Assignments restoredAssignments) {
@@ -79,11 +106,11 @@ class AssignmentsSerializerTest {
                 .collect(toList());
 
         Assignment assignment1 = orderedNodes.get(0);
-        assertThat(assignment1.consistentId(), is("a"));
+        assertThat(assignment1.consistentId(), is("abc"));
         assertThat(assignment1.isPeer(), is(true));
 
         Assignment assignment2 = orderedNodes.get(1);
-        assertThat(assignment2.consistentId(), is("b"));
+        assertThat(assignment2.consistentId(), is("def"));
         assertThat(assignment2.isPeer(), is(false));
     }
 }
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/disaster/ManualGroupRestartRequestSerializer.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/disaster/ManualGroupRestartRequestSerializer.java
index e8a4348828..023e1c3ea5 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/disaster/ManualGroupRestartRequestSerializer.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/disaster/ManualGroupRestartRequestSerializer.java
@@ -17,12 +17,14 @@
 
 package org.apache.ignite.internal.table.distributed.disaster;
 
+import static org.apache.ignite.internal.hlc.HybridTimestamp.hybridTimestamp;
 import static 
org.apache.ignite.internal.table.distributed.disaster.DisasterRecoveryRequestsSerialization.readVarIntSet;
 import static 
org.apache.ignite.internal.table.distributed.disaster.DisasterRecoveryRequestsSerialization.writeVarIntSet;
 
 import java.io.IOException;
 import java.util.Set;
 import java.util.UUID;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.util.io.IgniteDataInput;
 import org.apache.ignite.internal.util.io.IgniteDataOutput;
 import org.apache.ignite.internal.versioned.VersionedSerializer;
@@ -41,8 +43,7 @@ class ManualGroupRestartRequestSerializer extends 
VersionedSerializer<ManualGrou
         out.writeVarInt(request.tableId());
         writeVarIntSet(request.partitionIds(), out);
         writeStringSet(request.nodeNames(), out);
-        // Writing long and not a varlong as the latter requires 9 bytes for 
hybrid timestamps.
-        out.writeLong(request.assignmentsTimestamp());
+        hybridTimestamp(request.assignmentsTimestamp()).writeTo(out);
     }
 
     @Override
@@ -52,8 +53,8 @@ class ManualGroupRestartRequestSerializer extends 
VersionedSerializer<ManualGrou
         int tableId = in.readVarIntAsInt();
         Set<Integer> partitionIds = readVarIntSet(in);
         Set<String> nodeNames = readStringSet(in);
-        long assignmentsTimestamp = in.readLong();
+        HybridTimestamp assignmentsTimestamp = HybridTimestamp.readFrom(in);
 
-        return new ManualGroupRestartRequest(operationId, zoneId, tableId, 
partitionIds, nodeNames, assignmentsTimestamp);
+        return new ManualGroupRestartRequest(operationId, zoneId, tableId, 
partitionIds, nodeNames, assignmentsTimestamp.longValue());
     }
 }
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/index/IndexMetaSerializer.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/index/IndexMetaSerializer.java
index 0462bcfb94..a2aefed94d 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/index/IndexMetaSerializer.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/index/IndexMetaSerializer.java
@@ -17,10 +17,13 @@
 
 package org.apache.ignite.internal.table.distributed.index;
 
+import static org.apache.ignite.internal.hlc.HybridTimestamp.hybridTimestamp;
+
 import java.io.IOException;
 import java.util.EnumMap;
 import java.util.Map;
 import java.util.Map.Entry;
+import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.util.io.IgniteDataInput;
 import org.apache.ignite.internal.util.io.IgniteDataOutput;
 import org.apache.ignite.internal.versioned.VersionedSerializer;
@@ -46,8 +49,7 @@ class IndexMetaSerializer extends 
VersionedSerializer<IndexMeta> {
 
             MetaIndexStatusChange change = entry.getValue();
             out.writeVarInt(change.catalogVersion());
-            // Writing long and not varlong as the latter requires 9 bytes.
-            out.writeLong(change.activationTimestamp());
+            hybridTimestamp(change.activationTimestamp()).writeTo(out);
         }
     }
 
@@ -80,8 +82,8 @@ class IndexMetaSerializer extends 
VersionedSerializer<IndexMeta> {
             MetaIndexStatus status = 
MetaIndexStatus.findByCode(in.readVarIntAsInt());
 
             int catalogVersion = in.readVarIntAsInt();
-            long activationTimestamp = in.readLong();
-            MetaIndexStatusChange change = new 
MetaIndexStatusChange(catalogVersion, activationTimestamp);
+            HybridTimestamp activationTimestamp = HybridTimestamp.readFrom(in);
+            MetaIndexStatusChange change = new 
MetaIndexStatusChange(catalogVersion, activationTimestamp.longValue());
 
             map.put(status, change);
         }
diff --git 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/disaster/DisasterRecoveryRequestSerializerTest.java
 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/disaster/DisasterRecoveryRequestSerializerTest.java
index 432b6f3800..39c95aee15 100644
--- 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/disaster/DisasterRecoveryRequestSerializerTest.java
+++ 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/disaster/DisasterRecoveryRequestSerializerTest.java
@@ -28,6 +28,9 @@ import 
org.apache.ignite.internal.versioned.VersionedSerialization;
 import org.junit.jupiter.api.Test;
 
 class DisasterRecoveryRequestSerializerTest {
+    private static final String MANUAL_GROUP_UPDATE_REQUEST_V1_BASE64 = 
"Ae++QwEB775D782rkHhWNBIhQ2WHCbrc/ukH0Q+5FwQWDCA=";
+    private static final String MANUAL_GROUP_RESTART_REQUEST_V1_BASE64 = 
"Ae++QwIB775D782rkHhWNBIhQ2WHCbrc/tEPuRcEIBYMAwJiAmH///9///+AgAQ=";
+
     private final DisasterRecoveryRequestSerializer serializer = new 
DisasterRecoveryRequestSerializer();
 
     @Test
@@ -52,7 +55,7 @@ class DisasterRecoveryRequestSerializerTest {
 
     @Test
     void v1OfManualGroupUpdateRequestCanBeDeserialized() {
-        byte[] bytes = 
Base64.getDecoder().decode("Ae++QwEB775D782rkHhWNBIhQ2WHCbrc/ukH0Q+5FwQWDCA=");
+        byte[] bytes = 
Base64.getDecoder().decode(MANUAL_GROUP_UPDATE_REQUEST_V1_BASE64);
         ManualGroupUpdateRequest restoredRequest = (ManualGroupUpdateRequest) 
VersionedSerialization.fromBytes(bytes, serializer);
 
         assertThat(restoredRequest.operationId(), is(new 
UUID(0x1234567890ABCDEFL, 0xFEDCBA0987654321L)));
@@ -86,7 +89,7 @@ class DisasterRecoveryRequestSerializerTest {
 
     @Test
     void v1OfManualGroupRestartRequestCanBeDeserialized() {
-        byte[] bytes = 
Base64.getDecoder().decode("Ae++QwIB775D782rkHhWNBIhQ2WHCbrc/tEPuRcEDCAWAwJiAmH/////////fw==");
+        byte[] bytes = 
Base64.getDecoder().decode(MANUAL_GROUP_RESTART_REQUEST_V1_BASE64);
         ManualGroupRestartRequest restoredRequest = 
(ManualGroupRestartRequest) VersionedSerialization.fromBytes(bytes, serializer);
 
         assertThat(restoredRequest.operationId(), is(new 
UUID(0x1234567890ABCDEFL, 0xFEDCBA0987654321L)));
@@ -96,4 +99,19 @@ class DisasterRecoveryRequestSerializerTest {
         assertThat(restoredRequest.nodeNames(), is(Set.of("a", "b")));
         assertThat(restoredRequest.assignmentsTimestamp(), 
is(HybridTimestamp.MAX_VALUE.longValue()));
     }
+
+    @SuppressWarnings("unused")
+    private String manualGroupRestartRequestV1Base64() {
+        var originalRequest = new ManualGroupRestartRequest(
+                new UUID(0x1234567890ABCDEFL, 0xFEDCBA0987654321L),
+                2000,
+                3000,
+                Set.of(11, 21, 31),
+                Set.of("a", "b"),
+                HybridTimestamp.MAX_VALUE.longValue()
+        );
+
+        byte[] v1Bytes = VersionedSerialization.toBytes(originalRequest, 
serializer);
+        return Base64.getEncoder().encodeToString(v1Bytes);
+    }
 }
diff --git 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/index/IndexMetaSerializerTest.java
 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/index/IndexMetaSerializerTest.java
index c15c2be7ae..e57e139962 100644
--- 
a/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/index/IndexMetaSerializerTest.java
+++ 
b/modules/table/src/test/java/org/apache/ignite/internal/table/distributed/index/IndexMetaSerializerTest.java
@@ -33,6 +33,9 @@ import 
org.apache.ignite.internal.versioned.VersionedSerialization;
 import org.junit.jupiter.api.Test;
 
 class IndexMetaSerializerTest {
+    private static final String V1_SERIALIZED_BASE64 = 
"Ae++Q+kH0Q+5F6EfBmluZGV4BgcEBQAAAAAAAJEDAgMAAAAAAADJAQYHAAAAAAAA2QQFBgAAAAAAAPU"
+            + "DAwQAAAAAAACtAgECAAAAAAAAZQ==";
+
     private final IndexMetaSerializer serializer = new IndexMetaSerializer();
 
     @Test
@@ -64,8 +67,7 @@ class IndexMetaSerializerTest {
 
     @Test
     void v1CanBeDeserialized() {
-        byte[] bytes = 
Base64.getDecoder().decode("Ae++Q+kH0Q+5F6EfBmluZGV4BgcCA8gAAAAAAAAAAwQsAQAAAAAAAAECZAAAAAAAAAAFBvQBAAAAAAAABAWQ"
-                + "AQAAAAAAAAYHWAIAAAAAAAA=");
+        byte[] bytes = Base64.getDecoder().decode(V1_SERIALIZED_BASE64);
         IndexMeta restoredMeta = VersionedSerialization.fromBytes(bytes, 
serializer);
 
         assertThat(restoredMeta.catalogVersion(), is(1000));
@@ -76,4 +78,12 @@ class IndexMetaSerializerTest {
         assertThat(restoredMeta.status(), is(READ_ONLY));
         assertThat(restoredMeta.statusChanges(), 
equalTo(originalStatusChanges()));
     }
+
+    @SuppressWarnings("unused")
+    private String v1Base64() {
+        IndexMeta originalMeta = new IndexMeta(1000, 2000, 3000, 4000, 
"index", READ_ONLY, originalStatusChanges());
+
+        byte[] v1Bytes = VersionedSerialization.toBytes(originalMeta, 
serializer);
+        return Base64.getEncoder().encodeToString(v1Bytes);
+    }
 }
diff --git 
a/modules/transactions/src/main/java/org/apache/ignite/internal/tx/TxMetaSerializer.java
 
b/modules/transactions/src/main/java/org/apache/ignite/internal/tx/TxMetaSerializer.java
index 774a5c4b36..51f5df511a 100644
--- 
a/modules/transactions/src/main/java/org/apache/ignite/internal/tx/TxMetaSerializer.java
+++ 
b/modules/transactions/src/main/java/org/apache/ignite/internal/tx/TxMetaSerializer.java
@@ -43,16 +43,14 @@ public class TxMetaSerializer extends 
VersionedSerializer<TxMeta> {
             out.writeVarInt(partitionId.partitionId());
         }
 
-        HybridTimestamp commitTimestamp = meta.commitTimestamp();
-        // Using long and not varlong as the latter requires 9 bytes for 
hybrid timestamps.
-        out.writeLong(HybridTimestamp.hybridTimestampToLong(commitTimestamp));
+        HybridTimestamp.write(meta.commitTimestamp(), out);
     }
 
     @Override
     protected TxMeta readExternalData(byte protoVer, IgniteDataInput in) 
throws IOException {
         TxState state = TxState.fromOrdinal(in.readVarIntAsInt());
         List<TablePartitionId> enlistedPartitions = readEnlistedPartitions(in);
-        HybridTimestamp commitTimestamp = 
HybridTimestamp.nullableHybridTimestamp(in.readLong());
+        HybridTimestamp commitTimestamp = HybridTimestamp.readNullableFrom(in);
 
         return new TxMeta(state, enlistedPartitions, commitTimestamp);
     }
diff --git 
a/modules/transactions/src/test/java/org/apache/ignite/internal/tx/TxMetaSerializerTest.java
 
b/modules/transactions/src/test/java/org/apache/ignite/internal/tx/TxMetaSerializerTest.java
index eec9c7727e..ac28019da3 100644
--- 
a/modules/transactions/src/test/java/org/apache/ignite/internal/tx/TxMetaSerializerTest.java
+++ 
b/modules/transactions/src/test/java/org/apache/ignite/internal/tx/TxMetaSerializerTest.java
@@ -30,6 +30,8 @@ import 
org.apache.ignite.internal.versioned.VersionedSerialization;
 import org.junit.jupiter.api.Test;
 
 class TxMetaSerializerTest {
+    private static final String V1_SERIALIZED_BASE64 = 
"Ae++QwUD6QcQ0Q8a////f///gIAE";
+
     private final TxMetaSerializer serializer = new TxMetaSerializer();
 
     @Test
@@ -62,11 +64,22 @@ class TxMetaSerializerTest {
 
     @Test
     void v1CanBeDeserialized() {
-        byte[] bytes = 
Base64.getDecoder().decode("Ae++QwUD6QcQ0Q8a/////////38=");
+        byte[] bytes = Base64.getDecoder().decode(V1_SERIALIZED_BASE64);
         TxMeta restoredMeta = VersionedSerialization.fromBytes(bytes, 
serializer);
 
         assertThat(restoredMeta.txState(), is(TxState.ABANDONED));
         assertThat(restoredMeta.enlistedPartitions(), contains(new 
TablePartitionId(1000, 15), new TablePartitionId(2000, 25)));
         assertThat(restoredMeta.commitTimestamp(), 
is(HybridTimestamp.MAX_VALUE));
     }
+
+    @SuppressWarnings("unused")
+    private String v1SerializedBase64() {
+        TxMeta originalMeta = new TxMeta(
+                TxState.ABANDONED,
+                List.of(new TablePartitionId(1000, 15), new 
TablePartitionId(2000, 25)),
+                HybridTimestamp.MAX_VALUE
+        );
+        byte[] v1Bytes = VersionedSerialization.toBytes(originalMeta, 
serializer);
+        return Base64.getEncoder().encodeToString(v1Bytes);
+    }
 }

Reply via email to