onStateChanged();
}
+ String lastLeaderId = oldBehavior == null ? null : oldBehaviorState.getLastLeaderId();
String lastValidLeaderId = oldBehavior == null ? null : oldBehaviorState.getLastValidLeaderId();
String oldBehaviorStateName = oldBehavior == null ? null : oldBehavior.state().name();
// it can happen that the state has not changed but the leader has changed.
Optional<ActorRef> roleChangeNotifier = getRoleChangeNotifier();
- if(!Objects.equals(lastValidLeaderId, currentBehavior.getLeaderId()) ||
+ if(!Objects.equals(lastLeaderId, currentBehavior.getLeaderId()) ||
oldBehaviorState.getLeaderPayloadVersion() != currentBehavior.getLeaderPayloadVersion()) {
if(roleChangeNotifier.isPresent()) {
roleChangeNotifier.get().tell(newLeaderStateChanged(getId(), currentBehavior.getLeaderId(),
private static abstract class BehaviorState implements Immutable {
@Nullable abstract RaftActorBehavior getBehavior();
@Nullable abstract String getLastValidLeaderId();
+ @Nullable abstract String getLastLeaderId();
@Nullable abstract short getLeaderPayloadVersion();
}
private static final class SimpleBehaviorState extends BehaviorState {
private final RaftActorBehavior behavior;
private final String lastValidLeaderId;
+ private final String lastLeaderId;
private final short leaderPayloadVersion;
- SimpleBehaviorState(final String lastValidLeaderId, final RaftActorBehavior behavior) {
+ SimpleBehaviorState(final String lastValidLeaderId, final String lastLeaderId,
+ final RaftActorBehavior behavior) {
this.lastValidLeaderId = lastValidLeaderId;
+ this.lastLeaderId = lastLeaderId;
this.behavior = Preconditions.checkNotNull(behavior);
this.leaderPayloadVersion = behavior.getLeaderPayloadVersion();
}
short getLeaderPayloadVersion() {
return leaderPayloadVersion;
}
+
+ @Override
+ String getLastLeaderId() {
+ return lastLeaderId;
+ }
}
/**
short getLeaderPayloadVersion() {
return -1;
}
+
+ @Override
+ String getLastLeaderId() {
+ return null;
+ }
};
private String lastValidLeaderId;
+ private String lastLeaderId;
BehaviorState capture(final RaftActorBehavior behavior) {
if (behavior == null) {
return NULL_BEHAVIOR_STATE;
}
- final String leaderId = behavior.getLeaderId();
- if (leaderId != null) {
- lastValidLeaderId = leaderId;
+ lastLeaderId = behavior.getLeaderId();
+ if (lastLeaderId != null) {
+ lastValidLeaderId = lastLeaderId;
}
- return new SimpleBehaviorState(lastValidLeaderId, behavior);
+ return new SimpleBehaviorState(lastValidLeaderId, lastLeaderId, behavior);
}
}
package org.opendaylight.controller.cluster.raft;
import static org.junit.Assert.assertEquals;
+import akka.actor.ActorRef;
+import akka.dispatch.Dispatchers;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
import org.junit.Test;
+import org.opendaylight.controller.cluster.notifications.LeaderStateChanged;
+import org.opendaylight.controller.cluster.raft.AbstractRaftActorIntegrationTest.TestRaftActor.Builder;
import org.opendaylight.controller.cluster.raft.ServerConfigurationPayload.ServerInfo;
import org.opendaylight.controller.cluster.raft.base.messages.ApplyState;
+import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
import org.opendaylight.controller.cluster.raft.base.messages.SnapshotComplete;
import org.opendaylight.controller.cluster.raft.base.messages.UpdateElectionTerm;
import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
import org.opendaylight.controller.cluster.raft.policy.DisableElectionsRaftPolicy;
import org.opendaylight.controller.cluster.raft.utils.InMemoryJournal;
import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
+import scala.concurrent.duration.FiniteDuration;
/**
* Integration test for various scenarios involving non-voting followers.
public class NonVotingFollowerIntegrationTest extends AbstractRaftActorIntegrationTest {
private TestRaftActor followerInstance;
private TestRaftActor leaderInstance;
+ private final Builder follower1Builder = TestRaftActor.newBuilder();
/**
* Tests non-voting follower re-sync after the non-persistent leader restarts with an empty log. In this
testLog.info("testFollowerResyncWithMoreLeaderLogEntriesAndDownPeerAfterNonPersistentLeaderRestart ending");
}
+ @Test
+ public void testFollowerLeaderStateChanges() {
+ testLog.info("testFollowerLeaderStateChanges");
+
+ ActorRef roleChangeNotifier = factory.<MessageCollectorActor>createTestActor(
+ MessageCollectorActor.props().withDispatcher(Dispatchers.DefaultDispatcherId()),
+ factory.generateActorId("roleChangeNotifier"));
+ follower1Builder.roleChangeNotifier(roleChangeNotifier);
+
+ setupLeaderAndNonVotingFollower();
+
+ ((DefaultConfigParamsImpl)follower1Context.getConfigParams()).setElectionTimeoutFactor(2);
+ ((DefaultConfigParamsImpl)follower1Context.getConfigParams()).
+ setHeartBeatInterval(FiniteDuration.apply(100, TimeUnit.MILLISECONDS));
+
+ MessageCollectorActor.clearMessages(roleChangeNotifier);
+ follower1Actor.tell(ElectionTimeout.INSTANCE, ActorRef.noSender());
+ followerInstance.startDropMessages(AppendEntries.class);
+
+ LeaderStateChanged leaderStateChanged = MessageCollectorActor.expectFirstMatching(roleChangeNotifier,
+ LeaderStateChanged.class);
+ assertEquals("getLeaderId", null, leaderStateChanged.getLeaderId());
+
+ MessageCollectorActor.clearMessages(roleChangeNotifier);
+ followerInstance.stopDropMessages(AppendEntries.class);
+
+ leaderStateChanged = MessageCollectorActor.expectFirstMatching(roleChangeNotifier,
+ LeaderStateChanged.class);
+ assertEquals("getLeaderId", leaderId, leaderStateChanged.getLeaderId());
+ }
+
private void createNewLeaderActor() {
expSnapshotState.clear();
leaderActor = newTestRaftActor(leaderId, TestRaftActor.newBuilder().peerAddresses(peerAddresses).
InMemoryJournal.addEntry(follower1Id, 2, persistedServerConfigEntry);
DefaultConfigParamsImpl followerConfigParams = newFollowerConfigParams();
- follower1Actor = newTestRaftActor(follower1Id, TestRaftActor.newBuilder().peerAddresses(
+ follower1Actor = newTestRaftActor(follower1Id, follower1Builder.peerAddresses(
ImmutableMap.of(leaderId, testActorPath(leaderId))).config(followerConfigParams).
persistent(Optional.of(false)));