Madan Jampani
Committed by Gerrit Code Review

Added couple of methods to LeadershipService.

Change-Id: I259b1a282a51af9425e941a720336f89d66f1097
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 package org.onosproject.cluster; 16 package org.onosproject.cluster;
17 17
18 import java.util.Map; 18 import java.util.Map;
19 +import java.util.Set;
19 20
20 /** 21 /**
21 * Service for leader election. 22 * Service for leader election.
...@@ -27,13 +28,27 @@ import java.util.Map; ...@@ -27,13 +28,27 @@ import java.util.Map;
27 public interface LeadershipService { 28 public interface LeadershipService {
28 29
29 /** 30 /**
30 - * Gets the most recent leader for the topic. 31 + * Returns the current leader for the topic.
31 * @param path topic 32 * @param path topic
32 * @return nodeId of the leader, null if so such topic exists. 33 * @return nodeId of the leader, null if so such topic exists.
33 */ 34 */
34 NodeId getLeader(String path); 35 NodeId getLeader(String path);
35 36
36 /** 37 /**
38 + * Returns the current leadership info for the topic.
39 + * @param path topic
40 + * @return leadership info or null if so such topic exists.
41 + */
42 + Leadership getLeadership(String path);
43 +
44 + /**
45 + * Returns the set of topics owned by the specified node.
46 + * @param nodeId node Id.
47 + * @return set of topics for which this node is the current leader.
48 + */
49 + Set<String> ownedTopics(NodeId nodeId);
50 +
51 + /**
37 * Joins the leadership contest. 52 * Joins the leadership contest.
38 * @param path topic for which this controller node wishes to be a leader. 53 * @param path topic for which this controller node wishes to be a leader.
39 */ 54 */
...@@ -45,6 +60,10 @@ public interface LeadershipService { ...@@ -45,6 +60,10 @@ public interface LeadershipService {
45 */ 60 */
46 void withdraw(String path); 61 void withdraw(String path);
47 62
63 + /**
64 + * Returns the current leader board.
65 + * @return mapping from topic to leadership info.
66 + */
48 Map<String, Leadership> getLeaderBoard(); 67 Map<String, Leadership> getLeaderBoard();
49 68
50 /** 69 /**
......
...@@ -46,10 +46,12 @@ import org.slf4j.LoggerFactory; ...@@ -46,10 +46,12 @@ import org.slf4j.LoggerFactory;
46 46
47 import java.util.HashMap; 47 import java.util.HashMap;
48 import java.util.Map; 48 import java.util.Map;
49 +import java.util.Set;
49 import java.util.concurrent.ExecutorService; 50 import java.util.concurrent.ExecutorService;
50 import java.util.concurrent.Executors; 51 import java.util.concurrent.Executors;
51 import java.util.concurrent.Future; 52 import java.util.concurrent.Future;
52 import java.util.concurrent.locks.Lock; 53 import java.util.concurrent.locks.Lock;
54 +import java.util.stream.Collectors;
53 55
54 import static com.google.common.base.Preconditions.checkArgument; 56 import static com.google.common.base.Preconditions.checkArgument;
55 import static org.onlab.util.Tools.groupedThreads; 57 import static org.onlab.util.Tools.groupedThreads;
...@@ -162,6 +164,28 @@ public class HazelcastLeadershipService implements LeadershipService { ...@@ -162,6 +164,28 @@ public class HazelcastLeadershipService implements LeadershipService {
162 } 164 }
163 165
164 @Override 166 @Override
167 + public Leadership getLeadership(String path) {
168 + checkArgument(path != null);
169 + Topic topic = topics.get(path);
170 + if (topic != null) {
171 + return new Leadership(topic.topicName(),
172 + topic.leader(),
173 + topic.term());
174 + }
175 + return null;
176 + }
177 +
178 + @Override
179 + public Set<String> ownedTopics(NodeId nodeId) {
180 + checkArgument(nodeId != null);
181 + return topics.values()
182 + .stream()
183 + .filter(topic -> nodeId.equals(topic.leader()))
184 + .map(topic -> topic.topicName)
185 + .collect(Collectors.toSet());
186 + }
187 +
188 + @Override
165 public void runForLeadership(String path) { 189 public void runForLeadership(String path) {
166 checkArgument(path != null); 190 checkArgument(path != null);
167 Topic topic = new Topic(path); 191 Topic topic = new Topic(path);
......
1 -/*
2 - * Copyright 2014 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -package org.onosproject.store.cluster.impl;
17 -
18 -import com.google.common.collect.ImmutableMap;
19 -import com.google.common.collect.Maps;
20 -import com.google.common.collect.Sets;
21 -
22 -import org.apache.felix.scr.annotations.Activate;
23 -import org.apache.felix.scr.annotations.Component;
24 -import org.apache.felix.scr.annotations.Deactivate;
25 -import org.apache.felix.scr.annotations.Reference;
26 -import org.apache.felix.scr.annotations.ReferenceCardinality;
27 -import org.apache.felix.scr.annotations.Service;
28 -import org.onlab.util.KryoNamespace;
29 -import org.onosproject.cluster.ClusterService;
30 -import org.onosproject.cluster.Leadership;
31 -import org.onosproject.cluster.LeadershipEvent;
32 -import org.onosproject.cluster.LeadershipEventListener;
33 -import org.onosproject.cluster.LeadershipService;
34 -import org.onosproject.cluster.NodeId;
35 -import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
36 -import org.onosproject.store.cluster.messaging.ClusterMessage;
37 -import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
38 -import org.onosproject.store.cluster.messaging.MessageSubject;
39 -import org.onosproject.store.serializers.KryoNamespaces;
40 -import org.onosproject.store.serializers.KryoSerializer;
41 -import org.onosproject.store.service.Lock;
42 -import org.onosproject.store.service.LockService;
43 -import org.slf4j.Logger;
44 -
45 -import java.util.Map;
46 -import java.util.Set;
47 -import java.util.concurrent.ExecutorService;
48 -import java.util.concurrent.Executors;
49 -import java.util.concurrent.ScheduledExecutorService;
50 -import java.util.concurrent.TimeUnit;
51 -
52 -import static com.google.common.base.Preconditions.checkArgument;
53 -import static org.onlab.util.Tools.groupedThreads;
54 -import static org.slf4j.LoggerFactory.getLogger;
55 -
56 -/**
57 - * Distributed implementation of LeadershipService that is based on the primitives exposed by
58 - * LockService.
59 - */
60 -@Component(enabled = false)
61 -@Service
62 -public class LeadershipManager implements LeadershipService {
63 -
64 - private final Logger log = getLogger(getClass());
65 -
66 - private static final int TERM_DURATION_MS = 2000;
67 -
68 - // Time to wait before retrying leadership after
69 - // a unexpected error.
70 - private static final int WAIT_BEFORE_RETRY_MS = 2000;
71 -
72 - // TODO: Make Thread pool size configurable.
73 - private final ScheduledExecutorService threadPool =
74 - Executors.newScheduledThreadPool(25, groupedThreads("onos/leadership", "manager-%d"));
75 -
76 - private static final MessageSubject LEADERSHIP_UPDATES =
77 - new MessageSubject("leadership-contest-updates");
78 -
79 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 - private ClusterService clusterService;
81 -
82 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 - private ClusterCommunicationService clusterCommunicator;
84 -
85 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 - private LockService lockService;
87 -
88 - private final Map<String, Leadership> leaderBoard = Maps.newHashMap();
89 -
90 - private final Map<String, Lock> openContests = Maps.newConcurrentMap();
91 - private final Set<LeadershipEventListener> listeners = Sets.newIdentityHashSet();
92 - private NodeId localNodeId;
93 -
94 - private final LeadershipEventListener peerAdvertiser = new PeerAdvertiser();
95 - private final LeadershipEventListener leaderBoardUpdater = new LeaderBoardUpdater();
96 -
97 - private ExecutorService messageHandlingExecutor;
98 -
99 - public static final KryoSerializer SERIALIZER = new KryoSerializer() {
100 - @Override
101 - protected void setupKryoPool() {
102 - serializerPool = KryoNamespace.newBuilder()
103 - .register(KryoNamespaces.API)
104 - .build()
105 - .populate(1);
106 - }
107 - };
108 -
109 - @Activate
110 - public void activate() {
111 - localNodeId = clusterService.getLocalNode().id();
112 -
113 - addListener(peerAdvertiser);
114 - addListener(leaderBoardUpdater);
115 -
116 - messageHandlingExecutor = Executors.newSingleThreadExecutor(
117 - groupedThreads("onos/store/leadership",
118 - "peer-advertisement-handler"));
119 -
120 - clusterCommunicator.addSubscriber(
121 - LEADERSHIP_UPDATES,
122 - new PeerAdvertisementHandler(),
123 - messageHandlingExecutor);
124 -
125 - log.info("Started.");
126 - }
127 -
128 - @Deactivate
129 - public void deactivate() {
130 - removeListener(peerAdvertiser);
131 - removeListener(leaderBoardUpdater);
132 -
133 - clusterCommunicator.removeSubscriber(LEADERSHIP_UPDATES);
134 -
135 - messageHandlingExecutor.shutdown();
136 - threadPool.shutdown();
137 -
138 - log.info("Stopped.");
139 - }
140 -
141 -
142 - @Override
143 - public NodeId getLeader(String path) {
144 - synchronized (leaderBoard) {
145 - Leadership leadership = leaderBoard.get(path);
146 - if (leadership != null) {
147 - return leadership.leader();
148 - }
149 - }
150 - return null;
151 - }
152 -
153 - @Override
154 - public void runForLeadership(String path) {
155 - checkArgument(path != null);
156 -
157 - if (openContests.containsKey(path)) {
158 - log.info("Already in the leadership contest for {}", path);
159 - return;
160 - } else {
161 - Lock lock = lockService.create(path);
162 - openContests.put(path, lock);
163 - threadPool.schedule(new TryLeadership(lock), 0, TimeUnit.MILLISECONDS);
164 - }
165 - }
166 -
167 - @Override
168 - public void withdraw(String path) {
169 - checkArgument(path != null);
170 -
171 - Lock lock = openContests.remove(path);
172 -
173 - if (lock != null && lock.isLocked()) {
174 - lock.unlock();
175 - notifyListeners(
176 - new LeadershipEvent(
177 - LeadershipEvent.Type.LEADER_BOOTED,
178 - new Leadership(lock.path(), localNodeId, lock.epoch())));
179 - }
180 - }
181 -
182 - @Override
183 - public Map<String, Leadership> getLeaderBoard() {
184 - return ImmutableMap.copyOf(leaderBoard);
185 - }
186 -
187 - @Override
188 - public void addListener(LeadershipEventListener listener) {
189 - checkArgument(listener != null);
190 - listeners.add(listener);
191 - }
192 -
193 - @Override
194 - public void removeListener(LeadershipEventListener listener) {
195 - checkArgument(listener != null);
196 - listeners.remove(listener);
197 - }
198 -
199 - private void notifyListeners(LeadershipEvent event) {
200 - for (LeadershipEventListener listener : listeners) {
201 - try {
202 - listener.event(event);
203 - } catch (Exception e) {
204 - log.error("Notifying listener failed with exception.", e);
205 - }
206 - }
207 - }
208 -
209 - private void tryAcquireLeadership(String path) {
210 - Lock lock = openContests.get(path);
211 - if (lock == null) {
212 - // withdrew from race.
213 - return;
214 - }
215 - lock.lockAsync(TERM_DURATION_MS).whenComplete((response, error) -> {
216 - if (error == null) {
217 - threadPool.schedule(
218 - new ReelectionTask(lock),
219 - TERM_DURATION_MS / 2,
220 - TimeUnit.MILLISECONDS);
221 - notifyListeners(
222 - new LeadershipEvent(
223 - LeadershipEvent.Type.LEADER_ELECTED,
224 - new Leadership(lock.path(), localNodeId, lock.epoch())));
225 - return;
226 - } else {
227 - log.warn("Failed to acquire lock for {}. Will retry in {} ms", path, WAIT_BEFORE_RETRY_MS, error);
228 - threadPool.schedule(new TryLeadership(lock), WAIT_BEFORE_RETRY_MS, TimeUnit.MILLISECONDS);
229 - }
230 - });
231 - }
232 -
233 - private class ReelectionTask implements Runnable {
234 -
235 - private final Lock lock;
236 -
237 - public ReelectionTask(Lock lock) {
238 - this.lock = lock;
239 - }
240 -
241 - @Override
242 - public void run() {
243 - if (!openContests.containsKey(lock.path())) {
244 - log.debug("Node withdrew from leadership race for {}. Cancelling reelection task.", lock.path());
245 - return;
246 - }
247 -
248 - boolean lockExtended = false;
249 - try {
250 - lockExtended = lock.extendExpiration(TERM_DURATION_MS);
251 - } catch (Exception e) {
252 - log.warn("Attempt to extend lock failed with an exception.", e);
253 - }
254 -
255 - if (lockExtended) {
256 - notifyListeners(
257 - new LeadershipEvent(
258 - LeadershipEvent.Type.LEADER_REELECTED,
259 - new Leadership(lock.path(), localNodeId, lock.epoch())));
260 - threadPool.schedule(this, TERM_DURATION_MS / 2, TimeUnit.MILLISECONDS);
261 - } else {
262 - // Check if this node already withdrew from the contest, in which case
263 - // we don't need to notify here.
264 - if (openContests.containsKey(lock.path())) {
265 - notifyListeners(
266 - new LeadershipEvent(
267 - LeadershipEvent.Type.LEADER_BOOTED,
268 - new Leadership(lock.path(), localNodeId, lock.epoch())));
269 - // Retry leadership after a brief wait.
270 - threadPool.schedule(new TryLeadership(lock), WAIT_BEFORE_RETRY_MS, TimeUnit.MILLISECONDS);
271 - }
272 - }
273 - }
274 - }
275 -
276 - private class TryLeadership implements Runnable {
277 - private final Lock lock;
278 -
279 - public TryLeadership(Lock lock) {
280 - this.lock = lock;
281 - }
282 -
283 - @Override
284 - public void run() {
285 - tryAcquireLeadership(lock.path());
286 - }
287 - }
288 -
289 - private class PeerAdvertiser implements LeadershipEventListener {
290 - @Override
291 - public void event(LeadershipEvent event) {
292 - // publish events originating on this host.
293 - if (event.subject().leader().equals(localNodeId)) {
294 - clusterCommunicator.broadcast(
295 - new ClusterMessage(
296 - localNodeId,
297 - LEADERSHIP_UPDATES,
298 - SERIALIZER.encode(event)));
299 - }
300 - }
301 - }
302 -
303 - private class PeerAdvertisementHandler implements ClusterMessageHandler {
304 - @Override
305 - public void handle(ClusterMessage message) {
306 - LeadershipEvent event = SERIALIZER.decode(message.payload());
307 - log.trace("Received {} from {}", event, message.sender());
308 - notifyListeners(event);
309 - }
310 - }
311 -
312 - private class LeaderBoardUpdater implements LeadershipEventListener {
313 - @Override
314 - public void event(LeadershipEvent event) {
315 - Leadership leadershipUpdate = event.subject();
316 - synchronized (leaderBoard) {
317 - Leadership currentLeadership = leaderBoard.get(leadershipUpdate.topic());
318 - switch (event.type()) {
319 - case LEADER_ELECTED:
320 - case LEADER_REELECTED:
321 - if (currentLeadership == null || currentLeadership.epoch() < leadershipUpdate.epoch()) {
322 - leaderBoard.put(leadershipUpdate.topic(), leadershipUpdate);
323 - }
324 - break;
325 - case LEADER_BOOTED:
326 - if (currentLeadership != null && currentLeadership.epoch() <= leadershipUpdate.epoch()) {
327 - leaderBoard.remove(leadershipUpdate.topic());
328 - }
329 - break;
330 - default:
331 - break;
332 - }
333 - }
334 - }
335 - }
336 -}
...@@ -2,6 +2,7 @@ package org.onosproject.store.consistent.impl; ...@@ -2,6 +2,7 @@ package org.onosproject.store.consistent.impl;
2 2
3 import static org.onlab.util.Tools.groupedThreads; 3 import static org.onlab.util.Tools.groupedThreads;
4 import static org.slf4j.LoggerFactory.getLogger; 4 import static org.slf4j.LoggerFactory.getLogger;
5 +import static com.google.common.base.Preconditions.checkArgument;
5 6
6 import java.util.Map; 7 import java.util.Map;
7 import java.util.Map.Entry; 8 import java.util.Map.Entry;
...@@ -10,6 +11,7 @@ import java.util.concurrent.ExecutorService; ...@@ -10,6 +11,7 @@ import java.util.concurrent.ExecutorService;
10 import java.util.concurrent.Executors; 11 import java.util.concurrent.Executors;
11 import java.util.concurrent.ScheduledExecutorService; 12 import java.util.concurrent.ScheduledExecutorService;
12 import java.util.concurrent.TimeUnit; 13 import java.util.concurrent.TimeUnit;
14 +import java.util.stream.Collectors;
13 15
14 import org.apache.felix.scr.annotations.Activate; 16 import org.apache.felix.scr.annotations.Activate;
15 import org.apache.felix.scr.annotations.Component; 17 import org.apache.felix.scr.annotations.Component;
...@@ -171,6 +173,22 @@ public class DistributedLeadershipManager implements LeadershipService { ...@@ -171,6 +173,22 @@ public class DistributedLeadershipManager implements LeadershipService {
171 } 173 }
172 174
173 @Override 175 @Override
176 + public Leadership getLeadership(String path) {
177 + checkArgument(path != null);
178 + return leaderBoard.get(path);
179 + }
180 +
181 + @Override
182 + public Set<String> ownedTopics(NodeId nodeId) {
183 + checkArgument(nodeId != null);
184 + return leaderBoard.entrySet()
185 + .stream()
186 + .filter(entry -> nodeId.equals(entry.getValue().leader()))
187 + .map(Entry::getKey)
188 + .collect(Collectors.toSet());
189 + }
190 +
191 + @Override
174 public void runForLeadership(String path) { 192 public void runForLeadership(String path) {
175 log.info("Running for leadership for topic: {}", path); 193 log.info("Running for leadership for topic: {}", path);
176 activeTopics.add(path); 194 activeTopics.add(path);
......
...@@ -15,10 +15,14 @@ ...@@ -15,10 +15,14 @@
15 */ 15 */
16 package org.onosproject.store.trivial.impl; 16 package org.onosproject.store.trivial.impl;
17 17
18 +import static com.google.common.base.Preconditions.checkArgument;
19 +
18 import java.util.Map; 20 import java.util.Map;
21 +import java.util.Map.Entry;
19 import java.util.Set; 22 import java.util.Set;
20 import java.util.concurrent.ConcurrentHashMap; 23 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.CopyOnWriteArraySet; 24 import java.util.concurrent.CopyOnWriteArraySet;
25 +import java.util.stream.Collectors;
22 26
23 import org.apache.felix.scr.annotations.Component; 27 import org.apache.felix.scr.annotations.Component;
24 import org.apache.felix.scr.annotations.Reference; 28 import org.apache.felix.scr.annotations.Reference;
...@@ -55,6 +59,22 @@ public class SimpleLeadershipManager implements LeadershipService { ...@@ -55,6 +59,22 @@ public class SimpleLeadershipManager implements LeadershipService {
55 } 59 }
56 60
57 @Override 61 @Override
62 + public Leadership getLeadership(String path) {
63 + checkArgument(path != null);
64 + return elections.get(path) ? new Leadership(path, clusterService.getLocalNode().id(), 0) : null;
65 + }
66 +
67 + @Override
68 + public Set<String> ownedTopics(NodeId nodeId) {
69 + checkArgument(nodeId != null);
70 + return elections.entrySet()
71 + .stream()
72 + .filter(Entry::getValue)
73 + .map(Entry::getKey)
74 + .collect(Collectors.toSet());
75 + }
76 +
77 + @Override
58 public void runForLeadership(String path) { 78 public void runForLeadership(String path) {
59 elections.put(path, true); 79 elections.put(path, true);
60 for (LeadershipEventListener listener : listeners) { 80 for (LeadershipEventListener listener : listeners) {
...@@ -88,5 +108,4 @@ public class SimpleLeadershipManager implements LeadershipService { ...@@ -88,5 +108,4 @@ public class SimpleLeadershipManager implements LeadershipService {
88 public void removeListener(LeadershipEventListener listener) { 108 public void removeListener(LeadershipEventListener listener) {
89 listeners.remove(listener); 109 listeners.remove(listener);
90 } 110 }
91 -
92 } 111 }
......