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>
@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() {
* @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
chunkIndex,
installSnapshotState.getTotalChunks(),
OptionalInt.of(installSnapshotState.getLastChunkHashCode()),
- serverConfig
- ).toSerializable(followerLogInfo.getRaftVersion()),
+ serverConfig,
+ followerLogInfo.getRaftVersion()),
actor()
);
}
--- /dev/null
+/*
+ * 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);
+ }
+}
* @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
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
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) {
@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);
}
/**
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);
@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);
}
/**
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
+
*/
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;
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;
/**
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;
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() {
return serverConfig;
}
- public <T> Object toSerializable(final short version) {
- return this;
- }
-
@Override
public String toString() {
return "InstallSnapshot [term=" + getTerm() + ", leaderId=" + leaderId + ", lastIncludedIndex="
@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;
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;
}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
+
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
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;
* @author Thomas Pantelis
*/
public class InstallSnapshotTest {
-
@Test
public void testSerialization() {
byte[] data = new byte[1000];
}
}
- 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) {
--- /dev/null
+/*
+ * 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));
+ }
+}