Updated the HazelcastLeadershipService implemenation:
* Every listener receives all leadership events, even for new topics and topics the local instance is not running for a leadership election * Now getLeaderBoard() returns all leadership info Change-Id: Ia11a10ed287d2f8d905dd987beb8052c35be6cf1
Showing
1 changed file
with
79 additions
and
46 deletions
| ... | @@ -71,7 +71,8 @@ import com.hazelcast.core.MessageListener; | ... | @@ -71,7 +71,8 @@ import com.hazelcast.core.MessageListener; |
| 71 | */ | 71 | */ |
| 72 | @Component(immediate = true) | 72 | @Component(immediate = true) |
| 73 | @Service | 73 | @Service |
| 74 | -public class HazelcastLeadershipService implements LeadershipService { | 74 | +public class HazelcastLeadershipService implements LeadershipService, |
| 75 | + MessageListener<byte[]> { | ||
| 75 | private static final Logger log = | 76 | private static final Logger log = |
| 76 | LoggerFactory.getLogger(HazelcastLeadershipService.class); | 77 | LoggerFactory.getLogger(HazelcastLeadershipService.class); |
| 77 | 78 | ||
| ... | @@ -87,6 +88,7 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -87,6 +88,7 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 87 | 88 | ||
| 88 | private static final long LEADERSHIP_PERIODIC_INTERVAL_MS = 5 * 1000; // 5s | 89 | private static final long LEADERSHIP_PERIODIC_INTERVAL_MS = 5 * 1000; // 5s |
| 89 | private static final long LEADERSHIP_REMOTE_TIMEOUT_MS = 15 * 1000; // 15s | 90 | private static final long LEADERSHIP_REMOTE_TIMEOUT_MS = 15 * 1000; // 15s |
| 91 | + private static final String TOPIC_HZ_ID = "LeadershipService/AllTopics"; | ||
| 90 | 92 | ||
| 91 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 93 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 92 | protected ClusterService clusterService; | 94 | protected ClusterService clusterService; |
| ... | @@ -102,18 +104,29 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -102,18 +104,29 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 102 | private final Map<String, Topic> topics = Maps.newConcurrentMap(); | 104 | private final Map<String, Topic> topics = Maps.newConcurrentMap(); |
| 103 | private NodeId localNodeId; | 105 | private NodeId localNodeId; |
| 104 | 106 | ||
| 107 | + private ITopic<byte[]> leaderTopic; | ||
| 108 | + private String leaderTopicRegistrationId; | ||
| 109 | + | ||
| 105 | @Activate | 110 | @Activate |
| 106 | protected void activate() { | 111 | protected void activate() { |
| 107 | localNodeId = clusterService.getLocalNode().id(); | 112 | localNodeId = clusterService.getLocalNode().id(); |
| 108 | listenerRegistry = new AbstractListenerRegistry<>(); | 113 | listenerRegistry = new AbstractListenerRegistry<>(); |
| 109 | eventDispatcher.addSink(LeadershipEvent.class, listenerRegistry); | 114 | eventDispatcher.addSink(LeadershipEvent.class, listenerRegistry); |
| 110 | 115 | ||
| 116 | + TopicConfig topicConfig = new TopicConfig(); | ||
| 117 | + topicConfig.setGlobalOrderingEnabled(true); | ||
| 118 | + topicConfig.setName(TOPIC_HZ_ID); | ||
| 119 | + storeService.getHazelcastInstance().getConfig().addTopicConfig(topicConfig); | ||
| 120 | + leaderTopic = storeService.getHazelcastInstance().getTopic(TOPIC_HZ_ID); | ||
| 121 | + leaderTopicRegistrationId = leaderTopic.addMessageListener(this); | ||
| 122 | + | ||
| 111 | log.info("Hazelcast Leadership Service started"); | 123 | log.info("Hazelcast Leadership Service started"); |
| 112 | } | 124 | } |
| 113 | 125 | ||
| 114 | @Deactivate | 126 | @Deactivate |
| 115 | protected void deactivate() { | 127 | protected void deactivate() { |
| 116 | eventDispatcher.removeSink(LeadershipEvent.class); | 128 | eventDispatcher.removeSink(LeadershipEvent.class); |
| 129 | + leaderTopic.removeMessageListener(leaderTopicRegistrationId); | ||
| 117 | 130 | ||
| 118 | for (Topic topic : topics.values()) { | 131 | for (Topic topic : topics.values()) { |
| 119 | topic.stop(); | 132 | topic.stop(); |
| ... | @@ -139,6 +152,9 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -139,6 +152,9 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 139 | Topic oldTopic = topics.putIfAbsent(path, topic); | 152 | Topic oldTopic = topics.putIfAbsent(path, topic); |
| 140 | if (oldTopic == null) { | 153 | if (oldTopic == null) { |
| 141 | topic.start(); | 154 | topic.start(); |
| 155 | + topic.runForLeadership(); | ||
| 156 | + } else { | ||
| 157 | + oldTopic.runForLeadership(); | ||
| 142 | } | 158 | } |
| 143 | } | 159 | } |
| 144 | 160 | ||
| ... | @@ -156,11 +172,6 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -156,11 +172,6 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 156 | public Map<String, Leadership> getLeaderBoard() { | 172 | public Map<String, Leadership> getLeaderBoard() { |
| 157 | Map<String, Leadership> result = new HashMap<>(); | 173 | Map<String, Leadership> result = new HashMap<>(); |
| 158 | 174 | ||
| 159 | - // | ||
| 160 | - // Get the leaders for the topics. | ||
| 161 | - // NOTE: A topic is listed only if this instance is running for | ||
| 162 | - // a leadership for that topic. | ||
| 163 | - // | ||
| 164 | for (Topic topic : topics.values()) { | 175 | for (Topic topic : topics.values()) { |
| 165 | Leadership leadership = new Leadership(topic.topicName(), | 176 | Leadership leadership = new Leadership(topic.topicName(), |
| 166 | topic.leader(), | 177 | topic.leader(), |
| ... | @@ -180,12 +191,41 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -180,12 +191,41 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 180 | listenerRegistry.removeListener(listener); | 191 | listenerRegistry.removeListener(listener); |
| 181 | } | 192 | } |
| 182 | 193 | ||
| 194 | + @Override | ||
| 195 | + public void onMessage(Message<byte[]> message) { | ||
| 196 | + LeadershipEvent leadershipEvent = | ||
| 197 | + SERIALIZER.decode(message.getMessageObject()); | ||
| 198 | + | ||
| 199 | + log.debug("Leadership Event: time = {} type = {} event = {}", | ||
| 200 | + leadershipEvent.time(), leadershipEvent.type(), | ||
| 201 | + leadershipEvent); | ||
| 202 | + | ||
| 203 | + // | ||
| 204 | + // If there is no entry for the topic, then create a new one to | ||
| 205 | + // keep track of the leadership, but don't run for leadership itself. | ||
| 206 | + // | ||
| 207 | + String topicName = leadershipEvent.subject().topic(); | ||
| 208 | + Topic topic = topics.get(topicName); | ||
| 209 | + if (topic == null) { | ||
| 210 | + topic = new Topic(topicName); | ||
| 211 | + Topic oldTopic = topics.putIfAbsent(topicName, topic); | ||
| 212 | + if (oldTopic == null) { | ||
| 213 | + topic.start(); | ||
| 214 | + } else { | ||
| 215 | + topic = oldTopic; | ||
| 216 | + } | ||
| 217 | + } | ||
| 218 | + topic.receivedLeadershipEvent(leadershipEvent); | ||
| 219 | + eventDispatcher.post(leadershipEvent); | ||
| 220 | + } | ||
| 221 | + | ||
| 183 | /** | 222 | /** |
| 184 | * Class for keeping per-topic information. | 223 | * Class for keeping per-topic information. |
| 185 | */ | 224 | */ |
| 186 | - private final class Topic implements MessageListener<byte[]> { | 225 | + private final class Topic { |
| 187 | private final String topicName; | 226 | private final String topicName; |
| 188 | private volatile boolean isShutdown = true; | 227 | private volatile boolean isShutdown = true; |
| 228 | + private volatile boolean isRunningForLeadership = false; | ||
| 189 | private volatile long lastLeadershipUpdateMs = 0; | 229 | private volatile long lastLeadershipUpdateMs = 0; |
| 190 | private ExecutorService leaderElectionExecutor; | 230 | private ExecutorService leaderElectionExecutor; |
| 191 | 231 | ||
| ... | @@ -193,8 +233,6 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -193,8 +233,6 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 193 | private Lock leaderLock; | 233 | private Lock leaderLock; |
| 194 | private Future<?> getLockFuture; | 234 | private Future<?> getLockFuture; |
| 195 | private Future<?> periodicProcessingFuture; | 235 | private Future<?> periodicProcessingFuture; |
| 196 | - private ITopic<byte[]> leaderTopic; | ||
| 197 | - private String leaderTopicRegistrationId; | ||
| 198 | 236 | ||
| 199 | /** | 237 | /** |
| 200 | * Constructor. | 238 | * Constructor. |
| ... | @@ -224,38 +262,39 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -224,38 +262,39 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 224 | } | 262 | } |
| 225 | 263 | ||
| 226 | /** | 264 | /** |
| 227 | - * Starts leadership election for the topic. | 265 | + * Starts operation. |
| 228 | */ | 266 | */ |
| 229 | private void start() { | 267 | private void start() { |
| 230 | isShutdown = false; | 268 | isShutdown = false; |
| 231 | - String lockHzId = "LeadershipService/" + topicName + "/lock"; | ||
| 232 | - String topicHzId = "LeadershipService/" + topicName + "/topic"; | ||
| 233 | - leaderLock = storeService.getHazelcastInstance().getLock(lockHzId); | ||
| 234 | - | ||
| 235 | String threadPoolName = "leader-election-" + topicName + "-%d"; | 269 | String threadPoolName = "leader-election-" + topicName + "-%d"; |
| 236 | leaderElectionExecutor = Executors.newScheduledThreadPool(2, | 270 | leaderElectionExecutor = Executors.newScheduledThreadPool(2, |
| 237 | namedThreads(threadPoolName)); | 271 | namedThreads(threadPoolName)); |
| 238 | 272 | ||
| 239 | - TopicConfig topicConfig = new TopicConfig(); | 273 | + periodicProcessingFuture = |
| 240 | - topicConfig.setGlobalOrderingEnabled(true); | 274 | + leaderElectionExecutor.submit(new Runnable() { |
| 241 | - topicConfig.setName(topicHzId); | ||
| 242 | - storeService.getHazelcastInstance().getConfig().addTopicConfig(topicConfig); | ||
| 243 | - | ||
| 244 | - leaderTopic = | ||
| 245 | - storeService.getHazelcastInstance().getTopic(topicHzId); | ||
| 246 | - leaderTopicRegistrationId = leaderTopic.addMessageListener(this); | ||
| 247 | - | ||
| 248 | - getLockFuture = leaderElectionExecutor.submit(new Runnable() { | ||
| 249 | @Override | 275 | @Override |
| 250 | public void run() { | 276 | public void run() { |
| 251 | - doLeaderElectionThread(); | 277 | + doPeriodicProcessing(); |
| 252 | } | 278 | } |
| 253 | }); | 279 | }); |
| 254 | - periodicProcessingFuture = | 280 | + } |
| 255 | - leaderElectionExecutor.submit(new Runnable() { | 281 | + |
| 282 | + /** | ||
| 283 | + * Runs for leadership. | ||
| 284 | + */ | ||
| 285 | + private void runForLeadership() { | ||
| 286 | + if (isRunningForLeadership) { | ||
| 287 | + return; // Nothing to do: already running | ||
| 288 | + } | ||
| 289 | + if (isShutdown) { | ||
| 290 | + start(); | ||
| 291 | + } | ||
| 292 | + String lockHzId = "LeadershipService/" + topicName + "/lock"; | ||
| 293 | + leaderLock = storeService.getHazelcastInstance().getLock(lockHzId); | ||
| 294 | + getLockFuture = leaderElectionExecutor.submit(new Runnable() { | ||
| 256 | @Override | 295 | @Override |
| 257 | public void run() { | 296 | public void run() { |
| 258 | - doPeriodicProcessing(); | 297 | + doLeaderElectionThread(); |
| 259 | } | 298 | } |
| 260 | }); | 299 | }); |
| 261 | } | 300 | } |
| ... | @@ -265,21 +304,19 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -265,21 +304,19 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 265 | */ | 304 | */ |
| 266 | private void stop() { | 305 | private void stop() { |
| 267 | isShutdown = true; | 306 | isShutdown = true; |
| 268 | - leaderTopic.removeMessageListener(leaderTopicRegistrationId); | 307 | + isRunningForLeadership = false; |
| 269 | // getLockFuture.cancel(true); | 308 | // getLockFuture.cancel(true); |
| 270 | // periodicProcessingFuture.cancel(true); | 309 | // periodicProcessingFuture.cancel(true); |
| 271 | leaderElectionExecutor.shutdownNow(); | 310 | leaderElectionExecutor.shutdownNow(); |
| 272 | } | 311 | } |
| 273 | 312 | ||
| 274 | - @Override | 313 | + /** |
| 275 | - public void onMessage(Message<byte[]> message) { | 314 | + * Received a Leadership Event. |
| 276 | - LeadershipEvent leadershipEvent = | 315 | + * |
| 277 | - SERIALIZER.decode(message.getMessageObject()); | 316 | + * @param leadershipEvent the received Leadership Event |
| 317 | + */ | ||
| 318 | + private void receivedLeadershipEvent(LeadershipEvent leadershipEvent) { | ||
| 278 | NodeId eventLeaderId = leadershipEvent.subject().leader(); | 319 | NodeId eventLeaderId = leadershipEvent.subject().leader(); |
| 279 | - | ||
| 280 | - log.debug("Leadership Event: time = {} type = {} event = {}", | ||
| 281 | - leadershipEvent.time(), leadershipEvent.type(), | ||
| 282 | - leadershipEvent); | ||
| 283 | if (!leadershipEvent.subject().topic().equals(topicName)) { | 320 | if (!leadershipEvent.subject().topic().equals(topicName)) { |
| 284 | return; // Not our topic: ignore | 321 | return; // Not our topic: ignore |
| 285 | } | 322 | } |
| ... | @@ -296,23 +333,21 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -296,23 +333,21 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 296 | // Another leader: if we are also a leader, then give up | 333 | // Another leader: if we are also a leader, then give up |
| 297 | // leadership and run for re-election. | 334 | // leadership and run for re-election. |
| 298 | // | 335 | // |
| 299 | - if ((leader != null) && | 336 | + if ((leader != null) && leader.equals(localNodeId)) { |
| 300 | - leader.equals(localNodeId)) { | 337 | + if (getLockFuture != null) { |
| 301 | getLockFuture.cancel(true); | 338 | getLockFuture.cancel(true); |
| 339 | + } | ||
| 302 | } else { | 340 | } else { |
| 303 | // Just update the current leader | 341 | // Just update the current leader |
| 304 | leader = leadershipEvent.subject().leader(); | 342 | leader = leadershipEvent.subject().leader(); |
| 305 | lastLeadershipUpdateMs = System.currentTimeMillis(); | 343 | lastLeadershipUpdateMs = System.currentTimeMillis(); |
| 306 | } | 344 | } |
| 307 | - eventDispatcher.post(leadershipEvent); | ||
| 308 | break; | 345 | break; |
| 309 | case LEADER_BOOTED: | 346 | case LEADER_BOOTED: |
| 310 | // Remove the state for the current leader | 347 | // Remove the state for the current leader |
| 311 | - if ((leader != null) && | 348 | + if ((leader != null) && eventLeaderId.equals(leader)) { |
| 312 | - eventLeaderId.equals(leader)) { | ||
| 313 | leader = null; | 349 | leader = null; |
| 314 | } | 350 | } |
| 315 | - eventDispatcher.post(leadershipEvent); | ||
| 316 | break; | 351 | break; |
| 317 | default: | 352 | default: |
| 318 | break; | 353 | break; |
| ... | @@ -340,7 +375,7 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -340,7 +375,7 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 340 | leadershipEvent = new LeadershipEvent( | 375 | leadershipEvent = new LeadershipEvent( |
| 341 | LeadershipEvent.Type.LEADER_REELECTED, | 376 | LeadershipEvent.Type.LEADER_REELECTED, |
| 342 | new Leadership(topicName, localNodeId, 0)); | 377 | new Leadership(topicName, localNodeId, 0)); |
| 343 | - // Dispatch to all remote instances | 378 | + // Dispatch to all instances |
| 344 | leaderTopic.publish(SERIALIZER.encode(leadershipEvent)); | 379 | leaderTopic.publish(SERIALIZER.encode(leadershipEvent)); |
| 345 | } else { | 380 | } else { |
| 346 | // | 381 | // |
| ... | @@ -404,7 +439,6 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -404,7 +439,6 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 404 | leadershipEvent = new LeadershipEvent( | 439 | leadershipEvent = new LeadershipEvent( |
| 405 | LeadershipEvent.Type.LEADER_ELECTED, | 440 | LeadershipEvent.Type.LEADER_ELECTED, |
| 406 | new Leadership(topicName, localNodeId, 0)); | 441 | new Leadership(topicName, localNodeId, 0)); |
| 407 | - eventDispatcher.post(leadershipEvent); | ||
| 408 | leaderTopic.publish(SERIALIZER.encode(leadershipEvent)); | 442 | leaderTopic.publish(SERIALIZER.encode(leadershipEvent)); |
| 409 | } | 443 | } |
| 410 | 444 | ||
| ... | @@ -430,7 +464,6 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -430,7 +464,6 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 430 | leadershipEvent = new LeadershipEvent( | 464 | leadershipEvent = new LeadershipEvent( |
| 431 | LeadershipEvent.Type.LEADER_BOOTED, | 465 | LeadershipEvent.Type.LEADER_BOOTED, |
| 432 | new Leadership(topicName, localNodeId, 0)); | 466 | new Leadership(topicName, localNodeId, 0)); |
| 433 | - eventDispatcher.post(leadershipEvent); | ||
| 434 | leaderTopic.publish(SERIALIZER.encode(leadershipEvent)); | 467 | leaderTopic.publish(SERIALIZER.encode(leadershipEvent)); |
| 435 | leaderLock.unlock(); | 468 | leaderLock.unlock(); |
| 436 | } | 469 | } | ... | ... |
-
Please register or login to post a comment