Define RaftVersions.ARGON_VERSION 13/103513/9
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 2 Dec 2022 08:00:40 +0000 (09:00 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 2 Dec 2022 11:38:13 +0000 (12:38 +0100)
Our serialization proxies result in a unnecessarity-big footprint.
Define their replacements for forward compatibility with Argon raft
version.

JIRA: CONTROLLER-2058
Change-Id: I545485c6abdf16f6d81a48672b36eb83613013e1
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
22 files changed:
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftVersions.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/TimeoutNow.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractLeader.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/FI.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/client/messages/Shutdown.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AE.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AR.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntriesReply.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/IR.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/IS.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RV.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/VR.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/AJE.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/DE.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/LE.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/NP.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/SS.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/UT.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotTest.java
opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/persisted/NoopPayloadTest.java [new file with mode: 0644]

index a81dd48e28e977055eae8772f14c74051147d3e5..646a57a3fe3792560f20440b1cfad780e86b6f38 100644 (file)
@@ -20,6 +20,7 @@ public final class RaftVersions {
     @Deprecated(since = "7.0.0", forRemoval = true)
     public static final short BORON_VERSION = 3;
     public static final short FLUORINE_VERSION = 4;
+    public static final short ARGON_VERSION = 5;
     public static final short CURRENT_VERSION = FLUORINE_VERSION;
 
     private RaftVersions() {
index b212250dd4984828d3c57f8a70f0a177da5cff5c..780487b6ab714228d4d460cb3bd8f9f96f4ab58b 100644 (file)
@@ -16,18 +16,27 @@ import java.io.Serializable;
  * @author Thomas Pantelis
  */
 public final class TimeoutNow implements Serializable, ControlMessage {
+    @java.io.Serial
     private static final long serialVersionUID = 1L;
+
     public static final TimeoutNow INSTANCE = new TimeoutNow();
 
     private TimeoutNow() {
         // Hidden on purpose
     }
 
+    @java.io.Serial
+    private Object readResolve() {
+        return INSTANCE;
+    }
+
+    @java.io.Serial
     private Object writeReplace() {
         return new Proxy();
     }
 
     private static class Proxy extends EmptyExternalizableProxy {
+        @java.io.Serial
         private static final long serialVersionUID = 1L;
 
         // checkstyle flags the public modifier as redundant which really doesn't make sense since it clearly isn't
index 0188a6df1ac387e603962d838def314a7dab1c8d..6bccde0d622b48ecb2a9f97cd4e41aecca3dbf6f 100644 (file)
@@ -999,8 +999,8 @@ public abstract class AbstractLeader extends AbstractRaftActorBehavior {
                         chunkIndex,
                         installSnapshotState.getTotalChunks(),
                         OptionalInt.of(installSnapshotState.getLastChunkHashCode()),
-                        serverConfig
-                ).toSerializable(followerLogInfo.getRaftVersion()),
+                        serverConfig,
+                        followerLogInfo.getRaftVersion()),
                 actor()
         );
     }
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/FI.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/FI.java
new file mode 100644 (file)
index 0000000..79c605a
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.behaviors;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+/**
+ * Serialization proxy for {@link FollowerIdentifier}.
+ */
+final class FI implements Externalizable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    private String value;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public FI() {
+        // For Externalizable
+    }
+
+    FI(final String value) {
+        this.value = requireNonNull(value);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        out.writeObject(value);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        value = (String) in.readObject();
+    }
+
+    @java.io.Serial
+    private Object readResolve() {
+        return new FollowerIdentifier(value);
+    }
+}
index b670243b42f3167cc124f0b929d2ed9b8079e495..29f291f5cf524b0eff88d67a0f7be50a67602abb 100644 (file)
@@ -19,18 +19,27 @@ import org.opendaylight.controller.cluster.raft.base.messages.EmptyExternalizabl
  * @author Thomas Pantelis
  */
 public final class Shutdown implements Serializable, ControlMessage {
+    @java.io.Serial
     private static final long serialVersionUID = 1L;
+
     public static final Shutdown INSTANCE = new Shutdown();
 
     private Shutdown() {
         // Hidden on purpose
     }
 
+    @java.io.Serial
+    private Object readResolve() {
+        return INSTANCE;
+    }
+
+    @java.io.Serial
     private Object writeReplace() {
         return new Proxy();
     }
 
     private static class Proxy extends EmptyExternalizableProxy {
+        @java.io.Serial
         private static final long serialVersionUID = 1L;
 
         // checkstyle flags the public modifier as redundant which really doesn't make sense since it clearly isn't
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AE.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AE.java
new file mode 100644 (file)
index 0000000..402cb72
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.messages;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.ArrayList;
+import org.opendaylight.controller.cluster.raft.RaftVersions;
+import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
+import org.opendaylight.controller.cluster.raft.persisted.SimpleReplicatedLogEntry;
+import org.opendaylight.yangtools.concepts.WritableObjects;
+
+/**
+ * Argon serialization proxy for {@link AppendEntries}.
+ */
+final class AE implements Externalizable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    private AppendEntries appendEntries;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public AE() {
+        // For Externalizable
+    }
+
+    AE(final AppendEntries appendEntries) {
+        this.appendEntries = requireNonNull(appendEntries);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        out.writeShort(appendEntries.getLeaderRaftVersion());
+        WritableObjects.writeLong(out, appendEntries.getTerm());
+        out.writeObject(appendEntries.getLeaderId());
+
+        WritableObjects.writeLongs(out, appendEntries.getPrevLogTerm(), appendEntries.getPrevLogIndex());
+        WritableObjects.writeLongs(out, appendEntries.getLeaderCommit(), appendEntries.getReplicatedToAllIndex());
+
+        out.writeShort(appendEntries.getPayloadVersion());
+
+        final var entries = appendEntries.getEntries();
+        out.writeInt(entries.size());
+        for (var e : entries) {
+            WritableObjects.writeLongs(out, e.getIndex(), e.getTerm());
+            out.writeObject(e.getData());
+        }
+
+        out.writeObject(appendEntries.getLeaderAddress().orElse(null));
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        short leaderRaftVersion = in.readShort();
+        long term = WritableObjects.readLong(in);
+        String leaderId = (String) in.readObject();
+
+        byte hdr = WritableObjects.readLongHeader(in);
+        long prevLogTerm = WritableObjects.readFirstLong(in, hdr);
+        long prevLogIndex = WritableObjects.readSecondLong(in, hdr);
+
+        hdr = WritableObjects.readLongHeader(in);
+        long leaderCommit = WritableObjects.readFirstLong(in, hdr);
+        long replicatedToAllIndex = WritableObjects.readSecondLong(in, hdr);
+        short payloadVersion = in.readShort();
+
+        int size = in.readInt();
+        var entries = new ArrayList<ReplicatedLogEntry>(size);
+        for (int i = 0; i < size; i++) {
+            hdr = WritableObjects.readLongHeader(in);
+            entries.add(new SimpleReplicatedLogEntry(WritableObjects.readFirstLong(in, hdr),
+                WritableObjects.readSecondLong(in, hdr), (Payload) in.readObject()));
+        }
+
+        String leaderAddress = (String)in.readObject();
+
+        appendEntries = new AppendEntries(term, leaderId, prevLogIndex, prevLogTerm, entries, leaderCommit,
+                replicatedToAllIndex, payloadVersion, RaftVersions.CURRENT_VERSION, leaderRaftVersion,
+                leaderAddress);
+    }
+
+    @java.io.Serial
+    private Object readResolve() {
+        return verifyNotNull(appendEntries);
+    }
+}
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AR.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AR.java
new file mode 100644 (file)
index 0000000..6aa2ed8
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.messages;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.controller.cluster.raft.RaftVersions;
+import org.opendaylight.yangtools.concepts.WritableObjects;
+
+/**
+ * Serialization proxy for {@link AppendEntriesReply}.
+ */
+final class AR implements Externalizable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    // Flag bits
+    private static final int SUCCESS                = 0x10;
+    private static final int FORCE_INSTALL_SNAPSHOT = 0x20;
+    private static final int NEEDS_LEADER_ADDRESS   = 0x40;
+
+    private AppendEntriesReply appendEntriesReply;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public AR() {
+        // For Externalizable
+    }
+
+    AR(final AppendEntriesReply appendEntriesReply) {
+        this.appendEntriesReply = requireNonNull(appendEntriesReply);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        out.writeShort(appendEntriesReply.getRaftVersion());
+
+        int flags = 0;
+        if (appendEntriesReply.isSuccess()) {
+            flags |= SUCCESS;
+        }
+        if (appendEntriesReply.isForceInstallSnapshot()) {
+            flags |= FORCE_INSTALL_SNAPSHOT;
+        }
+        if (appendEntriesReply.isNeedsLeaderAddress()) {
+            flags |= NEEDS_LEADER_ADDRESS;
+        }
+        WritableObjects.writeLong(out, appendEntriesReply.getTerm(), flags);
+
+        out.writeObject(appendEntriesReply.getFollowerId());
+
+        WritableObjects.writeLongs(out, appendEntriesReply.getLogLastIndex(), appendEntriesReply.getLogLastTerm());
+
+        out.writeShort(appendEntriesReply.getPayloadVersion());
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        short raftVersion = in.readShort();
+
+        byte hdr = WritableObjects.readLongHeader(in);
+        final int flags = WritableObjects.longHeaderFlags(hdr);
+
+        long term = WritableObjects.readLongBody(in, hdr);
+        String followerId = (String) in.readObject();
+
+        hdr = WritableObjects.readLongHeader(in);
+        long logLastIndex = WritableObjects.readFirstLong(in, hdr);
+        long logLastTerm = WritableObjects.readSecondLong(in, hdr);
+
+        short payloadVersion = in.readShort();
+
+        appendEntriesReply = new AppendEntriesReply(followerId, term, getFlag(flags, SUCCESS), logLastIndex,
+            logLastTerm, payloadVersion, getFlag(flags, FORCE_INSTALL_SNAPSHOT), getFlag(flags, NEEDS_LEADER_ADDRESS),
+            raftVersion, RaftVersions.CURRENT_VERSION);
+    }
+
+    @java.io.Serial
+    private Object readResolve() {
+        return verifyNotNull(appendEntriesReply);
+    }
+
+    private static boolean getFlag(final int flags, final int bit) {
+        return (flags & bit) != 0;
+    }
+}
index 4c63f59ef88da1f4f876f3c8c4ca1b20273a29ac..cf46a1f948ba5ee51be4e03b4d6ec9118607da00 100644 (file)
@@ -55,7 +55,7 @@ public final class AppendEntries extends AbstractRaftRPC {
 
     private final String leaderAddress;
 
-    private AppendEntries(final long term, @NonNull final String leaderId, final long prevLogIndex,
+    AppendEntries(final long term, @NonNull final String leaderId, final long prevLogIndex,
             final long prevLogTerm, @NonNull final List<ReplicatedLogEntry> entries, final long leaderCommit,
             final long replicatedToAllIndex, final short payloadVersion, final short recipientRaftVersion,
             final short leaderRaftVersion, @Nullable final String leaderAddress) {
@@ -140,7 +140,10 @@ public final class AppendEntries extends AbstractRaftRPC {
 
     @Override
     Object writeReplace() {
-        return recipientRaftVersion > RaftVersions.BORON_VERSION ? new ProxyV2(this) : new Proxy(this);
+        if (recipientRaftVersion <= RaftVersions.BORON_VERSION) {
+            return new Proxy(this);
+        }
+        return recipientRaftVersion == RaftVersions.FLUORINE_VERSION ? new ProxyV2(this) : new AE(this);
     }
 
     /**
index d9e6d9795712972af1890e5e243b06d6cf89d97b..80bcb70d9af9426daf7b65f275e13752038f4a85 100644 (file)
@@ -59,7 +59,7 @@ public final class AppendEntriesReply extends AbstractRaftRPC {
                 needsLeaderAddress, RaftVersions.CURRENT_VERSION, recipientRaftVersion);
     }
 
-    private AppendEntriesReply(final String followerId, final long term, final boolean success, final long logLastIndex,
+    AppendEntriesReply(final String followerId, final long term, final boolean success, final long logLastIndex,
             final long logLastTerm, final short payloadVersion, final boolean forceInstallSnapshot,
             final boolean needsLeaderAddress, final short raftVersion, final short recipientRaftVersion) {
         super(term);
@@ -117,7 +117,10 @@ public final class AppendEntriesReply extends AbstractRaftRPC {
 
     @Override
     Object writeReplace() {
-        return recipientRaftVersion > RaftVersions.BORON_VERSION ? new Proxy2(this) : new Proxy(this);
+        if (recipientRaftVersion <= RaftVersions.BORON_VERSION) {
+            return new Proxy(this);
+        }
+        return recipientRaftVersion == RaftVersions.FLUORINE_VERSION ? new Proxy2(this) : new AR(this);
     }
 
     /**
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/IR.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/IR.java
new file mode 100644 (file)
index 0000000..e9d95d8
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.messages;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.yangtools.concepts.WritableObjects;
+
+/**
+ * Serialization proxy for {@link InstallSnapshotReply}.
+ */
+final class IR implements Externalizable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    // Flags
+    private static final int SUCCESS = 0x10;
+
+    private InstallSnapshotReply installSnapshotReply;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public IR() {
+        // For Externalizable
+    }
+
+    IR(final InstallSnapshotReply installSnapshotReply) {
+        this.installSnapshotReply = requireNonNull(installSnapshotReply);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        WritableObjects.writeLong(out, installSnapshotReply.getTerm(), installSnapshotReply.isSuccess() ? SUCCESS : 0);
+        out.writeObject(installSnapshotReply.getFollowerId());
+        out.writeInt(installSnapshotReply.getChunkIndex());
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        final byte hdr = WritableObjects.readLongHeader(in);
+        final int flags = WritableObjects.longHeaderFlags(hdr);
+
+        long term = WritableObjects.readLongBody(in, hdr);
+        String followerId = (String) in.readObject();
+        int chunkIndex = in.readInt();
+
+        installSnapshotReply = new InstallSnapshotReply(term, followerId, chunkIndex, (flags & SUCCESS) != 0);
+    }
+
+    @java.io.Serial
+    private Object readResolve() {
+        return verifyNotNull(installSnapshotReply);
+    }
+}
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/IS.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/IS.java
new file mode 100644 (file)
index 0000000..df9884a
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.messages;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.Optional;
+import java.util.OptionalInt;
+import org.opendaylight.controller.cluster.raft.RaftVersions;
+import org.opendaylight.controller.cluster.raft.persisted.ServerConfigurationPayload;
+import org.opendaylight.yangtools.concepts.WritableObjects;
+
+/**
+ * Serialization proxy for {@link InstallSnapshot}.
+ */
+final class IS implements Externalizable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    // Flags
+    private static final int LAST_CHUNK_HASHCODE = 0x10;
+    private static final int SERVER_CONFIG       = 0x20;
+
+    private InstallSnapshot installSnapshot;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public IS() {
+        // For Externalizable
+    }
+
+    IS(final InstallSnapshot installSnapshot) {
+        this.installSnapshot = requireNonNull(installSnapshot);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        int flags = 0;
+        final var lastChunkHashCode = installSnapshot.getLastChunkHashCode();
+        if (lastChunkHashCode.isPresent()) {
+            flags |= LAST_CHUNK_HASHCODE;
+        }
+        final var serverConfig = installSnapshot.getServerConfig();
+        if (serverConfig.isPresent()) {
+            flags |= SERVER_CONFIG;
+        }
+
+        WritableObjects.writeLong(out, installSnapshot.getTerm(), flags);
+        out.writeObject(installSnapshot.getLeaderId());
+        WritableObjects.writeLongs(out, installSnapshot.getLastIncludedIndex(), installSnapshot.getLastIncludedTerm());
+        out.writeInt(installSnapshot.getChunkIndex());
+        out.writeInt(installSnapshot.getTotalChunks());
+
+        if (lastChunkHashCode.isPresent()) {
+            out.writeInt(lastChunkHashCode.getAsInt());
+        }
+        if (serverConfig.isPresent()) {
+            out.writeObject(serverConfig.get());
+        }
+
+        out.writeObject(installSnapshot.getData());
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        byte hdr = WritableObjects.readLongHeader(in);
+        final int flags = WritableObjects.longHeaderFlags(hdr);
+
+        long term = WritableObjects.readLongBody(in, hdr);
+        String leaderId = (String) in.readObject();
+
+        hdr = WritableObjects.readLongHeader(in);
+        long lastIncludedIndex = WritableObjects.readFirstLong(in, hdr);
+        long lastIncludedTerm = WritableObjects.readSecondLong(in, hdr);
+        int chunkIndex = in.readInt();
+        int totalChunks = in.readInt();
+
+        OptionalInt lastChunkHashCode = getFlag(flags, LAST_CHUNK_HASHCODE) ? OptionalInt.of(in.readInt())
+            : OptionalInt.empty();
+        Optional<ServerConfigurationPayload> serverConfig = getFlag(flags, SERVER_CONFIG)
+                ? Optional.of((ServerConfigurationPayload)in.readObject()) : Optional.empty();
+
+        byte[] data = (byte[])in.readObject();
+
+        installSnapshot = new InstallSnapshot(term, leaderId, lastIncludedIndex, lastIncludedTerm, data,
+                chunkIndex, totalChunks, lastChunkHashCode, serverConfig, RaftVersions.CURRENT_VERSION);
+    }
+
+    @java.io.Serial
+    private Object readResolve() {
+        return verifyNotNull(installSnapshot);
+    }
+
+    private static boolean getFlag(final int flags, final int bit) {
+        return (flags & bit) != 0;
+    }
+}
+
index 60c54f7fd01f03996f166ef562ff251bd34c4d56..e3e587f79121f680cc4d26d41b4ecbaa46d697b1 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.controller.cluster.raft.messages;
 
+import com.google.common.annotations.VisibleForTesting;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.io.Externalizable;
 import java.io.IOException;
@@ -14,6 +15,7 @@ import java.io.ObjectInput;
 import java.io.ObjectOutput;
 import java.util.Optional;
 import java.util.OptionalInt;
+import org.opendaylight.controller.cluster.raft.RaftVersions;
 import org.opendaylight.controller.cluster.raft.persisted.ServerConfigurationPayload;
 
 /**
@@ -32,13 +34,15 @@ public final class InstallSnapshot extends AbstractRaftRPC {
     private final OptionalInt lastChunkHashCode;
     @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "Handled via writeReplace()")
     private final Optional<ServerConfigurationPayload> serverConfig;
+    private final short recipientRaftVersion;
 
     @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "Stores a reference to an externally mutable byte[] "
             + "object but this is OK since this class is merely a DTO and does not process byte[] internally. "
             + "Also it would be inefficient to create a copy as the byte[] could be large.")
     public InstallSnapshot(final long term, final String leaderId, final long lastIncludedIndex,
             final long lastIncludedTerm, final byte[] data, final int chunkIndex, final int totalChunks,
-            final OptionalInt lastChunkHashCode, final Optional<ServerConfigurationPayload> serverConfig) {
+            final OptionalInt lastChunkHashCode, final Optional<ServerConfigurationPayload> serverConfig,
+            final short recipientRaftVersion) {
         super(term);
         this.leaderId = leaderId;
         this.lastIncludedIndex = lastIncludedIndex;
@@ -48,13 +52,15 @@ public final class InstallSnapshot extends AbstractRaftRPC {
         this.totalChunks = totalChunks;
         this.lastChunkHashCode = lastChunkHashCode;
         this.serverConfig = serverConfig;
+        this.recipientRaftVersion = recipientRaftVersion;
     }
 
+    @VisibleForTesting
     public InstallSnapshot(final long term, final String leaderId, final long lastIncludedIndex,
                            final long lastIncludedTerm, final byte[] data, final int chunkIndex,
                            final int totalChunks) {
         this(term, leaderId, lastIncludedIndex, lastIncludedTerm, data, chunkIndex, totalChunks, OptionalInt.empty(),
-            Optional.empty());
+            Optional.empty(), RaftVersions.CURRENT_VERSION);
     }
 
     public String getLeaderId() {
@@ -92,10 +98,6 @@ public final class InstallSnapshot extends AbstractRaftRPC {
         return serverConfig;
     }
 
-    public <T> Object toSerializable(final short version) {
-        return this;
-    }
-
     @Override
     public String toString() {
         return "InstallSnapshot [term=" + getTerm() + ", leaderId=" + leaderId + ", lastIncludedIndex="
@@ -106,10 +108,11 @@ public final class InstallSnapshot extends AbstractRaftRPC {
 
     @Override
     Object writeReplace() {
-        return new Proxy(this);
+        return recipientRaftVersion <= RaftVersions.FLUORINE_VERSION ? new Proxy(this) : new IS(this);
     }
 
     private static class Proxy implements Externalizable {
+        @java.io.Serial
         private static final long serialVersionUID = 1L;
 
         private InstallSnapshot installSnapshot;
@@ -162,9 +165,10 @@ public final class InstallSnapshot extends AbstractRaftRPC {
             byte[] data = (byte[])in.readObject();
 
             installSnapshot = new InstallSnapshot(term, leaderId, lastIncludedIndex, lastIncludedTerm, data,
-                    chunkIndex, totalChunks, lastChunkHashCode, serverConfig);
+                    chunkIndex, totalChunks, lastChunkHashCode, serverConfig, RaftVersions.CURRENT_VERSION);
         }
 
+        @java.io.Serial
         private Object readResolve() {
             return installSnapshot;
         }
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RV.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RV.java
new file mode 100644 (file)
index 0000000..b75f1b7
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.messages;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.yangtools.concepts.WritableObjects;
+
+/**
+ * Serialization proxy for {@link RequestVote}.
+ */
+final class RV implements Externalizable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    private RequestVote requestVote;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public RV() {
+        // For Externalizable
+    }
+
+    RV(final RequestVote requestVote) {
+        this.requestVote = requireNonNull(requestVote);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        WritableObjects.writeLong(out, requestVote.getTerm());
+        out.writeObject(requestVote.getCandidateId());
+        WritableObjects.writeLongs(out, requestVote.getLastLogIndex(), requestVote.getLastLogTerm());
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        long term = WritableObjects.readLong(in);
+        String candidateId = (String) in.readObject();
+
+        final byte hdr = WritableObjects.readLongHeader(in);
+        long lastLogIndex = WritableObjects.readFirstLong(in, hdr);
+        long lastLogTerm = WritableObjects.readSecondLong(in, hdr);
+
+        requestVote = new RequestVote(term, candidateId, lastLogIndex, lastLogTerm);
+    }
+
+    @java.io.Serial
+    private Object readResolve() {
+        return requestVote;
+    }
+}
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/VR.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/VR.java
new file mode 100644 (file)
index 0000000..d5a489b
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.messages;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.yangtools.concepts.WritableObjects;
+
+/**
+ * Serialization proxy for {@link RequestVoteReply}.
+ */
+final class VR implements Externalizable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    // Flags
+    private static final int VOTE_GRANTED = 0x10;
+
+    private RequestVoteReply requestVoteReply;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public VR() {
+        // For Externalizable
+    }
+
+    VR(final RequestVoteReply requestVoteReply) {
+        this.requestVoteReply = requireNonNull(requestVoteReply);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        WritableObjects.writeLong(out, requestVoteReply.getTerm(), requestVoteReply.isVoteGranted() ? VOTE_GRANTED : 0);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        final byte hdr = WritableObjects.readLongHeader(in);
+        requestVoteReply = new RequestVoteReply(WritableObjects.readLongBody(in, hdr),
+            (WritableObjects.longHeaderFlags(hdr) & VOTE_GRANTED) != 0);
+    }
+
+    @java.io.Serial
+    private Object readResolve() {
+        return verifyNotNull(requestVoteReply);
+    }
+}
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/AJE.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/AJE.java
new file mode 100644 (file)
index 0000000..4e39e98
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.yangtools.concepts.WritableObjects;
+
+/**
+ * Serialization proxy for {@link ApplyJournalEntries}.
+ */
+final class AJE implements Externalizable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    private ApplyJournalEntries applyEntries;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public AJE() {
+        // For Externalizable
+    }
+
+    AJE(final ApplyJournalEntries applyEntries) {
+        this.applyEntries = requireNonNull(applyEntries);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        WritableObjects.writeLong(out, applyEntries.getToIndex());
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        applyEntries = new ApplyJournalEntries(WritableObjects.readLong(in));
+    }
+
+    @java.io.Serial
+    private Object readResolve() {
+        return verifyNotNull(applyEntries);
+    }
+}
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/DE.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/DE.java
new file mode 100644 (file)
index 0000000..6bd34c2
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.yangtools.concepts.WritableObjects;
+
+/**
+ * Serialization proxy for {@link DeleteEntries}.
+ */
+final class DE implements Externalizable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    private DeleteEntries deleteEntries;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public DE() {
+        // For Externalizable
+    }
+
+    DE(final DeleteEntries deleteEntries) {
+        this.deleteEntries = requireNonNull(deleteEntries);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        WritableObjects.writeLong(out, deleteEntries.getFromIndex());
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException {
+        deleteEntries = new DeleteEntries(WritableObjects.readLong(in));
+    }
+
+    @java.io.Serial
+    private Object readResolve() {
+        return verifyNotNull(deleteEntries);
+    }
+}
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/LE.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/LE.java
new file mode 100644 (file)
index 0000000..950cdc5
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.persisted;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.controller.cluster.raft.messages.Payload;
+import org.opendaylight.yangtools.concepts.WritableObjects;
+
+/**
+ * Serialization proxy for {@link SimpleReplicatedLogEntry}.
+ */
+final class LE implements Externalizable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    private long index;
+    private long term;
+    private Payload data;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public LE() {
+        // For Externalizable
+    }
+
+    LE(final SimpleReplicatedLogEntry logEntry) {
+        index = logEntry.getIndex();
+        term = logEntry.getTerm();
+        data = logEntry.getData();
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        WritableObjects.writeLongs(out, index, term);
+        out.writeObject(data);
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        final byte hdr = WritableObjects.readLongHeader(in);
+        index = WritableObjects.readFirstLong(in, hdr);
+        term = WritableObjects.readSecondLong(in, hdr);
+        data = (Payload) in.readObject();
+    }
+
+    @java.io.Serial
+    private Object readResolve() {
+        return new SimpleReplicatedLogEntry(index, term, data);
+    }
+}
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/NP.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/NP.java
new file mode 100644 (file)
index 0000000..a041f2f
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.persisted;
+
+import java.io.Serializable;
+
+/**
+ * Serialization proxy for {@link NoopPayload}.
+ */
+// There is no need for Externalizable
+final class NP implements Serializable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    @java.io.Serial
+    private Object readResolve() {
+        return NoopPayload.INSTANCE;
+    }
+}
+
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/SS.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/SS.java
new file mode 100644 (file)
index 0000000..4f1c6f3
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.ArrayList;
+import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
+import org.opendaylight.controller.cluster.raft.messages.Payload;
+import org.opendaylight.controller.cluster.raft.persisted.Snapshot.State;
+import org.opendaylight.yangtools.concepts.WritableObjects;
+
+/**
+ * Externalizable proxy for {@link Snapshot}.
+ */
+final class SS implements Externalizable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    private Snapshot snapshot;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public SS() {
+        // For Externalizable
+    }
+
+    SS(final Snapshot snapshot) {
+        this.snapshot = requireNonNull(snapshot);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        WritableObjects.writeLongs(out, snapshot.getLastIndex(), snapshot.getLastTerm());
+        WritableObjects.writeLongs(out, snapshot.getLastAppliedIndex(), snapshot.getLastAppliedTerm());
+        WritableObjects.writeLong(out, snapshot.getElectionTerm());
+        out.writeObject(snapshot.getElectionVotedFor());
+        out.writeObject(snapshot.getServerConfiguration());
+
+        final var unAppliedEntries = snapshot.getUnAppliedEntries();
+        out.writeInt(unAppliedEntries.size());
+        for (var e : unAppliedEntries) {
+            WritableObjects.writeLongs(out, e.getIndex(), e.getTerm());
+            out.writeObject(e.getData());
+        }
+
+        out.writeObject(snapshot.getState());
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        byte hdr = WritableObjects.readLongHeader(in);
+        long lastIndex = WritableObjects.readFirstLong(in, hdr);
+        long lastTerm = WritableObjects.readSecondLong(in, hdr);
+
+        hdr = WritableObjects.readLongHeader(in);
+        long lastAppliedIndex = WritableObjects.readFirstLong(in, hdr);
+        long lastAppliedTerm = WritableObjects.readSecondLong(in, hdr);
+        long electionTerm = WritableObjects.readLong(in);
+        String electionVotedFor = (String) in.readObject();
+        ServerConfigurationPayload serverConfig = (ServerConfigurationPayload) in.readObject();
+
+        int size = in.readInt();
+        var unAppliedEntries = new ArrayList<ReplicatedLogEntry>(size);
+        for (int i = 0; i < size; i++) {
+            hdr = WritableObjects.readLongHeader(in);
+            unAppliedEntries.add(new SimpleReplicatedLogEntry(
+                WritableObjects.readFirstLong(in, hdr), WritableObjects.readSecondLong(in, hdr),
+                (Payload) in.readObject()));
+        }
+
+        State state = (State) in.readObject();
+
+        snapshot = Snapshot.create(state, unAppliedEntries, lastIndex, lastTerm, lastAppliedIndex, lastAppliedTerm,
+                electionTerm, electionVotedFor, serverConfig);
+    }
+
+    @java.io.Serial
+    private Object readResolve() {
+        return verifyNotNull(snapshot);
+    }
+}
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/UT.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/UT.java
new file mode 100644 (file)
index 0000000..0fc6f6d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.persisted;
+
+import static com.google.common.base.Verify.verifyNotNull;
+import static java.util.Objects.requireNonNull;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import org.opendaylight.yangtools.concepts.WritableObjects;
+
+/**
+ * Serialization proxy for {@link UpdateElectionTerm}.
+ */
+final class UT implements Externalizable {
+    @java.io.Serial
+    private static final long serialVersionUID = 1L;
+
+    private UpdateElectionTerm updateElectionTerm;
+
+    @SuppressWarnings("checkstyle:RedundantModifier")
+    public UT() {
+        // For Externalizable
+    }
+
+    UT(final UpdateElectionTerm updateElectionTerm) {
+        this.updateElectionTerm = requireNonNull(updateElectionTerm);
+    }
+
+    @Override
+    public void writeExternal(final ObjectOutput out) throws IOException {
+        WritableObjects.writeLong(out, updateElectionTerm.getCurrentTerm());
+        out.writeObject(updateElectionTerm.getVotedFor());
+    }
+
+    @Override
+    public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
+        updateElectionTerm = new UpdateElectionTerm(WritableObjects.readLong(in), (String) in.readObject());
+    }
+
+    @java.io.Serial
+    private Object readResolve() {
+        return verifyNotNull(updateElectionTerm);
+    }
+}
index b371ade4853d69ce8ad2b237fda815fa36098259..a3da9594169af01290664a255e18d8d9e5fbd385 100644 (file)
@@ -10,8 +10,7 @@ package org.opendaylight.controller.cluster.raft.messages;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
-import java.io.Serializable;
-import java.util.Arrays;
+import java.util.List;
 import java.util.Optional;
 import java.util.OptionalInt;
 import org.apache.commons.lang.SerializationUtils;
