private final boolean wasOwner;
private final boolean isOwner;
private final boolean hasOwner;
+ private final boolean inJeopardy;
public EntityOwnershipChange(@Nonnull Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner) {
+ this(entity, wasOwner, isOwner, hasOwner, false);
+ }
+
+ public EntityOwnershipChange(@Nonnull Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner,
+ boolean inJeopardy) {
this.entity = Preconditions.checkNotNull(entity, "entity can't be null");
this.wasOwner = wasOwner;
this.isOwner = isOwner;
this.hasOwner = hasOwner;
+ this.inJeopardy = inJeopardy;
}
/**
return hasOwner;
}
+ /**
+ * Returns the current jeopardy state. When in a jeopardy state, the values from other methods may potentially
+ * be out of date.
+ *
+ * @return true if the local node is in a jeopardy state. If false, the reported information is accurate.
+ */
+ public boolean inJeopardy() {
+ return inJeopardy;
+ }
+
@Override
public String toString() {
return "EntityOwnershipChanged [entity=" + entity + ", wasOwner=" + wasOwner + ", isOwner=" + isOwner
- + ", hasOwner=" + hasOwner + "]";
+ + ", hasOwner=" + hasOwner + ", inJeopardy=" + inJeopardy + "]";
}
}
private final Map<EntityOwnershipListener, ListenerActorRefEntry> listenerActorMap = new IdentityHashMap<>();
private final Set<Entity> entitiesWithCandidateSet = new HashSet<>();
private final Multimap<String, EntityOwnershipListener> entityTypeListenerMap = HashMultimap.create();
+ private volatile boolean inJeopardy = false;
EntityOwnershipListenerSupport(ActorContext actorContext, String logId) {
this.actorContext = actorContext;
return logId;
}
+ /**
+ * Set the in-jeopardy flag and indicate its previous state.
+ *
+ * @param inJeopardy new value of the in-jeopardy flag
+ * @return Previous value of the flag.
+ */
+ boolean setInJeopardy(final boolean inJeopardy) {
+ final boolean wasInJeopardy = this.inJeopardy;
+ this.inJeopardy = inJeopardy;
+ return wasInJeopardy;
+ }
+
boolean hasCandidateForEntity(Entity entity) {
return entitiesWithCandidateSet.contains(entity);
}
private void notifyListeners(Entity entity, boolean wasOwner, boolean isOwner, boolean hasOwner,
Collection<EntityOwnershipListener> listeners) {
- EntityOwnershipChange changed = new EntityOwnershipChange(entity, wasOwner, isOwner, hasOwner);
+ EntityOwnershipChange changed = new EntityOwnershipChange(entity, wasOwner, isOwner, hasOwner, inJeopardy);
for(EntityOwnershipListener listener: listeners) {
ActorRef listenerActor = listenerActorFor(listener);
import org.opendaylight.controller.cluster.datastore.modification.DeleteModification;
import org.opendaylight.controller.cluster.datastore.modification.MergeModification;
import org.opendaylight.controller.cluster.datastore.modification.WriteModification;
+import org.opendaylight.controller.cluster.raft.RaftState;
import org.opendaylight.controller.md.sal.common.api.clustering.Entity;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
return getLeader() != null && !isIsolatedLeader();
}
+ /**
+ * Determine if we are in jeopardy based on observed RAFT state.
+ */
+ private static boolean inJeopardy(final RaftState state) {
+ switch (state) {
+ case Candidate:
+ case Follower:
+ case Leader:
+ return false;
+ case IsolatedLeader:
+ return true;
+ }
+ throw new IllegalStateException("Unsupported RAFT state " + state);
+ }
+
+ private void notifyAllListeners() {
+ searchForEntities(new EntityWalker() {
+ @Override
+ public void onEntity(MapEntryNode entityTypeNode, MapEntryNode entityNode) {
+ Optional<DataContainerChild<?, ?>> possibleType = entityTypeNode.getChild(ENTITY_TYPE_NODE_ID);
+ if (possibleType.isPresent()) {
+ final boolean hasOwner;
+ final boolean isOwner;
+
+ Optional<DataContainerChild<?, ?>> possibleOwner = entityNode.getChild(ENTITY_OWNER_NODE_ID);
+ if (possibleOwner.isPresent()) {
+ isOwner = localMemberName.equals(possibleOwner.get().getValue().toString());
+ hasOwner = true;
+ } else {
+ isOwner = false;
+ hasOwner = false;
+ }
+
+ Entity entity = new Entity(possibleType.get().getValue().toString(),
+ (YangInstanceIdentifier) entityNode.getChild(ENTITY_ID_NODE_ID).get().getValue());
+
+ listenerSupport.notifyEntityOwnershipListeners(entity, isOwner, isOwner, hasOwner);
+ }
+ }
+ });
+ }
+
@Override
protected void onStateChanged() {
super.onStateChanged();
boolean isLeader = isLeader();
- if(LOG.isDebugEnabled()) {
- LOG.debug("{}: onStateChanged: isLeader: {}, hasLeader: {}", persistenceId(), isLeader, hasLeader());
+ LOG.debug("{}: onStateChanged: isLeader: {}, hasLeader: {}", persistenceId(), isLeader, hasLeader());
+
+ // Examine current RAFT state to see if we are in jeopardy, potentially notifying all listeners
+ final boolean inJeopardy = inJeopardy(getRaftState());
+ final boolean wasInJeopardy = listenerSupport.setInJeopardy(inJeopardy);
+ if (inJeopardy != wasInJeopardy) {
+ LOG.debug("{}: {} jeopardy state, notifying all listeners", persistenceId(),
+ inJeopardy ? "entered" : "left");
+ notifyAllListeners();
}
commitCoordinator.onStateChanged(this, isLeader);