@Override
void onNewLeader(String newLeader) {
+ if(newLeader == null) {
+ return;
+ }
+
LOG.debug("{}: New leader {} elected", raftContext.getId(), newLeader);
timer.cancel();
protected void scheduleElection(FiniteDuration interval) {
stopElection();
- if(canStartElection()) {
- // Schedule an election. When the scheduler triggers an ElectionTimeout message is sent to itself
- electionCancel = context.getActorSystem().scheduler().scheduleOnce(interval, context.getActor(),
- ElectionTimeout.INSTANCE, context.getActorSystem().dispatcher(), context.getActor());
- }
+ // Schedule an election. When the scheduler triggers an ElectionTimeout message is sent to itself
+ electionCancel = context.getActorSystem().scheduler().scheduleOnce(interval, context.getActor(),
+ ElectionTimeout.INSTANCE, context.getActorSystem().dispatcher(), context.getActor());
}
/**
import akka.actor.ActorRef;
import akka.japi.Procedure;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
import java.util.ArrayList;
+import javax.annotation.Nullable;
import org.opendaylight.controller.cluster.raft.RaftActorContext;
import org.opendaylight.controller.cluster.raft.RaftState;
import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
initialSyncStatusTracker = new SyncStatusTracker(context.getActor(), getId(), SYNC_THRESHOLD);
- if(canStartElection()) {
- if (context.getPeerIds().isEmpty() && getLeaderId() == null) {
- actor().tell(ElectionTimeout.INSTANCE, actor());
- } else {
- scheduleElection(electionDuration());
- }
+ if (context.getPeerIds().isEmpty() && getLeaderId() == null) {
+ actor().tell(ElectionTimeout.INSTANCE, actor());
+ } else {
+ scheduleElection(electionDuration());
}
}
}
@VisibleForTesting
- protected final void setLeaderId(final String leaderId) {
- this.leaderId = Preconditions.checkNotNull(leaderId);
+ protected final void setLeaderId(@Nullable final String leaderId) {
+ this.leaderId = leaderId;
}
@Override
LOG.debug("{}: Received ElectionTimeout - switching to Candidate", logName());
return internalSwitchBehavior(RaftState.Candidate);
} else {
+ setLeaderId(null);
+ scheduleElection(electionDuration());
return this;
}
}
DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl();
configParams.setHeartBeatInterval(new FiniteDuration(100, TimeUnit.MILLISECONDS));
+ configParams.setElectionTimeoutFactor(5);
final String node1ID = "node1";
final String node2ID = "node2";
InMemoryJournal.addEntry(node1ID, 1, new UpdateElectionTerm(1, "downNode1"));
InMemoryJournal.addEntry(node1ID, 2, persistedServerConfigEntry);
+ InMemoryJournal.addEntry(node1ID, 3, new ApplyJournalEntries(0));
InMemoryJournal.addEntry(node2ID, 1, new UpdateElectionTerm(1, "downNode2"));
InMemoryJournal.addEntry(node2ID, 2, persistedServerConfigEntry);
+ InMemoryJournal.addEntry(node2ID, 3, new ApplyJournalEntries(0));
TestActorRef<MessageCollectorActor> node1Collector = actorFactory.createTestActor(
MessageCollectorActor.props().withDispatcher(Dispatchers.DefaultDispatcherId()),
ServerChangeReply reply = testKit.expectMsgClass(JavaTestKit.duration("5 seconds"), ServerChangeReply.class);
assertEquals("getStatus", ServerChangeStatus.NO_LEADER, reply.getStatus());
+ // Send an AppendEntries so node1 has a leaderId
+
+ MessageCollectorActor.clearMessages(node1Collector);
+
+ long term = node1RaftActor.getRaftActorContext().getTermInformation().getCurrentTerm();
+ node1RaftActorRef.tell(new AppendEntries(term, "downNode1", -1L, -1L,
+ Collections.<ReplicatedLogEntry>emptyList(), 0, -1, (short)1), ActorRef.noSender());
+
+ // Wait for the ElectionTimeout to clear the leaderId. he leaderId must be null so on the
+ // ChangeServersVotingStatus message, it will try to elect a leader.
+
+ MessageCollectorActor.expectFirstMatching(node1Collector, ElectionTimeout.class);
+
// Update node2's peer address and send the message again
node1RaftActor.setPeerAddress(node2ID, node2RaftActorRef.path().toString());
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
import org.opendaylight.controller.cluster.raft.RaftActorContext;
import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
+import org.opendaylight.controller.cluster.raft.ServerConfigurationPayload;
+import org.opendaylight.controller.cluster.raft.ServerConfigurationPayload.ServerInfo;
import org.opendaylight.controller.cluster.raft.Snapshot;
import org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot;
import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
follower = createBehavior(context);
- MessageCollectorActor.expectFirstMatching(followerActor, ElectionTimeout.class);
+ ElectionTimeout electionTimeout = MessageCollectorActor.expectFirstMatching(followerActor,
+ ElectionTimeout.class);
long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
assertTrue(elapsed < context.getConfigParams().getElectionTimeOutInterval().toMillis());
+
+ RaftActorBehavior newBehavior = follower.handleMessage(ActorRef.noSender(), electionTimeout);
+ assertTrue("Expected Candidate", newBehavior instanceof Candidate);
}
@Test
- public void testFollowerDoesNotScheduleAnElectionIfAutomaticElectionsAreDisabled(){
+ public void testFollowerSchedulesElectionIfAutomaticElectionsAreDisabled(){
MockRaftActorContext context = createActorContext();
context.setConfigParams(new DefaultConfigParamsImpl(){
@Override
follower = createBehavior(context);
- MessageCollectorActor.assertNoneMatching(followerActor, ElectionTimeout.class, 500);
+ ElectionTimeout electionTimeout = MessageCollectorActor.expectFirstMatching(followerActor,
+ ElectionTimeout.class);
+ RaftActorBehavior newBehavior = follower.handleMessage(ActorRef.noSender(), electionTimeout);
+ assertSame("handleMessage result", follower, newBehavior);
+ }
+
+ @Test
+ public void testFollowerSchedulesElectionIfNonVoting(){
+ MockRaftActorContext context = createActorContext();
+ context.updatePeerIds(new ServerConfigurationPayload(Arrays.asList(new ServerInfo(context.getId(), false))));
+ ((DefaultConfigParamsImpl)context.getConfigParams()).setHeartBeatInterval(
+ FiniteDuration.apply(100, TimeUnit.MILLISECONDS));
+ ((DefaultConfigParamsImpl)context.getConfigParams()).setElectionTimeoutFactor(1);
+
+ follower = new Follower(context, "leader", (short)1);
+
+ ElectionTimeout electionTimeout = MessageCollectorActor.expectFirstMatching(followerActor,
+ ElectionTimeout.class);
+ RaftActorBehavior newBehavior = follower.handleMessage(ActorRef.noSender(), electionTimeout);
+ assertSame("handleMessage result", follower, newBehavior);
+ assertNull("Expected null leaderId", follower.getLeaderId());
}
@Test