Committed by
Gerrit Code Review
Fix balance-masters functionality in the new LeadershipService based device mastership store
Change-Id: I9f64d514cee7d5a5383fd4c2fa30a8616c97785c
Showing
8 changed files
with
168 additions
and
61 deletions
| ... | @@ -67,6 +67,32 @@ public interface LeadershipService { | ... | @@ -67,6 +67,32 @@ public interface LeadershipService { |
| 67 | void withdraw(String path); | 67 | void withdraw(String path); |
| 68 | 68 | ||
| 69 | /** | 69 | /** |
| 70 | + * If the local nodeId is the leader for specified topic, this method causes it to | ||
| 71 | + * step down temporarily from leadership. | ||
| 72 | + * <p> | ||
| 73 | + * The node will continue to be in contention for leadership and can | ||
| 74 | + * potentially become the leader again if and when it becomes the highest | ||
| 75 | + * priority candidate | ||
| 76 | + * <p> | ||
| 77 | + * If the local nodeId is not the leader, this method will be a noop. | ||
| 78 | + * | ||
| 79 | + * @param path topic for which this controller node should give up leadership | ||
| 80 | + * @return true if this node stepped down from leadership, false otherwise | ||
| 81 | + */ | ||
| 82 | + boolean stepdown(String path); | ||
| 83 | + | ||
| 84 | + /** | ||
| 85 | + * Moves the specified nodeId to the top of the candidates list for the topic. | ||
| 86 | + * <p> | ||
| 87 | + * If the node is not a candidate for this topic, this method will be a noop. | ||
| 88 | + * | ||
| 89 | + * @param path leadership topic | ||
| 90 | + * @param nodeId nodeId to make the top candidate | ||
| 91 | + * @return true if nodeId is now the top candidate, false otherwise | ||
| 92 | + */ | ||
| 93 | + boolean makeTopCandidate(String path, NodeId nodeId); | ||
| 94 | + | ||
| 95 | + /** | ||
| 70 | * Returns the current leader board. | 96 | * Returns the current leader board. |
| 71 | * | 97 | * |
| 72 | * @return mapping from topic to leadership info. | 98 | * @return mapping from topic to leadership info. | ... | ... |
| ... | @@ -73,4 +73,14 @@ public class LeadershipServiceAdapter implements LeadershipService { | ... | @@ -73,4 +73,14 @@ public class LeadershipServiceAdapter implements LeadershipService { |
| 73 | public List<NodeId> getCandidates(String path) { | 73 | public List<NodeId> getCandidates(String path) { |
| 74 | return null; | 74 | return null; |
| 75 | } | 75 | } |
| 76 | -} | 76 | + |
| 77 | + @Override | ||
| 78 | + public boolean stepdown(String path) { | ||
| 79 | + return false; | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + @Override | ||
| 83 | + public boolean makeTopCandidate(String path, NodeId nodeId) { | ||
| 84 | + return false; | ||
| 85 | + } | ||
| 86 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -24,8 +24,6 @@ import org.apache.felix.scr.annotations.Reference; | ... | @@ -24,8 +24,6 @@ import org.apache.felix.scr.annotations.Reference; |
| 24 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 24 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
| 25 | import org.apache.felix.scr.annotations.Service; | 25 | import org.apache.felix.scr.annotations.Service; |
| 26 | import org.onlab.metrics.MetricsService; | 26 | import org.onlab.metrics.MetricsService; |
| 27 | -import org.onosproject.cluster.ClusterEvent; | ||
| 28 | -import org.onosproject.cluster.ClusterEventListener; | ||
| 29 | import org.onosproject.cluster.ClusterService; | 27 | import org.onosproject.cluster.ClusterService; |
| 30 | import org.onosproject.cluster.ControllerNode; | 28 | import org.onosproject.cluster.ControllerNode; |
| 31 | import org.onosproject.cluster.NodeId; | 29 | import org.onosproject.cluster.NodeId; |
| ... | @@ -52,8 +50,6 @@ import java.util.Iterator; | ... | @@ -52,8 +50,6 @@ import java.util.Iterator; |
| 52 | import java.util.List; | 50 | import java.util.List; |
| 53 | import java.util.Map; | 51 | import java.util.Map; |
| 54 | import java.util.Set; | 52 | import java.util.Set; |
| 55 | -import java.util.concurrent.atomic.AtomicInteger; | ||
| 56 | - | ||
| 57 | import static com.google.common.base.Preconditions.checkNotNull; | 53 | import static com.google.common.base.Preconditions.checkNotNull; |
| 58 | import static com.google.common.collect.Lists.newArrayList; | 54 | import static com.google.common.collect.Lists.newArrayList; |
| 59 | import static org.onlab.metrics.MetricsUtil.startTimer; | 55 | import static org.onlab.metrics.MetricsUtil.startTimer; |
| ... | @@ -91,7 +87,6 @@ public class MastershipManager | ... | @@ -91,7 +87,6 @@ public class MastershipManager |
| 91 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 87 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 92 | protected MetricsService metricsService; | 88 | protected MetricsService metricsService; |
| 93 | 89 | ||
| 94 | - private ClusterEventListener clusterListener = new InternalClusterEventListener(); | ||
| 95 | private Timer requestRoleTimer; | 90 | private Timer requestRoleTimer; |
| 96 | 91 | ||
| 97 | @Activate | 92 | @Activate |
| ... | @@ -99,7 +94,6 @@ public class MastershipManager | ... | @@ -99,7 +94,6 @@ public class MastershipManager |
| 99 | requestRoleTimer = createTimer("Mastership", "requestRole", "responseTime"); | 94 | requestRoleTimer = createTimer("Mastership", "requestRole", "responseTime"); |
| 100 | 95 | ||
| 101 | eventDispatcher.addSink(MastershipEvent.class, listenerRegistry); | 96 | eventDispatcher.addSink(MastershipEvent.class, listenerRegistry); |
| 102 | - clusterService.addListener(clusterListener); | ||
| 103 | store.setDelegate(delegate); | 97 | store.setDelegate(delegate); |
| 104 | log.info("Started"); | 98 | log.info("Started"); |
| 105 | } | 99 | } |
| ... | @@ -107,7 +101,6 @@ public class MastershipManager | ... | @@ -107,7 +101,6 @@ public class MastershipManager |
| 107 | @Deactivate | 101 | @Deactivate |
| 108 | public void deactivate() { | 102 | public void deactivate() { |
| 109 | eventDispatcher.removeSink(MastershipEvent.class); | 103 | eventDispatcher.removeSink(MastershipEvent.class); |
| 110 | - clusterService.removeListener(clusterListener); | ||
| 111 | store.unsetDelegate(delegate); | 104 | store.unsetDelegate(delegate); |
| 112 | log.info("Stopped"); | 105 | log.info("Stopped"); |
| 113 | } | 106 | } |
| ... | @@ -282,52 +275,6 @@ public class MastershipManager | ... | @@ -282,52 +275,6 @@ public class MastershipManager |
| 282 | } | 275 | } |
| 283 | } | 276 | } |
| 284 | 277 | ||
| 285 | - //callback for reacting to cluster events | ||
| 286 | - private class InternalClusterEventListener implements ClusterEventListener { | ||
| 287 | - | ||
| 288 | - // A notion of a local maximum cluster size, used to tie-break. | ||
| 289 | - // Think of a better way to do this. | ||
| 290 | - private AtomicInteger clusterSize; | ||
| 291 | - | ||
| 292 | - InternalClusterEventListener() { | ||
| 293 | - clusterSize = new AtomicInteger(0); | ||
| 294 | - } | ||
| 295 | - | ||
| 296 | - @Override | ||
| 297 | - public void event(ClusterEvent event) { | ||
| 298 | - switch (event.type()) { | ||
| 299 | - case INSTANCE_ADDED: | ||
| 300 | - case INSTANCE_ACTIVATED: | ||
| 301 | - clusterSize.incrementAndGet(); | ||
| 302 | - log.info("instance {} added/activated", event.subject()); | ||
| 303 | - break; | ||
| 304 | - case INSTANCE_REMOVED: | ||
| 305 | - case INSTANCE_DEACTIVATED: | ||
| 306 | - ControllerNode node = event.subject(); | ||
| 307 | - log.info("instance {} removed/deactivated", node); | ||
| 308 | - store.relinquishAllRole(node.id()); | ||
| 309 | - | ||
| 310 | - clusterSize.decrementAndGet(); | ||
| 311 | - break; | ||
| 312 | - default: | ||
| 313 | - log.warn("unknown cluster event {}", event); | ||
| 314 | - } | ||
| 315 | - } | ||
| 316 | - | ||
| 317 | - // Can be removed if we go with naive split-brain handling: only majority | ||
| 318 | - // assigns mastership | ||
| 319 | - private boolean isInMajority() { | ||
| 320 | - if (clusterService.getNodes().size() > (clusterSize.intValue() / 2)) { | ||
| 321 | - return true; | ||
| 322 | - } | ||
| 323 | - | ||
| 324 | - //FIXME: break tie for equal-sized clusters, | ||
| 325 | - | ||
| 326 | - return false; | ||
| 327 | - } | ||
| 328 | - | ||
| 329 | - } | ||
| 330 | - | ||
| 331 | public class InternalDelegate implements MastershipStoreDelegate { | 278 | public class InternalDelegate implements MastershipStoreDelegate { |
| 332 | 279 | ||
| 333 | @Override | 280 | @Override | ... | ... |
| ... | @@ -536,7 +536,7 @@ public class DeviceManager | ... | @@ -536,7 +536,7 @@ public class DeviceManager |
| 536 | if (myNextRole == NONE) { | 536 | if (myNextRole == NONE) { |
| 537 | mastershipService.requestRoleFor(did); | 537 | mastershipService.requestRoleFor(did); |
| 538 | MastershipTerm term = termService.getMastershipTerm(did); | 538 | MastershipTerm term = termService.getMastershipTerm(did); |
| 539 | - if (myNodeId.equals(term.master())) { | 539 | + if (term != null && myNodeId.equals(term.master())) { |
| 540 | myNextRole = MASTER; | 540 | myNextRole = MASTER; |
| 541 | } else { | 541 | } else { |
| 542 | myNextRole = STANDBY; | 542 | myNextRole = STANDBY; | ... | ... |
| ... | @@ -584,4 +584,14 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -584,4 +584,14 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 584 | public List<NodeId> getCandidates(String path) { | 584 | public List<NodeId> getCandidates(String path) { |
| 585 | return null; | 585 | return null; |
| 586 | } | 586 | } |
| 587 | + | ||
| 588 | + @Override | ||
| 589 | + public boolean stepdown(String path) { | ||
| 590 | + throw new UnsupportedOperationException(); | ||
| 591 | + } | ||
| 592 | + | ||
| 593 | + @Override | ||
| 594 | + public boolean makeTopCandidate(String path, NodeId nodeId) { | ||
| 595 | + throw new UnsupportedOperationException(); | ||
| 596 | + } | ||
| 587 | } | 597 | } | ... | ... |
| ... | @@ -34,6 +34,7 @@ import org.onosproject.store.service.StorageService; | ... | @@ -34,6 +34,7 @@ import org.onosproject.store.service.StorageService; |
| 34 | import org.onosproject.store.service.Versioned; | 34 | import org.onosproject.store.service.Versioned; |
| 35 | import org.slf4j.Logger; | 35 | import org.slf4j.Logger; |
| 36 | 36 | ||
| 37 | +import java.util.ArrayList; | ||
| 37 | import java.util.Map; | 38 | import java.util.Map; |
| 38 | import java.util.Map.Entry; | 39 | import java.util.Map.Entry; |
| 39 | import java.util.Objects; | 40 | import java.util.Objects; |
| ... | @@ -48,7 +49,6 @@ import java.util.stream.Collectors; | ... | @@ -48,7 +49,6 @@ import java.util.stream.Collectors; |
| 48 | import static com.google.common.base.Preconditions.checkArgument; | 49 | import static com.google.common.base.Preconditions.checkArgument; |
| 49 | import static org.onlab.util.Tools.groupedThreads; | 50 | import static org.onlab.util.Tools.groupedThreads; |
| 50 | import static org.slf4j.LoggerFactory.getLogger; | 51 | import static org.slf4j.LoggerFactory.getLogger; |
| 51 | - | ||
| 52 | import static org.onosproject.cluster.ControllerNode.State.ACTIVE; | 52 | import static org.onosproject.cluster.ControllerNode.State.ACTIVE; |
| 53 | import static org.onosproject.cluster.ControllerNode.State.INACTIVE; | 53 | import static org.onosproject.cluster.ControllerNode.State.INACTIVE; |
| 54 | 54 | ||
| ... | @@ -210,7 +210,7 @@ public class DistributedLeadershipManager implements LeadershipService { | ... | @@ -210,7 +210,7 @@ public class DistributedLeadershipManager implements LeadershipService { |
| 210 | candidateList.add(localNodeId); | 210 | candidateList.add(localNodeId); |
| 211 | if (candidateMap.replace(path, candidates.version(), candidateList)) { | 211 | if (candidateMap.replace(path, candidates.version(), candidateList)) { |
| 212 | Versioned<List<NodeId>> newCandidates = candidateMap.get(path); | 212 | Versioned<List<NodeId>> newCandidates = candidateMap.get(path); |
| 213 | - notifyCandidateAdded( | 213 | + notifyCandidateUpdated( |
| 214 | path, candidateList, newCandidates.version(), newCandidates.creationTime()); | 214 | path, candidateList, newCandidates.version(), newCandidates.creationTime()); |
| 215 | } else { | 215 | } else { |
| 216 | rerunForLeadership(path); | 216 | rerunForLeadership(path); |
| ... | @@ -221,7 +221,7 @@ public class DistributedLeadershipManager implements LeadershipService { | ... | @@ -221,7 +221,7 @@ public class DistributedLeadershipManager implements LeadershipService { |
| 221 | List<NodeId> candidateList = ImmutableList.of(localNodeId); | 221 | List<NodeId> candidateList = ImmutableList.of(localNodeId); |
| 222 | if ((candidateMap.putIfAbsent(path, candidateList) == null)) { | 222 | if ((candidateMap.putIfAbsent(path, candidateList) == null)) { |
| 223 | Versioned<List<NodeId>> newCandidates = candidateMap.get(path); | 223 | Versioned<List<NodeId>> newCandidates = candidateMap.get(path); |
| 224 | - notifyCandidateAdded(path, candidateList, newCandidates.version(), newCandidates.creationTime()); | 224 | + notifyCandidateUpdated(path, candidateList, newCandidates.version(), newCandidates.creationTime()); |
| 225 | } else { | 225 | } else { |
| 226 | rerunForLeadership(path); | 226 | rerunForLeadership(path); |
| 227 | return; | 227 | return; |
| ... | @@ -270,6 +270,27 @@ public class DistributedLeadershipManager implements LeadershipService { | ... | @@ -270,6 +270,27 @@ public class DistributedLeadershipManager implements LeadershipService { |
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | @Override | 272 | @Override |
| 273 | + public boolean stepdown(String path) { | ||
| 274 | + if (!activeTopics.contains(path)) { | ||
| 275 | + return false; | ||
| 276 | + } | ||
| 277 | + | ||
| 278 | + try { | ||
| 279 | + Versioned<NodeId> leader = leaderMap.get(path); | ||
| 280 | + if (leader != null && Objects.equals(leader.value(), localNodeId)) { | ||
| 281 | + if (leaderMap.remove(path, leader.version())) { | ||
| 282 | + log.info("Gave up leadership for {}", path); | ||
| 283 | + notifyRemovedLeader(path, localNodeId, leader.version(), leader.creationTime()); | ||
| 284 | + return true; | ||
| 285 | + } | ||
| 286 | + } | ||
| 287 | + } catch (Exception e) { | ||
| 288 | + log.warn("Error executing stepdown for {}", path, e); | ||
| 289 | + } | ||
| 290 | + return false; | ||
| 291 | + } | ||
| 292 | + | ||
| 293 | + @Override | ||
| 273 | public void addListener(LeadershipEventListener listener) { | 294 | public void addListener(LeadershipEventListener listener) { |
| 274 | listenerRegistry.addListener(listener); | 295 | listenerRegistry.addListener(listener); |
| 275 | } | 296 | } |
| ... | @@ -279,6 +300,28 @@ public class DistributedLeadershipManager implements LeadershipService { | ... | @@ -279,6 +300,28 @@ public class DistributedLeadershipManager implements LeadershipService { |
| 279 | listenerRegistry.removeListener(listener); | 300 | listenerRegistry.removeListener(listener); |
| 280 | } | 301 | } |
| 281 | 302 | ||
| 303 | + @Override | ||
| 304 | + public boolean makeTopCandidate(String path, NodeId nodeId) { | ||
| 305 | + Versioned<List<NodeId>> candidates = candidateMap.get(path); | ||
| 306 | + if (candidates == null || !candidates.value().contains(nodeId)) { | ||
| 307 | + return false; | ||
| 308 | + } | ||
| 309 | + if (nodeId.equals(candidates.value().get(0))) { | ||
| 310 | + return true; | ||
| 311 | + } | ||
| 312 | + List<NodeId> currentRoster = candidates.value(); | ||
| 313 | + List<NodeId> newRoster = new ArrayList<>(currentRoster.size()); | ||
| 314 | + newRoster.add(nodeId); | ||
| 315 | + currentRoster.stream().filter(id -> !nodeId.equals(id)).forEach(newRoster::add); | ||
| 316 | + boolean updated = candidateMap.replace(path, candidates.version(), newRoster); | ||
| 317 | + if (updated) { | ||
| 318 | + Versioned<List<NodeId>> newCandidates = candidateMap.get(path); | ||
| 319 | + notifyCandidateUpdated( | ||
| 320 | + path, newCandidates.value(), newCandidates.version(), newCandidates.creationTime()); | ||
| 321 | + } | ||
| 322 | + return updated; | ||
| 323 | + } | ||
| 324 | + | ||
| 282 | private void tryLeaderLock(String path) { | 325 | private void tryLeaderLock(String path) { |
| 283 | if (!activeTopics.contains(path)) { | 326 | if (!activeTopics.contains(path)) { |
| 284 | return; | 327 | return; |
| ... | @@ -334,7 +377,7 @@ public class DistributedLeadershipManager implements LeadershipService { | ... | @@ -334,7 +377,7 @@ public class DistributedLeadershipManager implements LeadershipService { |
| 334 | } | 377 | } |
| 335 | } | 378 | } |
| 336 | 379 | ||
| 337 | - private void notifyCandidateAdded( | 380 | + private void notifyCandidateUpdated( |
| 338 | String path, List<NodeId> candidates, long epoch, long electedTime) { | 381 | String path, List<NodeId> candidates, long epoch, long electedTime) { |
| 339 | Leadership newInfo = new Leadership(path, candidates, epoch, electedTime); | 382 | Leadership newInfo = new Leadership(path, candidates, epoch, electedTime); |
| 340 | final MutableBoolean updated = new MutableBoolean(false); | 383 | final MutableBoolean updated = new MutableBoolean(false); | ... | ... |
| ... | @@ -93,6 +93,8 @@ public class ConsistentDeviceMastershipStore | ... | @@ -93,6 +93,8 @@ public class ConsistentDeviceMastershipStore |
| 93 | new MessageSubject("mastership-store-device-role-query"); | 93 | new MessageSubject("mastership-store-device-role-query"); |
| 94 | private static final MessageSubject ROLE_RELINQUISH_SUBJECT = | 94 | private static final MessageSubject ROLE_RELINQUISH_SUBJECT = |
| 95 | new MessageSubject("mastership-store-device-role-relinquish"); | 95 | new MessageSubject("mastership-store-device-role-relinquish"); |
| 96 | + private static final MessageSubject MASTERSHIP_RELINQUISH_SUBJECT = | ||
| 97 | + new MessageSubject("mastership-store-device-mastership-relinquish"); | ||
| 96 | 98 | ||
| 97 | private static final Pattern DEVICE_MASTERSHIP_TOPIC_PATTERN = | 99 | private static final Pattern DEVICE_MASTERSHIP_TOPIC_PATTERN = |
| 98 | Pattern.compile("device:(.*)"); | 100 | Pattern.compile("device:(.*)"); |
| ... | @@ -111,6 +113,7 @@ public class ConsistentDeviceMastershipStore | ... | @@ -111,6 +113,7 @@ public class ConsistentDeviceMastershipStore |
| 111 | .register(KryoNamespaces.API) | 113 | .register(KryoNamespaces.API) |
| 112 | .register(MastershipRole.class) | 114 | .register(MastershipRole.class) |
| 113 | .register(MastershipEvent.class) | 115 | .register(MastershipEvent.class) |
| 116 | + .register(MastershipEvent.Type.class) | ||
| 114 | .build(); | 117 | .build(); |
| 115 | } | 118 | } |
| 116 | }; | 119 | }; |
| ... | @@ -125,6 +128,11 @@ public class ConsistentDeviceMastershipStore | ... | @@ -125,6 +128,11 @@ public class ConsistentDeviceMastershipStore |
| 125 | clusterCommunicator.addSubscriber(ROLE_RELINQUISH_SUBJECT, | 128 | clusterCommunicator.addSubscriber(ROLE_RELINQUISH_SUBJECT, |
| 126 | new RoleRelinquishHandler(), | 129 | new RoleRelinquishHandler(), |
| 127 | messageHandlingExecutor); | 130 | messageHandlingExecutor); |
| 131 | + clusterCommunicator.addSubscriber(MASTERSHIP_RELINQUISH_SUBJECT, | ||
| 132 | + SERIALIZER::decode, | ||
| 133 | + this::relinquishMastership, | ||
| 134 | + SERIALIZER::encode, | ||
| 135 | + messageHandlingExecutor); | ||
| 128 | localNodeId = clusterService.getLocalNode().id(); | 136 | localNodeId = clusterService.getLocalNode().id(); |
| 129 | leadershipService.addListener(leadershipEventListener); | 137 | leadershipService.addListener(leadershipEventListener); |
| 130 | 138 | ||
| ... | @@ -135,6 +143,7 @@ public class ConsistentDeviceMastershipStore | ... | @@ -135,6 +143,7 @@ public class ConsistentDeviceMastershipStore |
| 135 | public void deactivate() { | 143 | public void deactivate() { |
| 136 | clusterCommunicator.removeSubscriber(ROLE_QUERY_SUBJECT); | 144 | clusterCommunicator.removeSubscriber(ROLE_QUERY_SUBJECT); |
| 137 | clusterCommunicator.removeSubscriber(ROLE_RELINQUISH_SUBJECT); | 145 | clusterCommunicator.removeSubscriber(ROLE_RELINQUISH_SUBJECT); |
| 146 | + clusterCommunicator.removeSubscriber(MASTERSHIP_RELINQUISH_SUBJECT); | ||
| 138 | messageHandlingExecutor.shutdown(); | 147 | messageHandlingExecutor.shutdown(); |
| 139 | leadershipService.removeListener(leadershipEventListener); | 148 | leadershipService.removeListener(leadershipEventListener); |
| 140 | 149 | ||
| ... | @@ -237,7 +246,22 @@ public class ConsistentDeviceMastershipStore | ... | @@ -237,7 +246,22 @@ public class ConsistentDeviceMastershipStore |
| 237 | checkArgument(nodeId != null, NODE_ID_NULL); | 246 | checkArgument(nodeId != null, NODE_ID_NULL); |
| 238 | checkArgument(deviceId != null, DEVICE_ID_NULL); | 247 | checkArgument(deviceId != null, DEVICE_ID_NULL); |
| 239 | 248 | ||
| 240 | - throw new UnsupportedOperationException("This operation is not supported in " + this.getClass().getName()); | 249 | + NodeId currentMaster = getMaster(deviceId); |
| 250 | + if (nodeId.equals(currentMaster)) { | ||
| 251 | + return null; | ||
| 252 | + } else { | ||
| 253 | + String leadershipTopic = createDeviceMastershipTopic(deviceId); | ||
| 254 | + List<NodeId> candidates = leadershipService.getCandidates(leadershipTopic); | ||
| 255 | + if (candidates.isEmpty()) { | ||
| 256 | + return null; | ||
| 257 | + } | ||
| 258 | + if (leadershipService.makeTopCandidate(leadershipTopic, nodeId)) { | ||
| 259 | + return relinquishMastership(deviceId); | ||
| 260 | + } else { | ||
| 261 | + log.warn("Failed to promote {} to mastership for {}", nodeId, deviceId); | ||
| 262 | + } | ||
| 263 | + } | ||
| 264 | + return null; | ||
| 241 | } | 265 | } |
| 242 | 266 | ||
| 243 | @Override | 267 | @Override |
| ... | @@ -254,7 +278,13 @@ public class ConsistentDeviceMastershipStore | ... | @@ -254,7 +278,13 @@ public class ConsistentDeviceMastershipStore |
| 254 | checkArgument(nodeId != null, NODE_ID_NULL); | 278 | checkArgument(nodeId != null, NODE_ID_NULL); |
| 255 | checkArgument(deviceId != null, DEVICE_ID_NULL); | 279 | checkArgument(deviceId != null, DEVICE_ID_NULL); |
| 256 | 280 | ||
| 257 | - throw new UnsupportedOperationException("This operation is not supported in " + this.getClass().getName()); | 281 | + NodeId currentMaster = getMaster(deviceId); |
| 282 | + if (!nodeId.equals(currentMaster)) { | ||
| 283 | + return null; | ||
| 284 | + } | ||
| 285 | + // FIXME: This can becomes the master again unless it | ||
| 286 | + // is demoted to the end of candidates list. | ||
| 287 | + return relinquishMastership(deviceId); | ||
| 258 | } | 288 | } |
| 259 | 289 | ||
| 260 | @Override | 290 | @Override |
| ... | @@ -294,6 +324,37 @@ public class ConsistentDeviceMastershipStore | ... | @@ -294,6 +324,37 @@ public class ConsistentDeviceMastershipStore |
| 294 | return new MastershipEvent(eventType, deviceId, getNodes(deviceId)); | 324 | return new MastershipEvent(eventType, deviceId, getNodes(deviceId)); |
| 295 | } | 325 | } |
| 296 | 326 | ||
| 327 | + private MastershipEvent relinquishMastership(DeviceId deviceId) { | ||
| 328 | + checkArgument(deviceId != null, DEVICE_ID_NULL); | ||
| 329 | + | ||
| 330 | + NodeId currentMaster = getMaster(deviceId); | ||
| 331 | + if (currentMaster == null) { | ||
| 332 | + return null; | ||
| 333 | + } | ||
| 334 | + | ||
| 335 | + if (!currentMaster.equals(localNodeId)) { | ||
| 336 | + log.info("Forwarding request to relinquish " | ||
| 337 | + + "mastership for device {} to {}", deviceId, currentMaster); | ||
| 338 | + return futureGetOrElse(clusterCommunicator.sendAndReceive( | ||
| 339 | + deviceId, | ||
| 340 | + MASTERSHIP_RELINQUISH_SUBJECT, | ||
| 341 | + SERIALIZER::encode, | ||
| 342 | + SERIALIZER::decode, | ||
| 343 | + currentMaster), null); | ||
| 344 | + } | ||
| 345 | + | ||
| 346 | + String leadershipTopic = createDeviceMastershipTopic(deviceId); | ||
| 347 | + Leadership currentLeadership = leadershipService.getLeadership(leadershipTopic); | ||
| 348 | + | ||
| 349 | + MastershipEvent.Type eventType = null; | ||
| 350 | + if (currentLeadership != null && currentLeadership.leader().equals(localNodeId)) { | ||
| 351 | + eventType = MastershipEvent.Type.MASTER_CHANGED; | ||
| 352 | + } | ||
| 353 | + | ||
| 354 | + return leadershipService.stepdown(leadershipTopic) | ||
| 355 | + ? new MastershipEvent(eventType, deviceId, getNodes(deviceId)) : null; | ||
| 356 | + } | ||
| 357 | + | ||
| 297 | private class RoleQueryHandler implements ClusterMessageHandler { | 358 | private class RoleQueryHandler implements ClusterMessageHandler { |
| 298 | @Override | 359 | @Override |
| 299 | public void handle(ClusterMessage message) { | 360 | public void handle(ClusterMessage message) { | ... | ... |
| ... | @@ -119,4 +119,14 @@ public class SimpleLeadershipManager implements LeadershipService { | ... | @@ -119,4 +119,14 @@ public class SimpleLeadershipManager implements LeadershipService { |
| 119 | public List<NodeId> getCandidates(String path) { | 119 | public List<NodeId> getCandidates(String path) { |
| 120 | return null; | 120 | return null; |
| 121 | } | 121 | } |
| 122 | + | ||
| 123 | + @Override | ||
| 124 | + public boolean stepdown(String path) { | ||
| 125 | + throw new UnsupportedOperationException(); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + @Override | ||
| 129 | + public boolean makeTopCandidate(String path, NodeId nodeId) { | ||
| 130 | + throw new UnsupportedOperationException(); | ||
| 131 | + } | ||
| 122 | } | 132 | } | ... | ... |
-
Please register or login to post a comment