X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-akka-raft%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fcluster%2Fraft%2Fbehaviors%2FLeader.java;h=eb49abc17aea4a0ebefacde0a5925312082e1405;hp=380e22a7779ab07b1268c06fb919818269459ff7;hb=1b1360ac337d23b9a586f62616eb278c3065eef0;hpb=f7f6b1742311d27967f1740795a25772da74f2c8 diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java index 380e22a777..eb49abc17a 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/behaviors/Leader.java @@ -10,6 +10,7 @@ package org.opendaylight.controller.cluster.raft.behaviors; import akka.actor.ActorRef; import akka.actor.ActorSelection; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Stopwatch; import java.util.concurrent.TimeUnit; @@ -19,12 +20,13 @@ import org.opendaylight.controller.cluster.raft.FollowerLogInformation; import org.opendaylight.controller.cluster.raft.RaftActorContext; import org.opendaylight.controller.cluster.raft.RaftActorLeadershipTransferCohort; import org.opendaylight.controller.cluster.raft.RaftState; -import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout; +import org.opendaylight.controller.cluster.raft.base.messages.TimeoutNow; import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply; /** - * The behavior of a RaftActor when it is in the Leader state - *

+ * The behavior of a RaftActor when it is in the Leader state. + * + *

* Leaders: *

*/ public class Leader extends AbstractLeader { /** @@ -52,39 +53,45 @@ public class Leader extends AbstractLeader { @VisibleForTesting static final Object ISOLATED_LEADER_CHECK = new Object(); - private final Stopwatch isolatedLeaderCheck; - private @Nullable LeadershipTransferContext leadershipTransferContext; + private final Stopwatch isolatedLeaderCheck = Stopwatch.createStarted(); + @Nullable private LeadershipTransferContext leadershipTransferContext; + + Leader(RaftActorContext context, @Nullable AbstractLeader initializeFromLeader) { + super(context, RaftState.Leader, initializeFromLeader); + } public Leader(RaftActorContext context) { - super(context, RaftState.Leader); - isolatedLeaderCheck = Stopwatch.createStarted(); + this(context, null); } - @Override public RaftActorBehavior handleMessage(ActorRef sender, Object originalMessage) { + @Override + public RaftActorBehavior handleMessage(ActorRef sender, Object originalMessage) { Preconditions.checkNotNull(sender, "sender should not be null"); if (ISOLATED_LEADER_CHECK.equals(originalMessage)) { if (isLeaderIsolated()) { - LOG.warn("{}: At least {} followers need to be active, Switching {} from Leader to IsolatedLeader", - context.getId(), getMinIsolatedLeaderPeerCount(), leaderId); - - return internalSwitchBehavior(RaftState.IsolatedLeader); + log.warn("{}: At least {} followers need to be active, Switching {} from Leader to IsolatedLeader", + context.getId(), getMinIsolatedLeaderPeerCount(), getLeaderId()); + return internalSwitchBehavior(new IsolatedLeader(context, this)); + } else { + return this; } + } else { + return super.handleMessage(sender, originalMessage); } - - return super.handleMessage(sender, originalMessage); } @Override - protected void beforeSendHeartbeat(){ - if(isolatedLeaderCheck.elapsed(TimeUnit.MILLISECONDS) > context.getConfigParams().getIsolatedCheckIntervalInMillis()){ + protected void beforeSendHeartbeat() { + if (isolatedLeaderCheck.elapsed(TimeUnit.MILLISECONDS) + > context.getConfigParams().getIsolatedCheckIntervalInMillis()) { context.getActor().tell(ISOLATED_LEADER_CHECK, context.getActor()); isolatedLeaderCheck.reset().start(); } - if(leadershipTransferContext != null && leadershipTransferContext.isExpired( + if (leadershipTransferContext != null && leadershipTransferContext.isExpired( context.getConfigParams().getElectionTimeOutInterval().toMillis())) { - LOG.debug("{}: Leadership transfer expired", logName()); + log.debug("{}: Leadership transfer expired", logName()); leadershipTransferContext = null; } } @@ -102,20 +109,20 @@ public class Leader extends AbstractLeader { *
  • Start a timer (Stopwatch).
  • *
  • Send an initial AppendEntries heartbeat to all followers.
  • *
  • On AppendEntriesReply, check if the follower's new match Index matches the leader's last index
  • - *
  • If it matches,
  • + *
  • If it matches, * + *
  • *
  • Otherwise if the election time out period elapses, notify - * {@link RaftActorLeadershipTransferCohort#abortTtransfer}.
  • + * {@link RaftActorLeadershipTransferCohort#abortTransfer}. * * - * @param leadershipTransferCohort + * @param leadershipTransferCohort the cohort participating in the leadership transfer */ public void transferLeadership(@Nonnull RaftActorLeadershipTransferCohort leadershipTransferCohort) { - LOG.debug("{}: Attempting to transfer leadership", logName()); + log.debug("{}: Attempting to transfer leadership", logName()); leadershipTransferContext = new LeadershipTransferContext(leadershipTransferCohort); @@ -124,33 +131,40 @@ public class Leader extends AbstractLeader { } private void tryToCompleteLeadershipTransfer(String followerId) { - if(leadershipTransferContext == null) { + if (leadershipTransferContext == null) { + return; + } + + final Optional requestedFollowerIdOptional + = leadershipTransferContext.transferCohort.getRequestedFollowerId(); + if (requestedFollowerIdOptional.isPresent() && !requestedFollowerIdOptional.get().equals(followerId)) { + // we want to transfer leadership to specific follower return; } FollowerLogInformation followerInfo = getFollower(followerId); - if(followerInfo == null) { + if (followerInfo == null) { return; } long lastIndex = context.getReplicatedLog().lastIndex(); boolean isVoting = context.getPeerInfo(followerId).isVoting(); - LOG.debug("{}: tryToCompleteLeadershipTransfer: followerId: {}, matchIndex: {}, lastIndex: {}, isVoting: {}", + log.debug("{}: tryToCompleteLeadershipTransfer: followerId: {}, matchIndex: {}, lastIndex: {}, isVoting: {}", logName(), followerId, followerInfo.getMatchIndex(), lastIndex, isVoting); - if(isVoting && followerInfo.getMatchIndex() == lastIndex) { - LOG.debug("{}: Follower's log matches - sending ElectionTimeout", logName()); + if (isVoting && followerInfo.getMatchIndex() == lastIndex) { + log.debug("{}: Follower's log matches - sending ElectionTimeout", logName()); // We can't be sure if the follower has applied all its log entries to its state so send an // additional AppendEntries with the latest commit index. sendAppendEntries(0, false); - // Now send an ElectionTimeout to the matching follower to immediately start an election. + // Now send a TimeoutNow message to the matching follower to immediately start an election. ActorSelection followerActor = context.getPeerActorSelection(followerId); - followerActor.tell(ElectionTimeout.INSTANCE, context.getActor()); + followerActor.tell(TimeoutNow.INSTANCE, context.getActor()); - LOG.debug("{}: Leader transfer complete", logName()); + log.debug("{}: Leader transfer complete", logName()); leadershipTransferContext.transferCohort.transferComplete(); leadershipTransferContext = null; @@ -158,9 +172,11 @@ public class Leader extends AbstractLeader { } @Override - public void close() throws Exception { - if(leadershipTransferContext != null) { - leadershipTransferContext.transferCohort.abortTransfer(); + public void close() { + if (leadershipTransferContext != null) { + LeadershipTransferContext localLeadershipTransferContext = leadershipTransferContext; + leadershipTransferContext = null; + localLeadershipTransferContext.transferCohort.abortTransfer(); } super.close(); @@ -185,7 +201,7 @@ public class Leader extends AbstractLeader { } boolean isExpired(long timeout) { - if(timer.elapsed(TimeUnit.MILLISECONDS) >= timeout) { + if (timer.elapsed(TimeUnit.MILLISECONDS) >= timeout) { transferCohort.abortTransfer(); return true; }