Trigger snapshots on legacy persisted entries 25/103525/1
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 2 Dec 2022 16:00:02 +0000 (17:00 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 2 Dec 2022 16:02:26 +0000 (17:02 +0100)
We have migrated proxies, hence we should be purging them from journal
if we encounter them during recovery. Introduce a marker interface for
that and subclasses which implement it.

JIRA: CONTROLLER-2064
Change-Id: Ie16b1247306d64580df977bece70a94eb3187cbf
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/ApplyJournalEntries.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/DeleteEntries.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/LegacySerializable.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/NoopPayload.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/SimpleReplicatedLogEntry.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/Snapshot.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/UpdateElectionTerm.java

index ad334f4c156ad0dc43aaf4eb69cf0e3184e4a039..4439e2cfa1721063c7555325386195cfb9497dfc 100644 (file)
@@ -22,13 +22,23 @@ import java.io.Serializable;
  *
  * @author Thomas Pantelis
  */
-public class ApplyJournalEntries implements Serializable, ControlMessage {
+public sealed class ApplyJournalEntries implements Serializable, ControlMessage {
+    @Deprecated(since = "7.0.0", forRemoval = true)
+    private static final class Legacy extends ApplyJournalEntries implements LegacySerializable {
+        @java.io.Serial
+        private static final long serialVersionUID = 1L;
+
+        Legacy(final long toIndex) {
+            super(toIndex);
+        }
+    }
+
     @Deprecated(since = "7.0.0", forRemoval = true)
     private static final class Proxy implements Externalizable {
         @java.io.Serial
         private static final long serialVersionUID = 1L;
 
-        private ApplyJournalEntries applyEntries;
+        private ApplyJournalEntries applyEntries = null;
 
         // checkstyle flags the public modifier as redundant which really doesn't make sense since it clearly isn't
         // redundant. It is explicitly needed for Java serialization to be able to create instances via reflection.
@@ -37,10 +47,6 @@ public class ApplyJournalEntries implements Serializable, ControlMessage {
             // For Externalizable
         }
 
-        Proxy(final ApplyJournalEntries applyEntries) {
-            this.applyEntries = applyEntries;
-        }
-
         @Override
         public void writeExternal(final ObjectOutput out) throws IOException {
             out.writeLong(applyEntries.toIndex);
@@ -48,7 +54,7 @@ public class ApplyJournalEntries implements Serializable, ControlMessage {
 
         @Override
         public void readExternal(final ObjectInput in) throws IOException {
-            applyEntries = new ApplyJournalEntries(in.readLong());
+            applyEntries = new Legacy(in.readLong());
         }
 
         @java.io.Serial
@@ -66,16 +72,17 @@ public class ApplyJournalEntries implements Serializable, ControlMessage {
         this.toIndex = toIndex;
     }
 
-    public long getToIndex() {
+    public final long getToIndex() {
         return toIndex;
     }
 
-    private Object writeReplace() {
+    @java.io.Serial
+    public final Object writeReplace() {
         return new AJE(this);
     }
 
     @Override
-    public String toString() {
+    public final String toString() {
         return "ApplyJournalEntries [toIndex=" + toIndex + "]";
     }
 }
index b4b45465047420198b22ee0c1b0ab74fb9a18a7f..189ca9c5c83351c5dfe47eb95d73293cdd237ea3 100644 (file)
@@ -18,13 +18,23 @@ import java.io.Serializable;
  *
  * @author Thomas Pantelis
  */
-public class DeleteEntries implements Serializable {
+public sealed class DeleteEntries implements Serializable {
+    @Deprecated(since = "7.0.0", forRemoval = true)
+    private static final class Legacy extends DeleteEntries implements LegacySerializable {
+        @java.io.Serial
+        private static final long serialVersionUID = 1L;
+
+        Legacy(final long fromIndex) {
+            super(fromIndex);
+        }
+    }
+
     @Deprecated(since = "7.0.0", forRemoval = true)
     private static final class Proxy implements Externalizable {
         @java.io.Serial
         private static final long serialVersionUID = 1L;
 
-        private DeleteEntries deleteEntries;
+        private DeleteEntries deleteEntries = null;
 
         // checkstyle flags the public modifier as redundant which really doesn't make sense since it clearly isn't
         // redundant. It is explicitly needed for Java serialization to be able to create instances via reflection.
@@ -33,10 +43,6 @@ public class DeleteEntries implements Serializable {
             // For Externalizable
         }
 
-        Proxy(final DeleteEntries deleteEntries) {
-            this.deleteEntries = deleteEntries;
-        }
-
         @Override
         public void writeExternal(final ObjectOutput out) throws IOException {
             out.writeLong(deleteEntries.fromIndex);
@@ -44,7 +50,7 @@ public class DeleteEntries implements Serializable {
 
         @Override
         public void readExternal(final ObjectInput in) throws IOException {
-            deleteEntries = new DeleteEntries(in.readLong());
+            deleteEntries = new Legacy(in.readLong());
         }
 
         @java.io.Serial
@@ -62,16 +68,17 @@ public class DeleteEntries implements Serializable {
         this.fromIndex = fromIndex;
     }
 
-    public long getFromIndex() {
+    public final long getFromIndex() {
         return fromIndex;
     }
 
-    private Object writeReplace() {
+    @java.io.Serial
+    public final Object writeReplace() {
         return new DE(this);
     }
 
     @Override
-    public String toString() {
+    public final String toString() {
         return "DeleteEntries [fromIndex=" + fromIndex + "]";
     }
 }
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/LegacySerializable.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/persisted/LegacySerializable.java
new file mode 100644 (file)
index 0000000..0e75d88
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+/**
+ * Marker interface for serializable objects which have been migrated. It implements {@link MigratedSerializable} and
+ * always returns {@code true} from {@link #isMigrated()}. This interface is marked as deprecated , as any of its users
+ * should also be marked as deprecated.
+ */
+@Deprecated
+public interface LegacySerializable extends MigratedSerializable {
+    @Override
+    @Deprecated(forRemoval = true)
+    default boolean isMigrated() {
+        return true;
+    }
+}
index 1fa865948a3b3fcad6f0efa0d2bf9c30acf17a89..f1a3a3b923cab4bd370511316663ce74e20d402e 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.controller.cluster.raft.persisted;
 import akka.dispatch.ControlMessage;
 import java.io.Serializable;
 import org.apache.commons.lang3.SerializationUtils;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.controller.cluster.raft.messages.Payload;
 
 /**
@@ -18,14 +19,14 @@ import org.opendaylight.controller.cluster.raft.messages.Payload;
  *
  * @author Thomas Pantelis
  */
-public final class NoopPayload extends Payload implements ControlMessage {
-    public static final NoopPayload INSTANCE = new NoopPayload();
-
+// FIXME: do not implement MigratedSerializable once Proxy is gone
+public final class NoopPayload extends Payload implements ControlMessage, MigratedSerializable {
     // There is no need for Externalizable
     @Deprecated(since = "7.0.0", forRemoval = true)
     private static final class Proxy implements Serializable {
         @java.io.Serial
         private static final long serialVersionUID = 1L;
+        private static final @NonNull NoopPayload INSTANCE = new NoopPayload(true);
 
         @java.io.Serial
         private Object readResolve() {
@@ -35,12 +36,16 @@ public final class NoopPayload extends Payload implements ControlMessage {
 
     @java.io.Serial
     private static final long serialVersionUID = 1L;
-    private static final NP PROXY = new NP();
+    private static final @NonNull NP PROXY = new NP();
     // Estimate to how big the proxy is. Note this includes object stream overhead, so it is a bit conservative
     private static final int PROXY_SIZE = SerializationUtils.serialize(PROXY).length;
 
-    private NoopPayload() {
-        // Hidden on purpose
+    public static final @NonNull NoopPayload INSTANCE = new NoopPayload(false);
+
+    private final boolean migrated;
+
+    private NoopPayload(final boolean migrated) {
+        this.migrated = migrated;
     }
 
     @Override
@@ -54,7 +59,13 @@ public final class NoopPayload extends Payload implements ControlMessage {
     }
 
     @Override
-    protected Object writeReplace() {
+    public boolean isMigrated() {
+        return migrated;
+    }
+
+    // FIXME: protected once not MigratedSerializable
+    @Override
+    public Object writeReplace() {
         return PROXY;
     }
 }
index 56c3f769ff18306f746496c48f387409697ecf10..d075a417017a6066e08d62306f68c85e52508e89 100644 (file)
@@ -23,7 +23,17 @@ import org.opendaylight.controller.cluster.raft.messages.Payload;
  *
  * @author Thomas Pantelis
  */
-public final class SimpleReplicatedLogEntry implements ReplicatedLogEntry, Serializable {
+public sealed class SimpleReplicatedLogEntry implements ReplicatedLogEntry, Serializable {
+    @Deprecated(since = "7.0.0", forRemoval = true)
+    private static final class Legacy extends SimpleReplicatedLogEntry implements LegacySerializable {
+        @java.io.Serial
+        private static final long serialVersionUID = 1L;
+
+        Legacy(final long index, final long term, final Payload payload) {
+            super(index, term, payload);
+        }
+    }
+
     @Deprecated(since = "7.0.0", forRemoval = true)
     private static final class Proxy implements Externalizable {
         @java.io.Serial
@@ -62,7 +72,7 @@ public final class SimpleReplicatedLogEntry implements ReplicatedLogEntry, Seria
 
         @java.io.Serial
         private Object readResolve() {
-            return new SimpleReplicatedLogEntry(index, term, data);
+            return new Legacy(index, term, data);
         }
     }
 
@@ -90,46 +100,47 @@ public final class SimpleReplicatedLogEntry implements ReplicatedLogEntry, Seria
     }
 
     @Override
-    public Payload getData() {
+    public final Payload getData() {
         return payload;
     }
 
     @Override
-    public long getTerm() {
+    public final long getTerm() {
         return term;
     }
 
     @Override
-    public long getIndex() {
+    public final long getIndex() {
         return index;
     }
 
     @Override
-    public int size() {
+    public final int size() {
         return payload.size();
     }
 
     @Override
-    public int serializedSize() {
+    public final int serializedSize() {
         return PROXY_SIZE + payload.serializedSize();
     }
 
     @Override
-    public boolean isPersistencePending() {
+    public final boolean isPersistencePending() {
         return persistencePending;
     }
 
     @Override
-    public void setPersistencePending(final boolean pending) {
+    public final void setPersistencePending(final boolean pending) {
         persistencePending = pending;
     }
 
-    private Object writeReplace() {
+    @java.io.Serial
+    public final Object writeReplace() {
         return new LE(this);
     }
 
     @Override
-    public int hashCode() {
+    public final int hashCode() {
         final int prime = 31;
         int result = 1;
         result = prime * result + payload.hashCode();
@@ -139,20 +150,13 @@ public final class SimpleReplicatedLogEntry implements ReplicatedLogEntry, Seria
     }
 
     @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || getClass() != obj.getClass()) {
-            return false;
-        }
-
-        var other = (SimpleReplicatedLogEntry) obj;
-        return index == other.index && term == other.term && payload.equals(other.payload);
+    public final boolean equals(final Object obj) {
+        return this == obj || obj instanceof SimpleReplicatedLogEntry other && index == other.index
+            && term == other.term && payload.equals(other.payload);
     }
 
     @Override
-    public String toString() {
+    public final String toString() {
         return "SimpleReplicatedLogEntry [index=" + index + ", term=" + term + ", payload=" + payload + "]";
     }
 }
index 9812ac69d5c2be4194460c253f0316537cdb4c25..0acede1086ef20f3de26531cb7baf95a5f850507 100644 (file)
@@ -22,9 +22,8 @@ import org.opendaylight.controller.cluster.raft.messages.Payload;
  *
  * @author Thomas Pantelis
  */
-// Not final for mocking
+// Not final and non-sealed for mocking
 public class Snapshot implements Serializable {
-
     /**
      * Implementations of this interface are used as the state payload for a snapshot.
      *
@@ -42,12 +41,25 @@ public class Snapshot implements Serializable {
         }
     }
 
+    @Deprecated(since = "7.0.0", forRemoval = true)
+    private static final class Legacy extends Snapshot implements LegacySerializable {
+        @java.io.Serial
+        private static final long serialVersionUID = 1L;
+
+        Legacy(final State state, final List<ReplicatedLogEntry> unAppliedEntries, final long lastIndex,
+                final long lastTerm, final long lastAppliedIndex, final long lastAppliedTerm, final long electionTerm,
+                final String electionVotedFor, final ServerConfigurationPayload serverConfig) {
+            super(state, unAppliedEntries, lastIndex, lastTerm, lastAppliedIndex, lastAppliedTerm, electionTerm,
+                electionVotedFor, serverConfig);
+        }
+    }
+
     @Deprecated(since = "7.0.0", forRemoval = true)
     private static final class Proxy implements Externalizable {
         @java.io.Serial
         private static final long serialVersionUID = 1L;
 
-        private Snapshot snapshot;
+        private Snapshot snapshot = null;
 
         // checkstyle flags the public modifier as redundant which really doesn't make sense since it clearly isn't
         // redundant. It is explicitly needed for Java serialization to be able to create instances via reflection.
@@ -56,10 +68,6 @@ public class Snapshot implements Serializable {
             // For Externalizable
         }
 
-        Proxy(final Snapshot snapshot) {
-            this.snapshot = snapshot;
-        }
-
         @Override
         public void writeExternal(final ObjectOutput out) throws IOException {
             out.writeLong(snapshot.lastIndex);
@@ -91,7 +99,7 @@ public class Snapshot implements Serializable {
             ServerConfigurationPayload serverConfig = (ServerConfigurationPayload) in.readObject();
 
             int size = in.readInt();
-            List<ReplicatedLogEntry> unAppliedEntries = new ArrayList<>(size);
+            var unAppliedEntries = new ArrayList<ReplicatedLogEntry>(size);
             for (int i = 0; i < size; i++) {
                 unAppliedEntries.add(new SimpleReplicatedLogEntry(in.readLong(), in.readLong(),
                         (Payload) in.readObject()));
@@ -99,7 +107,7 @@ public class Snapshot implements Serializable {
 
             State state = (State) in.readObject();
 
-            snapshot = Snapshot.create(state, unAppliedEntries, lastIndex, lastTerm, lastAppliedIndex, lastAppliedTerm,
+            snapshot = new Legacy(state, unAppliedEntries, lastIndex, lastTerm, lastAppliedIndex, lastAppliedTerm,
                     electionTerm, electionVotedFor, serverConfig);
         }
 
@@ -180,12 +188,12 @@ public class Snapshot implements Serializable {
     }
 
     @java.io.Serial
-    private Object writeReplace() {
+    public final Object writeReplace() {
         return new SS(this);
     }
 
     @Override
-    public String toString() {
+    public final String toString() {
         return "Snapshot [lastIndex=" + lastIndex + ", lastTerm=" + lastTerm + ", lastAppliedIndex=" + lastAppliedIndex
                 + ", lastAppliedTerm=" + lastAppliedTerm + ", unAppliedEntries size=" + unAppliedEntries.size()
                 + ", state=" + state + ", electionTerm=" + electionTerm + ", electionVotedFor="
index 419aec0e321a678a04469f26cb38442bca2fe9c9..99b3b61bad920221959079f2cb101093d071e3d9 100644 (file)
@@ -16,13 +16,23 @@ import java.io.Serializable;
 /**
  * Message class to persist election term information.
  */
-public class UpdateElectionTerm implements Serializable {
+public sealed class UpdateElectionTerm implements Serializable {
+    @Deprecated(since = "7.0.0", forRemoval = true)
+    private static final class Legacy extends UpdateElectionTerm implements LegacySerializable {
+        @java.io.Serial
+        private static final long serialVersionUID = 1L;
+
+        Legacy(final long currentTerm, final String votedFor) {
+            super(currentTerm, votedFor);
+        }
+    }
+
     @Deprecated(since = "7.0.0", forRemoval = true)
     private static final class Proxy implements Externalizable {
         @java.io.Serial
         private static final long serialVersionUID = 1L;
 
-        private UpdateElectionTerm updateElectionTerm;
+        private UpdateElectionTerm updateElectionTerm = null;
 
         // checkstyle flags the public modifier as redundant which really doesn't make sense since it clearly isn't
         // redundant. It is explicitly needed for Java serialization to be able to create instances via reflection.
@@ -31,10 +41,6 @@ public class UpdateElectionTerm implements Serializable {
             // For Externalizable
         }
 
-        Proxy(final UpdateElectionTerm updateElectionTerm) {
-            this.updateElectionTerm = updateElectionTerm;
-        }
-
         @Override
         public void writeExternal(final ObjectOutput out) throws IOException {
             out.writeLong(updateElectionTerm.currentTerm);
@@ -43,7 +49,7 @@ public class UpdateElectionTerm implements Serializable {
 
         @Override
         public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
-            updateElectionTerm = new UpdateElectionTerm(in.readLong(), (String) in.readObject());
+            updateElectionTerm = new Legacy(in.readLong(), (String) in.readObject());
         }
 
         @java.io.Serial
@@ -63,21 +69,21 @@ public class UpdateElectionTerm implements Serializable {
         this.votedFor = votedFor;
     }
 
-    public long getCurrentTerm() {
+    public final long getCurrentTerm() {
         return currentTerm;
     }
 
-    public String getVotedFor() {
+    public final String getVotedFor() {
         return votedFor;
     }
 
     @java.io.Serial
-    private Object writeReplace() {
+    public final Object writeReplace() {
         return new UT(this);
     }
 
     @Override
-    public String toString() {
+    public final String toString() {
         return "UpdateElectionTerm [currentTerm=" + currentTerm + ", votedFor=" + votedFor + "]";
     }
 }