@@ -26,7 +25,6 @@ import org.opendaylight.controller.cluster.raft.persisted.ServerInfo;
  * @author Thomas Pantelis
  */
 public class InstallSnapshotTest {
-
     @Test
     public void testSerialization() {
         byte[] data = new byte[1000];
@@ -37,25 +35,19 @@ public class InstallSnapshotTest {
             }
         }
 
-        ServerConfigurationPayload serverConfig = new ServerConfigurationPayload(Arrays.asList(
+        ServerConfigurationPayload serverConfig = new ServerConfigurationPayload(List.of(
                 new ServerInfo("leader", true), new ServerInfo("follower", false)));
-        InstallSnapshot expected = new InstallSnapshot(3L, "leaderId", 11L, 2L, data, 5, 6, OptionalInt.of(54321),
-            Optional.of(serverConfig));
-
-        Object serialized = expected.toSerializable(RaftVersions.CURRENT_VERSION);
-        assertEquals("Serialized type", InstallSnapshot.class, serialized.getClass());
+        assertInstallSnapshot(1302, new InstallSnapshot(3L, "leaderId", 11L, 2L, data, 5, 6, OptionalInt.of(54321),
+            Optional.of(serverConfig), RaftVersions.CURRENT_VERSION));
 
-        var bytes = SerializationUtils.serialize((Serializable) serialized);
-        assertEquals(1302, bytes.length);
-        var actual = (InstallSnapshot) SerializationUtils.deserialize(bytes);
-
-        verifyInstallSnapshot(expected, actual);
+        assertInstallSnapshot(1165, new InstallSnapshot(3L, "leaderId", 11L, 2L, data, 5, 6, OptionalInt.empty(),
+            Optional.empty(), RaftVersions.CURRENT_VERSION));
+    }
 
-        expected = new InstallSnapshot(3L, "leaderId", 11L, 2L, data, 5, 6);
-        bytes = SerializationUtils.serialize((Serializable) expected.toSerializable(RaftVersions.CURRENT_VERSION));
-        assertEquals(1165, bytes.length);
-        actual = (InstallSnapshot) SerializationUtils.deserialize(bytes);
-        verifyInstallSnapshot(expected, actual);
+    private static void assertInstallSnapshot(final int expectedSize, final InstallSnapshot expected) {
+        final var bytes = SerializationUtils.serialize(expected);
+        assertEquals(expectedSize, bytes.length);
+        verifyInstallSnapshot(expected, (InstallSnapshot) SerializationUtils.deserialize(bytes));
     }
 
     private static void verifyInstallSnapshot(final InstallSnapshot expected, final InstallSnapshot actual) {
diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/persisted/NoopPayloadTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/persisted/NoopPayloadTest.java
new file mode 100644 (file)
index 0000000..cfeafbb
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.persisted;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import org.apache.commons.lang3.SerializationUtils;
+import org.junit.Test;
+
+public class NoopPayloadTest {
+    @Test
+    public void testSerialization() {
+        final var bytes = SerializationUtils.serialize(NoopPayload.INSTANCE);
+        assertEquals(89, bytes.length);
+        assertSame(NoopPayload.INSTANCE, SerializationUtils.deserialize(bytes));
+    }
+}