Ayaka Koshibe
Committed by Gerrit Code Review

DistributedLeadershipManager tracks topic election candidates in addition to

leaders. Includes update to leaders CLI command to list candidates.

part of: Device Mastership store on top of LeadershipService
Reference: ONOS-76

Conflicts:
	core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DistributedLeadershipManager.java

Change-Id: I587bb9e9ad16a9c8392969dde45001181053e5e6
...@@ -17,12 +17,15 @@ package org.onosproject.cli.net; ...@@ -17,12 +17,15 @@ package org.onosproject.cli.net;
17 17
18 import java.util.Comparator; 18 import java.util.Comparator;
19 import java.util.Map; 19 import java.util.Map;
20 +import java.util.List;
20 21
21 import org.apache.karaf.shell.commands.Command; 22 import org.apache.karaf.shell.commands.Command;
23 +import org.apache.karaf.shell.commands.Option;
22 import org.onlab.util.Tools; 24 import org.onlab.util.Tools;
23 import org.onosproject.cli.AbstractShellCommand; 25 import org.onosproject.cli.AbstractShellCommand;
24 import org.onosproject.cluster.Leadership; 26 import org.onosproject.cluster.Leadership;
25 import org.onosproject.cluster.LeadershipService; 27 import org.onosproject.cluster.LeadershipService;
28 +import org.onosproject.cluster.NodeId;
26 29
27 import com.fasterxml.jackson.databind.JsonNode; 30 import com.fasterxml.jackson.databind.JsonNode;
28 import com.fasterxml.jackson.databind.ObjectMapper; 31 import com.fasterxml.jackson.databind.ObjectMapper;
...@@ -36,6 +39,12 @@ import com.fasterxml.jackson.databind.node.ArrayNode; ...@@ -36,6 +39,12 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
36 public class LeaderCommand extends AbstractShellCommand { 39 public class LeaderCommand extends AbstractShellCommand {
37 40
38 private static final String FMT = "%-20s | %-15s | %-6s | %-10s |"; 41 private static final String FMT = "%-20s | %-15s | %-6s | %-10s |";
42 + private static final String FMT_C = "%-20s | %-15s | %-19s |";
43 +
44 + @Option(name = "-c", aliases = "--candidates",
45 + description = "List candidate Nodes for each topic's leadership race",
46 + required = false, multiValued = false)
47 + private boolean showCandidates = false;
39 48
40 /** 49 /**
41 * Compares leaders, sorting by toString() output. 50 * Compares leaders, sorting by toString() output.
...@@ -75,6 +84,28 @@ public class LeaderCommand extends AbstractShellCommand { ...@@ -75,6 +84,28 @@ public class LeaderCommand extends AbstractShellCommand {
75 print("--------------------------------------------------------------"); 84 print("--------------------------------------------------------------");
76 } 85 }
77 86
87 + private void displayCandidates(Map<String, Leadership> leaderBoard,
88 + Map<String, List<NodeId>> candidates) {
89 + print("--------------------------------------------------------------");
90 + print(FMT_C, "Topic", "Leader", "Candidates");
91 + print("--------------------------------------------------------------");
92 + leaderBoard
93 + .values()
94 + .stream()
95 + .sorted(leadershipComparator)
96 + .forEach(l -> {
97 + List<NodeId> list = candidates.get(l.topic());
98 + print(FMT_C,
99 + l.topic(),
100 + l.leader(),
101 + list.remove(0).toString());
102 + // formatting hacks to get it into a table
103 + list.forEach(n -> print(FMT_C, " ", " ", n));
104 + print(FMT_C, " ", " ", " ");
105 + });
106 + print("--------------------------------------------------------------");
107 + }
108 +
78 /** 109 /**
79 * Returns JSON node representing the leaders. 110 * Returns JSON node representing the leaders.
80 * 111 *
...@@ -91,6 +122,7 @@ public class LeaderCommand extends AbstractShellCommand { ...@@ -91,6 +122,7 @@ public class LeaderCommand extends AbstractShellCommand {
91 mapper.createObjectNode() 122 mapper.createObjectNode()
92 .put("topic", l.topic()) 123 .put("topic", l.topic())
93 .put("leader", l.leader().toString()) 124 .put("leader", l.leader().toString())
125 + .put("candidates", l.candidates().toString())
94 .put("epoch", l.epoch()) 126 .put("epoch", l.epoch())
95 .put("electedTime", Tools.timeAgo(l.electedTime())))); 127 .put("electedTime", Tools.timeAgo(l.electedTime()))));
96 128
...@@ -106,7 +138,12 @@ public class LeaderCommand extends AbstractShellCommand { ...@@ -106,7 +138,12 @@ public class LeaderCommand extends AbstractShellCommand {
106 if (outputJson()) { 138 if (outputJson()) {
107 print("%s", json(leaderBoard)); 139 print("%s", json(leaderBoard));
108 } else { 140 } else {
141 + if (showCandidates) {
142 + Map<String, List<NodeId>> candidates = leaderService.getCandidates();
143 + displayCandidates(leaderBoard, candidates);
144 + } else {
109 displayLeaders(leaderBoard); 145 displayLeaders(leaderBoard);
110 } 146 }
111 } 147 }
148 + }
112 } 149 }
......
...@@ -49,11 +49,10 @@ public class LeadershipEvent extends AbstractEvent<LeadershipEvent.Type, Leaders ...@@ -49,11 +49,10 @@ public class LeadershipEvent extends AbstractEvent<LeadershipEvent.Type, Leaders
49 LEADER_BOOTED, 49 LEADER_BOOTED,
50 50
51 /** 51 /**
52 - * Signifies that the list of candidates for leadership for a resource 52 + * Signifies that the list of candidates for leadership for a topic has
53 - * has changed. If the change in the backups list is accompanied by a 53 + * changed.
54 - * change in the leader, the event is subsumed by the leadership change.
55 */ 54 */
56 - LEADER_CANDIDATES_CHANGED 55 + CANDIDATES_CHANGED
57 } 56 }
58 57
59 /** 58 /**
......
...@@ -17,6 +17,7 @@ package org.onosproject.cluster; ...@@ -17,6 +17,7 @@ package org.onosproject.cluster;
17 17
18 import java.util.Map; 18 import java.util.Map;
19 import java.util.Set; 19 import java.util.Set;
20 +import java.util.List;
20 21
21 /** 22 /**
22 * Service for leader election. 23 * Service for leader election.
...@@ -67,6 +68,19 @@ public interface LeadershipService { ...@@ -67,6 +68,19 @@ public interface LeadershipService {
67 Map<String, Leadership> getLeaderBoard(); 68 Map<String, Leadership> getLeaderBoard();
68 69
69 /** 70 /**
71 + * Returns the candidates for all known topics.
72 + * @return A map of topics to lists of NodeIds.
73 + */
74 + Map<String, List<NodeId>> getCandidates();
75 +
76 + /**
77 + * Returns the candidates for a given topic.
78 + * @param path topic
79 + * @return A lists of NodeIds, which may be empty.
80 + */
81 + List<NodeId> getCandidates(String path);
82 +
83 + /**
70 * Registers a event listener to be notified of leadership events. 84 * Registers a event listener to be notified of leadership events.
71 * @param listener listener that will asynchronously notified of leadership events. 85 * @param listener listener that will asynchronously notified of leadership events.
72 */ 86 */
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onosproject.cluster; 16 package org.onosproject.cluster;
17 17
18 +import java.util.List;
18 import java.util.Map; 19 import java.util.Map;
19 import java.util.Set; 20 import java.util.Set;
20 21
...@@ -62,4 +63,14 @@ public class LeadershipServiceAdapter implements LeadershipService { ...@@ -62,4 +63,14 @@ public class LeadershipServiceAdapter implements LeadershipService {
62 public void removeListener(LeadershipEventListener listener) { 63 public void removeListener(LeadershipEventListener listener) {
63 64
64 } 65 }
66 +
67 + @Override
68 + public Map<String, List<NodeId>> getCandidates() {
69 + return null;
70 + }
71 +
72 + @Override
73 + public List<NodeId> getCandidates(String path) {
74 + return null;
75 + }
65 } 76 }
......
...@@ -46,6 +46,7 @@ import org.slf4j.Logger; ...@@ -46,6 +46,7 @@ import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory; 46 import org.slf4j.LoggerFactory;
47 47
48 import java.util.HashMap; 48 import java.util.HashMap;
49 +import java.util.List;
49 import java.util.Map; 50 import java.util.Map;
50 import java.util.Set; 51 import java.util.Set;
51 import java.util.concurrent.ExecutorService; 52 import java.util.concurrent.ExecutorService;
...@@ -573,4 +574,14 @@ public class HazelcastLeadershipService implements LeadershipService { ...@@ -573,4 +574,14 @@ public class HazelcastLeadershipService implements LeadershipService {
573 eventDispatcher.post(leadershipEvent); 574 eventDispatcher.post(leadershipEvent);
574 } 575 }
575 } 576 }
577 +
578 + @Override
579 + public Map<String, List<NodeId>> getCandidates() {
580 + return null;
581 + }
582 +
583 + @Override
584 + public List<NodeId> getCandidates(String path) {
585 + return null;
586 + }
576 } 587 }
......
1 package org.onosproject.store.consistent.impl; 1 package org.onosproject.store.consistent.impl;
2 2
3 +import com.google.common.collect.ImmutableList;
3 import com.google.common.collect.ImmutableMap; 4 import com.google.common.collect.ImmutableMap;
5 +import com.google.common.collect.Lists;
4 import com.google.common.collect.Maps; 6 import com.google.common.collect.Maps;
5 import com.google.common.collect.Sets; 7 import com.google.common.collect.Sets;
8 +
6 import org.apache.felix.scr.annotations.Activate; 9 import org.apache.felix.scr.annotations.Activate;
7 import org.apache.felix.scr.annotations.Component; 10 import org.apache.felix.scr.annotations.Component;
8 import org.apache.felix.scr.annotations.Deactivate; 11 import org.apache.felix.scr.annotations.Deactivate;
...@@ -12,6 +15,7 @@ import org.apache.felix.scr.annotations.Service; ...@@ -12,6 +15,7 @@ import org.apache.felix.scr.annotations.Service;
12 import org.onlab.util.KryoNamespace; 15 import org.onlab.util.KryoNamespace;
13 import org.onosproject.cluster.ClusterService; 16 import org.onosproject.cluster.ClusterService;
14 import org.onosproject.cluster.ControllerNode; 17 import org.onosproject.cluster.ControllerNode;
18 +import org.onosproject.cluster.ControllerNode.State;
15 import org.onosproject.cluster.Leadership; 19 import org.onosproject.cluster.Leadership;
16 import org.onosproject.cluster.LeadershipEvent; 20 import org.onosproject.cluster.LeadershipEvent;
17 import org.onosproject.cluster.LeadershipEventListener; 21 import org.onosproject.cluster.LeadershipEventListener;
...@@ -24,8 +28,8 @@ import org.onosproject.store.cluster.messaging.ClusterMessage; ...@@ -24,8 +28,8 @@ import org.onosproject.store.cluster.messaging.ClusterMessage;
24 import org.onosproject.store.cluster.messaging.ClusterMessageHandler; 28 import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
25 import org.onosproject.store.cluster.messaging.MessageSubject; 29 import org.onosproject.store.cluster.messaging.MessageSubject;
26 import org.onosproject.store.serializers.KryoNamespaces; 30 import org.onosproject.store.serializers.KryoNamespaces;
27 -import org.onosproject.store.serializers.KryoSerializer;
28 import org.onosproject.store.service.ConsistentMap; 31 import org.onosproject.store.service.ConsistentMap;
32 +import org.onosproject.store.service.ConsistentMapException;
29 import org.onosproject.store.service.Serializer; 33 import org.onosproject.store.service.Serializer;
30 import org.onosproject.store.service.StorageService; 34 import org.onosproject.store.service.StorageService;
31 import org.onosproject.store.service.Versioned; 35 import org.onosproject.store.service.Versioned;
...@@ -35,6 +39,7 @@ import java.util.Map; ...@@ -35,6 +39,7 @@ import java.util.Map;
35 import java.util.Map.Entry; 39 import java.util.Map.Entry;
36 import java.util.Objects; 40 import java.util.Objects;
37 import java.util.Set; 41 import java.util.Set;
42 +import java.util.List;
38 import java.util.concurrent.ExecutorService; 43 import java.util.concurrent.ExecutorService;
39 import java.util.concurrent.Executors; 44 import java.util.concurrent.Executors;
40 import java.util.concurrent.ScheduledExecutorService; 45 import java.util.concurrent.ScheduledExecutorService;
...@@ -77,7 +82,9 @@ public class DistributedLeadershipManager implements LeadershipService { ...@@ -77,7 +82,9 @@ public class DistributedLeadershipManager implements LeadershipService {
77 private ScheduledExecutorService deadLockDetectionExecutor; 82 private ScheduledExecutorService deadLockDetectionExecutor;
78 private ScheduledExecutorService leadershipStatusBroadcaster; 83 private ScheduledExecutorService leadershipStatusBroadcaster;
79 84
80 - private ConsistentMap<String, NodeId> lockMap; 85 + private ConsistentMap<String, NodeId> leaderMap;
86 + private ConsistentMap<String, List<NodeId>> candidateMap;
87 +
81 private AbstractListenerRegistry<LeadershipEvent, LeadershipEventListener> 88 private AbstractListenerRegistry<LeadershipEvent, LeadershipEventListener>
82 listenerRegistry; 89 listenerRegistry;
83 private final Map<String, Leadership> leaderBoard = Maps.newConcurrentMap(); 90 private final Map<String, Leadership> leaderBoard = Maps.newConcurrentMap();
...@@ -85,25 +92,25 @@ public class DistributedLeadershipManager implements LeadershipService { ...@@ -85,25 +92,25 @@ public class DistributedLeadershipManager implements LeadershipService {
85 92
86 private Set<String> activeTopics = Sets.newConcurrentHashSet(); 93 private Set<String> activeTopics = Sets.newConcurrentHashSet();
87 94
95 + private static final int ELECTION_JOIN_ATTEMPT_INTERVAL_SEC = 2;
88 private static final int DELAY_BETWEEN_LEADER_LOCK_ATTEMPTS_SEC = 2; 96 private static final int DELAY_BETWEEN_LEADER_LOCK_ATTEMPTS_SEC = 2;
89 private static final int DEADLOCK_DETECTION_INTERVAL_SEC = 2; 97 private static final int DEADLOCK_DETECTION_INTERVAL_SEC = 2;
90 private static final int LEADERSHIP_STATUS_UPDATE_INTERVAL_SEC = 2; 98 private static final int LEADERSHIP_STATUS_UPDATE_INTERVAL_SEC = 2;
91 99
92 - private static final KryoSerializer SERIALIZER = new KryoSerializer() { 100 + private static final int LEADER_CANDIDATE_POS = 0;
93 - @Override 101 +
94 - protected void setupKryoPool() { 102 + private static final Serializer SERIALIZER = Serializer.using(
95 - serializerPool = KryoNamespace.newBuilder() 103 + new KryoNamespace.Builder().register(KryoNamespaces.API).build());
96 - .register(KryoNamespaces.API)
97 - .build()
98 - .populate(1);
99 - }
100 - };
101 104
102 @Activate 105 @Activate
103 public void activate() { 106 public void activate() {
104 - lockMap = storageService.<String, NodeId>consistentMapBuilder() 107 + leaderMap = storageService.<String, NodeId>consistentMapBuilder()
105 - .withName("onos-leader-locks") 108 + .withName("onos-topic-leaders")
106 - .withSerializer(Serializer.using(new KryoNamespace.Builder().register(KryoNamespaces.API).build())) 109 + .withSerializer(SERIALIZER)
110 + .withPartitionsDisabled().build();
111 + candidateMap = storageService.<String, List<NodeId>>consistentMapBuilder()
112 + .withName("onos-topic-candidates")
113 + .withSerializer(SERIALIZER)
107 .withPartitionsDisabled().build(); 114 .withPartitionsDisabled().build();
108 115
109 localNodeId = clusterService.getLocalNode().id(); 116 localNodeId = clusterService.getLocalNode().id();
...@@ -157,6 +164,19 @@ public class DistributedLeadershipManager implements LeadershipService { ...@@ -157,6 +164,19 @@ public class DistributedLeadershipManager implements LeadershipService {
157 } 164 }
158 165
159 @Override 166 @Override
167 + public Map<String, List<NodeId>> getCandidates() {
168 + Map<String, List<NodeId>> candidates = Maps.newHashMap();
169 + candidateMap.entrySet().forEach(el -> candidates.put(el.getKey(), el.getValue().value()));
170 + return ImmutableMap.copyOf(candidates);
171 + }
172 +
173 + @Override
174 + public List<NodeId> getCandidates(String path) {
175 + Versioned<List<NodeId>> candidates = candidateMap.get(path);
176 + return candidates == null ? ImmutableList.of() : ImmutableList.copyOf(candidates.value());
177 + }
178 +
179 + @Override
160 public NodeId getLeader(String path) { 180 public NodeId getLeader(String path) {
161 Leadership leadership = leaderBoard.get(path); 181 Leadership leadership = leaderBoard.get(path);
162 return leadership != null ? leadership.leader() : null; 182 return leadership != null ? leadership.leader() : null;
...@@ -181,24 +201,62 @@ public class DistributedLeadershipManager implements LeadershipService { ...@@ -181,24 +201,62 @@ public class DistributedLeadershipManager implements LeadershipService {
181 @Override 201 @Override
182 public void runForLeadership(String path) { 202 public void runForLeadership(String path) {
183 log.debug("Running for leadership for topic: {}", path); 203 log.debug("Running for leadership for topic: {}", path);
204 + try {
205 + Versioned<List<NodeId>> candidates = candidateMap.get(path);
206 + if (candidates != null) {
207 + List<NodeId> candidateList = Lists.newArrayList(candidates.value());
208 + if (!candidateList.contains(localNodeId)) {
209 + candidateList.add(localNodeId);
210 + if (!candidateMap.replace(path, candidates.version(), candidateList)) {
211 + rerunForLeadership(path);
212 + return;
213 + }
214 + }
215 + } else {
216 + if (!(candidateMap.putIfAbsent(path, ImmutableList.of(localNodeId)) == null)) {
217 + rerunForLeadership(path);
218 + return;
219 + }
220 + }
221 + log.debug("In the leadership race for topic {} with candidates {}", path, candidates);
184 activeTopics.add(path); 222 activeTopics.add(path);
185 tryLeaderLock(path); 223 tryLeaderLock(path);
224 + } catch (ConsistentMapException e) {
225 + log.debug("Failed to enter topic leader race for {}. Retrying.", path, e);
226 + rerunForLeadership(path);
227 + }
186 } 228 }
187 229
188 @Override 230 @Override
189 public void withdraw(String path) { 231 public void withdraw(String path) {
190 activeTopics.remove(path); 232 activeTopics.remove(path);
233 +
191 try { 234 try {
192 - Versioned<NodeId> leader = lockMap.get(path); 235 + Versioned<NodeId> leader = leaderMap.get(path);
193 - if (Objects.equals(leader.value(), localNodeId)) { 236 + if (leader != null && Objects.equals(leader.value(), localNodeId)) {
194 - if (lockMap.remove(path, leader.version())) { 237 + if (leaderMap.remove(path, leader.version())) {
195 log.info("Gave up leadership for {}", path); 238 log.info("Gave up leadership for {}", path);
196 notifyRemovedLeader(path, localNodeId, leader.version(), leader.creationTime()); 239 notifyRemovedLeader(path, localNodeId, leader.version(), leader.creationTime());
197 } 240 }
198 } 241 }
199 - // else we are not the current owner. 242 + // else we are not the current leader, can still be a candidate.
243 + Versioned<List<NodeId>> candidates = candidateMap.get(path);
244 + List<NodeId> candidateList = candidates != null
245 + ? Lists.newArrayList(candidates.value())
246 + : Lists.newArrayList();
247 + if (!candidateList.remove(localNodeId)) {
248 + return;
249 + }
250 + boolean success = candidateList.isEmpty()
251 + ? candidateMap.remove(path, candidates.version())
252 + : candidateMap.replace(path, candidates.version(), candidateList);
253 + if (!success) {
254 + log.warn("Failed to withdraw from candidates list. Will retry");
255 + retryWithdraw(path);
256 + }
200 } catch (Exception e) { 257 } catch (Exception e) {
201 log.debug("Failed to verify (and clear) any lock this node might be holding for {}", path, e); 258 log.debug("Failed to verify (and clear) any lock this node might be holding for {}", path, e);
259 + retryWithdraw(path);
202 } 260 }
203 } 261 }
204 262
...@@ -216,39 +274,62 @@ public class DistributedLeadershipManager implements LeadershipService { ...@@ -216,39 +274,62 @@ public class DistributedLeadershipManager implements LeadershipService {
216 if (!activeTopics.contains(path)) { 274 if (!activeTopics.contains(path)) {
217 return; 275 return;
218 } 276 }
277 +
278 + Versioned<List<NodeId>> candidates = candidateMap.get(path);
279 + if (candidates != null) {
280 + List<NodeId> activeNodes = candidates.value().stream()
281 + .filter(n -> clusterService.getState(n) == State.ACTIVE)
282 + .collect(Collectors.toList());
283 + if (localNodeId.equals(activeNodes.get(LEADER_CANDIDATE_POS))) {
284 + leaderLockAttempt(path, candidates.value());
285 + } else {
286 + retryLock(path);
287 + }
288 + } else {
289 + throw new IllegalStateException("should not be here");
290 + }
291 + }
292 +
293 + private void leaderLockAttempt(String path, List<NodeId> candidates) {
219 try { 294 try {
220 - Versioned<NodeId> currentLeader = lockMap.get(path); 295 + Versioned<NodeId> currentLeader = leaderMap.get(path);
221 if (currentLeader != null) { 296 if (currentLeader != null) {
222 if (localNodeId.equals(currentLeader.value())) { 297 if (localNodeId.equals(currentLeader.value())) {
223 log.info("Already has leadership for {}", path); 298 log.info("Already has leadership for {}", path);
224 - notifyNewLeader(path, localNodeId, currentLeader.version(), currentLeader.creationTime()); 299 + // FIXME: candidates can get out of sync.
300 + notifyNewLeader(
301 + path, localNodeId, candidates, currentLeader.version(), currentLeader.creationTime());
225 } else { 302 } else {
226 // someone else has leadership. will retry after sometime. 303 // someone else has leadership. will retry after sometime.
227 - retry(path); 304 + retryLock(path);
228 } 305 }
229 } else { 306 } else {
230 - if (lockMap.putIfAbsent(path, localNodeId) == null) { 307 + if (leaderMap.putIfAbsent(path, localNodeId) == null) {
231 log.info("Assumed leadership for {}", path); 308 log.info("Assumed leadership for {}", path);
232 // do a get again to get the version (epoch) 309 // do a get again to get the version (epoch)
233 - Versioned<NodeId> newLeader = lockMap.get(path); 310 + Versioned<NodeId> newLeader = leaderMap.get(path);
234 - notifyNewLeader(path, localNodeId, newLeader.version(), newLeader.creationTime()); 311 + // FIXME: candidates can get out of sync
312 + notifyNewLeader(path, localNodeId, candidates, newLeader.version(), newLeader.creationTime());
235 } else { 313 } else {
236 // someone beat us to it. 314 // someone beat us to it.
237 - retry(path); 315 + retryLock(path);
238 } 316 }
239 } 317 }
240 } catch (Exception e) { 318 } catch (Exception e) {
241 log.debug("Attempt to acquire leadership lock for topic {} failed", path, e); 319 log.debug("Attempt to acquire leadership lock for topic {} failed", path, e);
242 - retry(path); 320 + retryLock(path);
243 } 321 }
244 } 322 }
245 323
246 - private void notifyNewLeader(String path, NodeId leader, long epoch, long electedTime) { 324 + private void notifyNewLeader(String path, NodeId leader,
247 - Leadership newLeadership = new Leadership(path, leader, epoch, electedTime); 325 + List<NodeId> candidates, long epoch, long electedTime) {
326 + Leadership newLeadership = new Leadership(path, leader, candidates, epoch, electedTime);
248 boolean updatedLeader = false; 327 boolean updatedLeader = false;
328 + log.debug("candidates for new Leadership {}", candidates);
249 synchronized (leaderBoard) { 329 synchronized (leaderBoard) {
250 Leadership currentLeader = leaderBoard.get(path); 330 Leadership currentLeader = leaderBoard.get(path);
251 if (currentLeader == null || currentLeader.epoch() < epoch) { 331 if (currentLeader == null || currentLeader.epoch() < epoch) {
332 + log.debug("updating leaderboard with new {}", newLeadership);
252 leaderBoard.put(path, newLeadership); 333 leaderBoard.put(path, newLeadership);
253 updatedLeader = true; 334 updatedLeader = true;
254 } 335 }
...@@ -256,6 +337,11 @@ public class DistributedLeadershipManager implements LeadershipService { ...@@ -256,6 +337,11 @@ public class DistributedLeadershipManager implements LeadershipService {
256 337
257 if (updatedLeader) { 338 if (updatedLeader) {
258 LeadershipEvent event = new LeadershipEvent(LeadershipEvent.Type.LEADER_ELECTED, newLeadership); 339 LeadershipEvent event = new LeadershipEvent(LeadershipEvent.Type.LEADER_ELECTED, newLeadership);
340 + notifyPeers(event);
341 + }
342 + }
343 +
344 + private void notifyPeers(LeadershipEvent event) {
259 eventDispatcher.post(event); 345 eventDispatcher.post(event);
260 clusterCommunicator.broadcast( 346 clusterCommunicator.broadcast(
261 new ClusterMessage( 347 new ClusterMessage(
...@@ -263,10 +349,11 @@ public class DistributedLeadershipManager implements LeadershipService { ...@@ -263,10 +349,11 @@ public class DistributedLeadershipManager implements LeadershipService {
263 LEADERSHIP_EVENT_MESSAGE_SUBJECT, 349 LEADERSHIP_EVENT_MESSAGE_SUBJECT,
264 SERIALIZER.encode(event))); 350 SERIALIZER.encode(event)));
265 } 351 }
266 - }
267 352
268 private void notifyRemovedLeader(String path, NodeId leader, long epoch, long electedTime) { 353 private void notifyRemovedLeader(String path, NodeId leader, long epoch, long electedTime) {
269 - Leadership oldLeadership = new Leadership(path, leader, epoch, electedTime); 354 + Versioned<List<NodeId>> candidates = candidateMap.get(path);
355 + Leadership oldLeadership = new Leadership(
356 + path, leader, candidates.value(), epoch, electedTime);
270 boolean updatedLeader = false; 357 boolean updatedLeader = false;
271 synchronized (leaderBoard) { 358 synchronized (leaderBoard) {
272 Leadership currentLeader = leaderBoard.get(path); 359 Leadership currentLeader = leaderBoard.get(path);
...@@ -316,6 +403,11 @@ public class DistributedLeadershipManager implements LeadershipService { ...@@ -316,6 +403,11 @@ public class DistributedLeadershipManager implements LeadershipService {
316 leaderBoard.remove(topic); 403 leaderBoard.remove(topic);
317 updateAccepted = true; 404 updateAccepted = true;
318 } 405 }
406 + } else if (eventType.equals(LeadershipEvent.Type.CANDIDATES_CHANGED)) {
407 + if (currentLeadership != null && currentLeadership.epoch() == leadershipUpdate.epoch()) {
408 + leaderBoard.replace(topic, leadershipUpdate);
409 + updateAccepted = true;
410 + }
319 } else { 411 } else {
320 throw new IllegalStateException("Unknown event type."); 412 throw new IllegalStateException("Unknown event type.");
321 } 413 }
...@@ -326,43 +418,46 @@ public class DistributedLeadershipManager implements LeadershipService { ...@@ -326,43 +418,46 @@ public class DistributedLeadershipManager implements LeadershipService {
326 } 418 }
327 } 419 }
328 420
329 - private void retry(String path) { 421 + private void rerunForLeadership(String path) {
422 + retryLeaderLockExecutor.schedule(
423 + () -> runForLeadership(path),
424 + ELECTION_JOIN_ATTEMPT_INTERVAL_SEC,
425 + TimeUnit.SECONDS);
426 + }
427 +
428 + private void retryLock(String path) {
330 retryLeaderLockExecutor.schedule( 429 retryLeaderLockExecutor.schedule(
331 () -> tryLeaderLock(path), 430 () -> tryLeaderLock(path),
332 DELAY_BETWEEN_LEADER_LOCK_ATTEMPTS_SEC, 431 DELAY_BETWEEN_LEADER_LOCK_ATTEMPTS_SEC,
333 TimeUnit.SECONDS); 432 TimeUnit.SECONDS);
334 } 433 }
335 434
435 + private void retryWithdraw(String path) {
436 + retryLeaderLockExecutor.schedule(
437 + () -> withdraw(path),
438 + DELAY_BETWEEN_LEADER_LOCK_ATTEMPTS_SEC,
439 + TimeUnit.SECONDS);
440 + }
441 +
336 private void purgeStaleLocks() { 442 private void purgeStaleLocks() {
337 try { 443 try {
338 - Set<Entry<String, Versioned<NodeId>>> entries = lockMap.entrySet(); 444 + leaderMap.entrySet()
339 - entries.forEach(entry -> { 445 + .stream()
446 + .filter(e -> clusterService.getState(e.getValue().value()) == ControllerNode.State.INACTIVE)
447 + .filter(e -> localNodeId.equals(e.getValue().value()) && !activeTopics.contains(e.getKey()))
448 + .forEach(entry -> {
340 String path = entry.getKey(); 449 String path = entry.getKey();
341 NodeId nodeId = entry.getValue().value(); 450 NodeId nodeId = entry.getValue().value();
342 long epoch = entry.getValue().version(); 451 long epoch = entry.getValue().version();
343 long creationTime = entry.getValue().creationTime(); 452 long creationTime = entry.getValue().creationTime();
344 - if (clusterService.getState(nodeId) == ControllerNode.State.INACTIVE) {
345 - log.info("Lock for {} is held by {} which is currently inactive", path, nodeId);
346 - try {
347 - if (lockMap.remove(path, epoch)) {
348 - log.info("Purged stale lock held by {} for {}", nodeId, path);
349 - notifyRemovedLeader(path, nodeId, epoch, creationTime);
350 - }
351 - } catch (Exception e) {
352 - log.warn("Failed to purge stale lock held by {} for {}", nodeId, path, e);
353 - }
354 - }
355 - if (localNodeId.equals(nodeId) && !activeTopics.contains(path)) {
356 - log.debug("Lock for {} is held by {} when it not running for leadership.", path, nodeId);
357 try { 453 try {
358 - if (lockMap.remove(path, epoch)) { 454 + if (leaderMap.remove(path, epoch)) {
359 log.info("Purged stale lock held by {} for {}", nodeId, path); 455 log.info("Purged stale lock held by {} for {}", nodeId, path);
360 notifyRemovedLeader(path, nodeId, epoch, creationTime); 456 notifyRemovedLeader(path, nodeId, epoch, creationTime);
361 } 457 }
362 } catch (Exception e) { 458 } catch (Exception e) {
363 log.warn("Failed to purge stale lock held by {} for {}", nodeId, path, e); 459 log.warn("Failed to purge stale lock held by {} for {}", nodeId, path, e);
364 } 460 }
365 - }
366 }); 461 });
367 } catch (Exception e) { 462 } catch (Exception e) {
368 log.debug("Failed cleaning up stale locks", e); 463 log.debug("Failed cleaning up stale locks", e);
......
...@@ -17,6 +17,7 @@ package org.onosproject.store.trivial.impl; ...@@ -17,6 +17,7 @@ package org.onosproject.store.trivial.impl;
17 17
18 import static com.google.common.base.Preconditions.checkArgument; 18 import static com.google.common.base.Preconditions.checkArgument;
19 19
20 +import java.util.List;
20 import java.util.Map; 21 import java.util.Map;
21 import java.util.Map.Entry; 22 import java.util.Map.Entry;
22 import java.util.Set; 23 import java.util.Set;
...@@ -108,4 +109,14 @@ public class SimpleLeadershipManager implements LeadershipService { ...@@ -108,4 +109,14 @@ public class SimpleLeadershipManager implements LeadershipService {
108 public void removeListener(LeadershipEventListener listener) { 109 public void removeListener(LeadershipEventListener listener) {
109 listeners.remove(listener); 110 listeners.remove(listener);
110 } 111 }
112 +
113 + @Override
114 + public Map<String, List<NodeId>> getCandidates() {
115 + return null;
116 + }
117 +
118 + @Override
119 + public List<NodeId> getCandidates(String path) {
120 + return null;
121 + }
111 } 122 }
......