Always persist ServerConfigurationPayload log entries 52/28952/4
authorTom Pantelis <tpanteli@brocade.com>
Thu, 29 Oct 2015 14:27:28 +0000 (10:27 -0400)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 2 Nov 2015 13:01:14 +0000 (13:01 +0000)
We need to always persist ServerConfigurationPayload log entries
regardless of whether or not persistence is enabled for the derived
RaftActor's data.

I added a new tagging interface PersistentPayload, implemented by
ServerConfigurationPayload, to indicate a Payload
needs to always be persisted. Since log entries are persisted by
both the RaftActor and Follower behavior via the ReplicatedLog, the
logic to determine persistence based on PersistentPayload needs to be
available to both. The ReplicatedLog uses the persistence provider
contained in the RaftActorContext which is the
DelegatingPersistentDataProvider set by the RaftActor. So to keep
the rest of the code the same and keep it simple, I derived a
RaftActorDelegatingPersistentDataProvider which overrides persist to
handle the PersistentPayload logic utilizing the RaftActor's
existing PersistentDataProvider.

Change-Id: I243026b28ed57461ad92324b6947091ae74a7127
Signed-off-by: Tom Pantelis <tpanteli@brocade.com>
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActorDelegatingPersistentDataProvider.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/ServerConfigurationPayload.java
opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorDelegatingPersistentDataProviderTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorServerConfigurationSupportTest.java
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/PersistentPayload.java [new file with mode: 0644]

