import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier;
import org.opendaylight.controller.cluster.datastore.identifiers.ShardManagerIdentifier;
import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shardmanager.ShardManagerInfo;
+import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shardmanager.ShardManagerInfoMBean;
import org.opendaylight.controller.cluster.datastore.messages.ActorInitialized;
import org.opendaylight.controller.cluster.datastore.messages.ActorNotInitialized;
import org.opendaylight.controller.cluster.datastore.messages.FindLocalShard;
import org.opendaylight.controller.cluster.notifications.RegisterRoleChangeListener;
import org.opendaylight.controller.cluster.notifications.RoleChangeNotification;
import org.opendaylight.controller.cluster.raft.RaftState;
+import org.opendaylight.controller.cluster.raft.base.messages.FollowerInitialSyncUpStatus;
import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.slf4j.Logger;
ignoreMessage(message);
} else if(message instanceof DatastoreContext) {
onDatastoreContext((DatastoreContext)message);
- } else if(message instanceof RoleChangeNotification){
+ } else if(message instanceof RoleChangeNotification) {
onRoleChangeNotification((RoleChangeNotification) message);
+ } else if(message instanceof FollowerInitialSyncUpStatus){
+ onFollowerInitialSyncStatus((FollowerInitialSyncUpStatus) message);
} else{
unknownMessage(message);
}
}
- private void onRoleChangeNotification(RoleChangeNotification message) {
- RoleChangeNotification roleChanged = message;
+ private void onFollowerInitialSyncStatus(FollowerInitialSyncUpStatus status) {
+ LOG.info("Received follower initial sync status for {} status sync done {}", status.getName(),
+ status.isInitialSyncDone());
+
+ ShardInformation shardInformation = findShardInformation(status.getName());
+
+ if(shardInformation != null) {
+ shardInformation.setFollowerSyncStatus(status.isInitialSyncDone());
+
+ mBean.setSyncStatus(isInSync());
+ }
+
+ }
+
+ private void onRoleChangeNotification(RoleChangeNotification roleChanged) {
LOG.info("Received role changed for {} from {} to {}", roleChanged.getMemberId(),
roleChanged.getOldRole(), roleChanged.getNewRole());
waitTillReadyCountdownLatch.countDown();
}
+
+ mBean.setSyncStatus(isInSync());
}
}
return isReady;
}
+ private boolean isInSync(){
+ for (ShardInformation info : localShards.values()) {
+ if(!info.isInSync()){
+ return false;
+ }
+ }
+ return true;
+ }
+
private void onActorInitialized(Object message) {
final ActorRef sender = getSender();
return dataPersistenceProvider;
}
+ @VisibleForTesting
+ ShardManagerInfoMBean getMBean(){
+ return mBean;
+ }
+
private class ShardInformation {
private final ShardIdentifier shardId;
private final String shardName;
// flag that determines if the actor is ready for business
private boolean actorInitialized = false;
+ private boolean followerSyncStatus = false;
+
private final List<Runnable> runnablesOnInitialized = Lists.newArrayList();
private String role ;
return this.role;
}
+ public void setFollowerSyncStatus(boolean syncStatus){
+ this.followerSyncStatus = syncStatus;
+ }
+
+ public boolean isInSync(){
+ if(RaftState.Follower.name().equals(this.role)){
+ return followerSyncStatus;
+ } else if(RaftState.Leader.name().equals(this.role)){
+ return true;
+ }
+
+ return false;
+ }
+
}
private static class ShardManagerCreator implements Creator<ShardManager> {
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Uninterruptibles;
import java.net.URI;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import org.opendaylight.controller.cluster.datastore.utils.MockConfiguration;
import org.opendaylight.controller.cluster.notifications.RoleChangeNotification;
import org.opendaylight.controller.cluster.raft.RaftState;
+import org.opendaylight.controller.cluster.raft.base.messages.FollowerInitialSyncUpStatus;
import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
import org.opendaylight.yangtools.yang.model.api.ModuleIdentifier;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
}
+ @Test
+ public void testByDefaultSyncStatusIsFalse() throws Exception{
+ final Props persistentProps = ShardManager.props(
+ new MockClusterWrapper(),
+ new MockConfiguration(),
+ DatastoreContext.newBuilder().persistent(true).build(), ready);
+ final TestActorRef<ShardManager> shardManager =
+ TestActorRef.create(getSystem(), persistentProps);
+
+ ShardManager shardManagerActor = shardManager.underlyingActor();
+
+ assertEquals(false, shardManagerActor.getMBean().getSyncStatus());
+ }
+
+ @Test
+ public void testWhenShardIsLeaderSyncStatusIsTrue() throws Exception{
+ final Props persistentProps = ShardManager.props(
+ new MockClusterWrapper(),
+ new MockConfiguration(),
+ DatastoreContext.newBuilder().persistent(true).build(), ready);
+ final TestActorRef<ShardManager> shardManager =
+ TestActorRef.create(getSystem(), persistentProps);
+
+ ShardManager shardManagerActor = shardManager.underlyingActor();
+ shardManagerActor.onReceiveCommand(new RoleChangeNotification("member-1-shard-default-unknown",
+ RaftState.Follower.name(), RaftState.Leader.name()));
+
+ assertEquals(true, shardManagerActor.getMBean().getSyncStatus());
+ }
+
+ @Test
+ public void testWhenShardIsCandidateSyncStatusIsFalse() throws Exception{
+ final Props persistentProps = ShardManager.props(
+ new MockClusterWrapper(),
+ new MockConfiguration(),
+ DatastoreContext.newBuilder().persistent(true).build(), ready);
+ final TestActorRef<ShardManager> shardManager =
+ TestActorRef.create(getSystem(), persistentProps);
+
+ ShardManager shardManagerActor = shardManager.underlyingActor();
+ shardManagerActor.onReceiveCommand(new RoleChangeNotification("member-1-shard-default-unknown",
+ RaftState.Follower.name(), RaftState.Candidate.name()));
+
+ assertEquals(false, shardManagerActor.getMBean().getSyncStatus());
+
+ // Send a FollowerInitialSyncStatus with status = true for the replica whose current state is candidate
+ shardManagerActor.onReceiveCommand(new FollowerInitialSyncUpStatus(true, "member-1-shard-default-unknown"));
+
+ assertEquals(false, shardManagerActor.getMBean().getSyncStatus());
+ }
+
+ @Test
+ public void testWhenShardIsFollowerSyncStatusDependsOnFollowerInitialSyncStatus() throws Exception{
+ final Props persistentProps = ShardManager.props(
+ new MockClusterWrapper(),
+ new MockConfiguration(),
+ DatastoreContext.newBuilder().persistent(true).build(), ready);
+ final TestActorRef<ShardManager> shardManager =
+ TestActorRef.create(getSystem(), persistentProps);
+
+ ShardManager shardManagerActor = shardManager.underlyingActor();
+ shardManagerActor.onReceiveCommand(new RoleChangeNotification("member-1-shard-default-unknown",
+ RaftState.Candidate.name(), RaftState.Follower.name()));
+
+ // Initially will be false
+ assertEquals(false, shardManagerActor.getMBean().getSyncStatus());
+
+ // Send status true will make sync status true
+ shardManagerActor.onReceiveCommand(new FollowerInitialSyncUpStatus(true, "member-1-shard-default-unknown"));
+
+ assertEquals(true, shardManagerActor.getMBean().getSyncStatus());
+
+ // Send status false will make sync status false
+ shardManagerActor.onReceiveCommand(new FollowerInitialSyncUpStatus(false, "member-1-shard-default-unknown"));
+
+ assertEquals(false, shardManagerActor.getMBean().getSyncStatus());
+
+ }
+
+ @Test
+ public void testWhenMultipleShardsPresentSyncStatusMustBeTrueForAllShards() throws Exception{
+ final Props persistentProps = ShardManager.props(
+ new MockClusterWrapper(),
+ new MockConfiguration() {
+ @Override
+ public List<String> getMemberShardNames(String memberName) {
+ return Arrays.asList("default", "astronauts");
+ }
+ },
+ DatastoreContext.newBuilder().persistent(true).build(), ready);
+ final TestActorRef<ShardManager> shardManager =
+ TestActorRef.create(getSystem(), persistentProps);
+
+ ShardManager shardManagerActor = shardManager.underlyingActor();
+
+ // Initially will be false
+ assertEquals(false, shardManagerActor.getMBean().getSyncStatus());
+
+ // Make default shard leader
+ shardManagerActor.onReceiveCommand(new RoleChangeNotification("member-1-shard-default-unknown",
+ RaftState.Follower.name(), RaftState.Leader.name()));
+
+ // default = Leader, astronauts is unknown so sync status remains false
+ assertEquals(false, shardManagerActor.getMBean().getSyncStatus());
+
+ // Make astronauts shard leader as well
+ shardManagerActor.onReceiveCommand(new RoleChangeNotification("member-1-shard-astronauts-unknown",
+ RaftState.Follower.name(), RaftState.Leader.name()));
+
+ // Now sync status should be true
+ assertEquals(true, shardManagerActor.getMBean().getSyncStatus());
+
+ // Make astronauts a Follower
+ shardManagerActor.onReceiveCommand(new RoleChangeNotification("member-1-shard-astronauts-unknown",
+ RaftState.Leader.name(), RaftState.Follower.name()));
+
+ // Sync status is not true
+ assertEquals(false, shardManagerActor.getMBean().getSyncStatus());
+
+ // Make the astronauts follower sync status true
+ shardManagerActor.onReceiveCommand(new FollowerInitialSyncUpStatus(true, "member-1-shard-astronauts-unknown"));
+
+ // Sync status is now true
+ assertEquals(true, shardManagerActor.getMBean().getSyncStatus());
+
+ }
private static class TestShardManager extends ShardManager {
private final CountDownLatch recoveryComplete = new CountDownLatch(1);