From 9a091425d16dadf09a5be4e764785707f5253013 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Fri, 2 Dec 2022 09:00:40 +0100 Subject: [PATCH] Define RaftVersions.ARGON_VERSION 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 --- .../controller/cluster/raft/RaftVersions.java | 1 + .../raft/base/messages/TimeoutNow.java | 9 ++ .../raft/behaviors/AbstractLeader.java | 4 +- .../controller/cluster/raft/behaviors/FI.java | 49 ++++++++ .../raft/client/messages/Shutdown.java | 9 ++ .../controller/cluster/raft/messages/AE.java | 96 ++++++++++++++++ .../controller/cluster/raft/messages/AR.java | 95 ++++++++++++++++ .../cluster/raft/messages/AppendEntries.java | 7 +- .../raft/messages/AppendEntriesReply.java | 7 +- .../controller/cluster/raft/messages/IR.java | 63 +++++++++++ .../controller/cluster/raft/messages/IS.java | 107 ++++++++++++++++++ .../raft/messages/InstallSnapshot.java | 20 ++-- .../controller/cluster/raft/messages/RV.java | 59 ++++++++++ .../controller/cluster/raft/messages/VR.java | 56 +++++++++ .../cluster/raft/persisted/AJE.java | 51 +++++++++ .../controller/cluster/raft/persisted/DE.java | 51 +++++++++ .../controller/cluster/raft/persisted/LE.java | 57 ++++++++++ .../controller/cluster/raft/persisted/NP.java | 25 ++++ .../controller/cluster/raft/persisted/SS.java | 91 +++++++++++++++ .../controller/cluster/raft/persisted/UT.java | 52 +++++++++ .../raft/messages/InstallSnapshotTest.java | 30 ++--- .../raft/persisted/NoopPayloadTest.java | 23 ++++ 22 files changed, 929 insertions(+), 33 deletions(-) create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/FI.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AE.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AR.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/IR.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/IS.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RV.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/VR.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/AJE.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/DE.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/LE.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/NP.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/SS.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/UT.java create mode 100644 opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/persisted/NoopPayloadTest.java diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftVersions.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftVersions.java index a81dd48e28..646a57a3fe 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftVersions.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftVersions.java @@ -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() { diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/TimeoutNow.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/TimeoutNow.java index b212250dd4..780487b6ab 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/TimeoutNow.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/TimeoutNow.java @@ -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 diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractLeader.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractLeader.java index 0188a6df1a..6bccde0d62 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractLeader.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractLeader.java @@ -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 index 0000000000..79c605a528 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/FI.java @@ -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); + } +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/client/messages/Shutdown.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/client/messages/Shutdown.java index b670243b42..29f291f5cf 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/client/messages/Shutdown.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/client/messages/Shutdown.java @@ -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 index 0000000000..402cb72df6 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AE.java @@ -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(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 index 0000000000..6aa2ed8636 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AR.java @@ -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; + } +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java index 4c63f59ef8..cf46a1f948 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntries.java @@ -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 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); } /** diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntriesReply.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntriesReply.java index d9e6d97957..80bcb70d9a 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntriesReply.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/AppendEntriesReply.java @@ -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 index 0000000000..e9d95d84e7 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/IR.java @@ -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 index 0000000000..df9884aaaa --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/IS.java @@ -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 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; + } +} + diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java index 60c54f7fd0..e3e587f791 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshot.java @@ -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 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 serverConfig) { + final OptionalInt lastChunkHashCode, final Optional 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 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 index 0000000000..b75f1b7dab --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/RV.java @@ -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 index 0000000000..d5a489bd97 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/messages/VR.java @@ -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 index 0000000000..4e39e9884c --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/AJE.java @@ -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 index 0000000000..6bd34c25ca --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/DE.java @@ -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 index 0000000000..950cdc589b --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/LE.java @@ -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 index 0000000000..a041f2f571 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/NP.java @@ -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 index 0000000000..4f1c6f3076 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/SS.java @@ -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(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 index 0000000000..0fc6f6d618 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/UT.java @@ -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); + } +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotTest.java index b371ade485..a3da959416 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/messages/InstallSnapshotTest.java @@ -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 index 0000000000..cfeafbb173 --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/persisted/NoopPayloadTest.java @@ -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)); + } +} -- 2.36.6