index a043c69cf760134f8a802b4f15af9878fdf9a223..4eb9ad8359eeb250b65235a590c6b8bff02a2143 100644 (file)
@@ -108,7 +108,7 @@ public abstract class RaftActor extends AbstractUntypedPersistentActor {
      */
     private final RaftActorContextImpl context;
 
-    private final DelegatingPersistentDataProvider delegatingPersistenceProvider = new DelegatingPersistentDataProvider(null);
+    private final DelegatingPersistentDataProvider delegatingPersistenceProvider;
 
     private final PersistentDataProvider persistentProvider;
 
@@ -126,6 +126,8 @@ public abstract class RaftActor extends AbstractUntypedPersistentActor {
          Optional<ConfigParams> configParams, short payloadVersion) {
 
         persistentProvider = new PersistentDataProvider(this);
+        delegatingPersistenceProvider = new RaftActorDelegatingPersistentDataProvider(null, persistentProvider);
+
         context = new RaftActorContextImpl(this.getSelf(),
             this.getContext(), id, new ElectionTermImpl(persistentProvider, id, LOG),
             -1, -1, peerAddresses,
diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActorDelegatingPersistentDataProvider.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActorDelegatingPersistentDataProvider.java
new file mode 100644 (file)
index 0000000..466609d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, Inc. 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;
+
+import akka.japi.Procedure;
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.cluster.DataPersistenceProvider;
+import org.opendaylight.controller.cluster.DelegatingPersistentDataProvider;
+import org.opendaylight.controller.cluster.PersistentDataProvider;
+import org.opendaylight.controller.cluster.raft.protobuff.client.messages.PersistentPayload;
+
+/**
+ * The DelegatingPersistentDataProvider used by RaftActor to override the configured persistent provider to
+ * persist ReplicatedLogEntry's based on whether or not the payload is a PersistentPayload instance.
+ *
+ * @author Thomas Pantelis
+ */
+class RaftActorDelegatingPersistentDataProvider extends DelegatingPersistentDataProvider {
+    private final PersistentDataProvider persistentProvider;
+
+    RaftActorDelegatingPersistentDataProvider(DataPersistenceProvider delegate,
+            PersistentDataProvider persistentProvider) {
+        super(delegate);
+        this.persistentProvider = Preconditions.checkNotNull(persistentProvider);
+    }
+
+    @Override
+    public <T> void persist(T o, Procedure<T> procedure) {
+        if(getDelegate().isRecoveryApplicable()) {
+            super.persist(o, procedure);
+        } else {
+            boolean isPersistentPayload = false;
+            if(o instanceof ReplicatedLogEntry) {
+                isPersistentPayload = ((ReplicatedLogEntry)o).getData() instanceof PersistentPayload;
+            }
+
+            if(isPersistentPayload) {
+                persistentProvider.persist(o, procedure);
+            } else {
+                super.persist(o, procedure);
+            }
+        }
+    }
+}
index 112c7d56e00faa62a84051cfda87fb2ab39102a4..b439cef035a321056d6fa07051d9cc144a0dcdce 100644 (file)
@@ -17,6 +17,7 @@ import java.util.List;
 import java.util.Map;
 import javax.annotation.Nonnull;
 import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
+import org.opendaylight.controller.cluster.raft.protobuff.client.messages.PersistentPayload;
 import org.opendaylight.controller.protobuff.messages.cluster.raft.AppendEntriesMessages;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -26,7 +27,7 @@ import org.slf4j.LoggerFactory;
  *
  * @author Thomas Pantelis
  */
-public class ServerConfigurationPayload extends Payload implements Serializable {
+public class ServerConfigurationPayload extends Payload implements PersistentPayload, Serializable {
     private static final long serialVersionUID = 1L;
 
     private static final Logger LOG = LoggerFactory.getLogger(ServerConfigurationPayload.class);
diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorDelegatingPersistentDataProviderTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/RaftActorDelegatingPersistentDataProviderTest.java
new file mode 100644 (file)
index 0000000..378c53c
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, Inc. 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;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import akka.japi.Procedure;
+import com.google.protobuf.GeneratedMessage.GeneratedExtension;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.cluster.DataPersistenceProvider;
+import org.opendaylight.controller.cluster.PersistentDataProvider;
+import org.opendaylight.controller.cluster.raft.protobuff.client.messages.Payload;
+import org.opendaylight.controller.cluster.raft.protobuff.client.messages.PersistentPayload;
+
+/**
+ * Unit tests for RaftActorDelegatingPersistentDataProvider.
+ *
+ * @author Thomas Pantelis
+ */
+public class RaftActorDelegatingPersistentDataProviderTest {
+    private static final Payload PERSISTENT_PAYLOAD = new TestPersistentPayload();
+
+    private static final Payload NON_PERSISTENT_PAYLOAD = new TestNonPersistentPayload();
+
+    private static final Object OTHER_DATA_OBJECT = new Object();
+
+    @Mock
+    private ReplicatedLogEntry mockPersistentLogEntry;
+
+    @Mock
+    private ReplicatedLogEntry mockNonPersistentLogEntry;
+
+    @Mock
+    private DataPersistenceProvider mockDelegateProvider;
+
+    @Mock
+    private PersistentDataProvider mockPersistentProvider;
+
+    @SuppressWarnings("rawtypes")
+    @Mock
+    private Procedure mockProcedure;
+
+    private RaftActorDelegatingPersistentDataProvider provider;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(PERSISTENT_PAYLOAD).when(mockPersistentLogEntry).getData();
+        doReturn(NON_PERSISTENT_PAYLOAD).when(mockNonPersistentLogEntry).getData();
+        provider = new RaftActorDelegatingPersistentDataProvider(mockDelegateProvider, mockPersistentProvider);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testPersistWithPersistenceEnabled() {
+        doReturn(true).when(mockDelegateProvider).isRecoveryApplicable();
+
+        provider.persist(mockPersistentLogEntry, mockProcedure);
+        verify(mockDelegateProvider).persist(mockPersistentLogEntry, mockProcedure);
+
+        provider.persist(mockNonPersistentLogEntry, mockProcedure);
+        verify(mockDelegateProvider).persist(mockNonPersistentLogEntry, mockProcedure);
+
+        provider.persist(OTHER_DATA_OBJECT, mockProcedure);
+        verify(mockDelegateProvider).persist(OTHER_DATA_OBJECT, mockProcedure);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testPersistWithPersistenceDisabled() {
+        doReturn(false).when(mockDelegateProvider).isRecoveryApplicable();
+
+        provider.persist(mockPersistentLogEntry, mockProcedure);
+        verify(mockPersistentProvider).persist(mockPersistentLogEntry, mockProcedure);
+
+        provider.persist(mockNonPersistentLogEntry, mockProcedure);
+        verify(mockDelegateProvider).persist(mockNonPersistentLogEntry, mockProcedure);
+
+        provider.persist(OTHER_DATA_OBJECT, mockProcedure);
+        verify(mockDelegateProvider).persist(OTHER_DATA_OBJECT, mockProcedure);
+    }
+
+    static class TestNonPersistentPayload extends Payload {
+        @SuppressWarnings("rawtypes")
+        @Override
+        public <T> Map<GeneratedExtension, T> encode() {
+            return null;
+        }
+
+        @Override
+        public Payload decode(
+                org.opendaylight.controller.protobuff.messages.cluster.raft.AppendEntriesMessages.AppendEntries.ReplicatedLogEntry.Payload payload) {
+            return null;
+        }
+
+        @Override
+        public int size() {
+            return 0;
+        }
+    }
+
+    static class TestPersistentPayload extends TestNonPersistentPayload implements PersistentPayload {
+    }
+}
index 883680b06d2f66d4a1e0d9a75bceb37434beebe1..3d6f7f414ce7b1f1fc03e876fb7b35912656f23a 100644 (file)
@@ -178,6 +178,21 @@ public class RaftActorServerConfigurationSupportTest extends AbstractActorTest {
         assertEquals("Follower last applied index", 3, followerActorContext.getLastApplied());
         assertEquals("New follower commit index", 3, newFollowerActorContext.getCommitIndex());
         assertEquals("New follower last applied index", 3, newFollowerActorContext.getLastApplied());
+
+        List<ReplicatedLogImplEntry> persistedLogEntries = InMemoryJournal.get(LEADER_ID, ReplicatedLogImplEntry.class);
+        assertEquals("Leader ReplicatedLogImplEntry entries", 1, persistedLogEntries.size());
+        ReplicatedLogImplEntry logEntry = persistedLogEntries.get(0);
+        assertEquals("Leader ReplicatedLogImplEntry getTerm", 1, logEntry.getTerm());
+        assertEquals("Leader ReplicatedLogImplEntry getIndex", 3, logEntry.getIndex());
+        assertEquals("Leader ReplicatedLogImplEntry getData", ServerConfigurationPayload.class, logEntry.getData().getClass());
+
+        persistedLogEntries = InMemoryJournal.get(NEW_SERVER_ID, ReplicatedLogImplEntry.class);
+        assertEquals("New follower ReplicatedLogImplEntry entries", 1, persistedLogEntries.size());
+        logEntry = persistedLogEntries.get(0);
+        assertEquals("New follower ReplicatedLogImplEntry getTerm", 1, logEntry.getTerm());
+        assertEquals("New follower ReplicatedLogImplEntry getIndex", 3, logEntry.getIndex());
+        assertEquals("New follower ReplicatedLogImplEntry getData", ServerConfigurationPayload.class,
+                logEntry.getData().getClass());
     }
 
     @Test
@@ -701,6 +716,7 @@ public class RaftActorServerConfigurationSupportTest extends AbstractActorTest {
     public static class MockNewFollowerRaftActor extends AbstractMockRaftActor {
         public MockNewFollowerRaftActor(ConfigParams config, TestActorRef<MessageCollectorActor> collectorActor) {
             super(NEW_SERVER_ID, Maps.<String, String>newHashMap(), Optional.of(config), null, collectorActor);
+            setPersistence(false);
         }
 
         static Props props(ConfigParams config, TestActorRef<MessageCollectorActor> collectorActor) {
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/PersistentPayload.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/raft/protobuff/client/messages/PersistentPayload.java
new file mode 100644 (file)
index 0000000..828ed2c
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2015 Brocade Communications Systems, Inc. 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.protobuff.client.messages;
+
+/**
+ * This is a tagging interface for a Payload implementation that needs to always be persisted regardless of
+ * whether or not the component is configured to be persistent.
+ *
+ * @author Thomas Pantelis
+ */
+public interface PersistentPayload {
+}