From 928f7af243a1e7ff9cf2b9b58846fa16a7009cca Mon Sep 17 00:00:00 2001 From: Harman Singh Date: Thu, 27 Aug 2015 18:28:23 -0700 Subject: [PATCH] Bug 2461 : Adding capture snapshot JMX operation. Also, updated Snapshot manager code, which may throw null pointer exception, if last log entry is null Change-Id: I4363a3da38af99a4193b8ad2b1c0a5cbaf714a32 Signed-off-by: Harman Singh --- .../controller/cluster/raft/RaftActor.java | 14 +++++++++ .../cluster/raft/SnapshotManager.java | 14 +++++++-- .../messages/InitiateCaptureSnapshot.java | 15 ++++++++++ .../cluster/raft/SnapshotManagerTest.java | 29 +++++++++++++++++++ .../jmx/mbeans/shard/ShardStats.java | 10 +++++++ .../jmx/mbeans/shard/ShardStatsMXBean.java | 3 ++ 6 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/InitiateCaptureSnapshot.java diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java index ebc157bc17..7604e3575d 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java @@ -34,6 +34,7 @@ import org.opendaylight.controller.cluster.notifications.LeaderStateChanged; import org.opendaylight.controller.cluster.notifications.RoleChanged; import org.opendaylight.controller.cluster.raft.base.messages.ApplyJournalEntries; import org.opendaylight.controller.cluster.raft.base.messages.ApplyState; +import org.opendaylight.controller.cluster.raft.base.messages.InitiateCaptureSnapshot; import org.opendaylight.controller.cluster.raft.base.messages.Replicate; import org.opendaylight.controller.cluster.raft.behaviors.AbstractLeader; import org.opendaylight.controller.cluster.raft.behaviors.DelegatingRaftActorBehavior; @@ -234,6 +235,8 @@ public abstract class RaftActor extends AbstractUntypedPersistentActor { ); } else if(message instanceof GetOnDemandRaftState) { onGetOnDemandRaftStats(); + } else if(message instanceof InitiateCaptureSnapshot) { + captureSnapshot(); } else if(!snapshotSupport.handleSnapshotMessage(message)) { reusableBehaviorStateHolder.init(getCurrentBehavior()); @@ -587,6 +590,17 @@ public abstract class RaftActor extends AbstractUntypedPersistentActor { return getRaftActorContext().hasFollowers(); } + private void captureSnapshot() { + SnapshotManager snapshotManager = context.getSnapshotManager(); + + if(!snapshotManager.isCapturing()) { + LOG.debug("Take a snapshot of current state. lastReplicatedLog is {} and replicatedToAllIndex is {}", + replicatedLog().last(), currentBehavior.getReplicatedToAllIndex()); + + snapshotManager.capture(replicatedLog().last(), currentBehavior.getReplicatedToAllIndex()); + } + } + /** * @deprecated Deprecated in favor of {@link org.opendaylight.controller.cluster.raft.base.messages.DeleteEntries} * whose type for fromIndex is long instead of int. This class was kept for backwards diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/SnapshotManager.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/SnapshotManager.java index 970fb8fc71..4c3fb109ad 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/SnapshotManager.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/SnapshotManager.java @@ -211,8 +211,18 @@ public class SnapshotManager implements SnapshotState { List unAppliedEntries = context.getReplicatedLog().getFrom(lastAppliedIndex + 1); - captureSnapshot = new CaptureSnapshot(lastLogEntry.getIndex(), - lastLogEntry.getTerm(), lastAppliedIndex, lastAppliedTerm, + long lastLogEntryIndex = lastAppliedIndex; + long lastLogEntryTerm = lastAppliedTerm; + if(lastLogEntry != null) { + lastLogEntryIndex = lastLogEntry.getIndex(); + lastLogEntryTerm = lastLogEntry.getTerm(); + } else { + LOG.warn("Capturing Snapshot : lastLogEntry is null. Using lastAppliedIndex {} and lastAppliedTerm {} instead.", + lastAppliedIndex, lastAppliedTerm); + } + + captureSnapshot = new CaptureSnapshot(lastLogEntryIndex, + lastLogEntryTerm, lastAppliedIndex, lastAppliedTerm, newReplicatedToAllIndex, newReplicatedToAllTerm, unAppliedEntries, targetFollower != null); if(captureSnapshot.isInstallSnapshotInitiated()) { diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/InitiateCaptureSnapshot.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/InitiateCaptureSnapshot.java new file mode 100644 index 0000000000..d848a8442d --- /dev/null +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/base/messages/InitiateCaptureSnapshot.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2015 Cisco 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.base.messages; + +import java.io.Serializable; + +public class InitiateCaptureSnapshot implements Serializable { + private static final long serialVersionUID = 1L; +} diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/SnapshotManagerTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/SnapshotManagerTest.java index 0078c2bd84..df83f8f558 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/SnapshotManagerTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/SnapshotManagerTest.java @@ -160,6 +160,35 @@ public class SnapshotManagerTest extends AbstractActorTest { } + @Test + public void testCaptureWithNullLastLogEntry() throws Exception { + boolean capture = snapshotManager.capture(null, 1); + + assertTrue(capture); + + assertEquals(true, snapshotManager.isCapturing()); + + verify(mockProcedure).apply(null); + + CaptureSnapshot captureSnapshot = snapshotManager.getCaptureSnapshot(); + + System.out.println(captureSnapshot); + + // LastIndex and LastTerm are picked up from the lastLogEntry + assertEquals(-1L, captureSnapshot.getLastIndex()); + assertEquals(-1L, captureSnapshot.getLastTerm()); + + // Since the actor does not have any followers (no peer addresses) lastApplied will be from lastLogEntry + assertEquals(-1L, captureSnapshot.getLastAppliedIndex()); + assertEquals(-1L, captureSnapshot.getLastAppliedTerm()); + + // + assertEquals(-1L, captureSnapshot.getReplicatedToAllIndex()); + assertEquals(-1L, captureSnapshot.getReplicatedToAllTerm()); + actorRef.underlyingActor().clear(); + + } + @Test public void testCaptureWithCreateProcedureError () throws Exception { doThrow(new Exception("mock")).when(mockProcedure).apply(null); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java index 1f51c6f3a1..1e6107c786 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java @@ -8,6 +8,7 @@ package org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard; +import akka.actor.ActorRef; import akka.pattern.Patterns; import akka.util.Timeout; import com.google.common.base.Stopwatch; @@ -20,6 +21,7 @@ import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.opendaylight.controller.cluster.datastore.Shard; +import org.opendaylight.controller.cluster.raft.base.messages.InitiateCaptureSnapshot; import org.opendaylight.controller.cluster.raft.client.messages.FollowerInfo; import org.opendaylight.controller.cluster.raft.client.messages.GetOnDemandRaftState; import org.opendaylight.controller.cluster.raft.client.messages.OnDemandRaftState; @@ -344,4 +346,12 @@ public class ShardStats extends AbstractMXBean implements ShardStatsMXBean { public int getPendingTxCommitQueueSize() { return shard.getPendingTxCommitQueueSize(); } + + @Override + public void captureSnapshot() { + if(shard != null) { + shard.getSelf().tell(new InitiateCaptureSnapshot(), ActorRef.noSender()); + } + } + } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMXBean.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMXBean.java index dc45d3f626..ecc51b0917 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMXBean.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStatsMXBean.java @@ -83,4 +83,7 @@ public interface ShardStatsMXBean { String getLastLeadershipChangeTime(); int getPendingTxCommitQueueSize(); + + void captureSnapshot(); + } -- 2.36.6