Committed by
Pavlin Radoslavov
LeadershipService: Support for a leaderBoard.
Change-Id: I0dd8267e104466ec65a2c67d23d1c4d923cad266 Change-Id: I6bc548510400eacabb12482f8fba1b7f2abb0604
Showing
6 changed files
with
175 additions
and
26 deletions
| ... | @@ -11,12 +11,12 @@ public class Leadership { | ... | @@ -11,12 +11,12 @@ public class Leadership { |
| 11 | 11 | ||
| 12 | private final String topic; | 12 | private final String topic; |
| 13 | private final ControllerNode leader; | 13 | private final ControllerNode leader; |
| 14 | - private final long term; | 14 | + private final long epoch; |
| 15 | 15 | ||
| 16 | - public Leadership(String topic, ControllerNode leader, long term) { | 16 | + public Leadership(String topic, ControllerNode leader, long epoch) { |
| 17 | this.topic = topic; | 17 | this.topic = topic; |
| 18 | this.leader = leader; | 18 | this.leader = leader; |
| 19 | - this.term = term; | 19 | + this.epoch = epoch; |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | /** | 22 | /** |
| ... | @@ -36,16 +36,16 @@ public class Leadership { | ... | @@ -36,16 +36,16 @@ public class Leadership { |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | /** | 38 | /** |
| 39 | - * The term number associated with this leadership. | 39 | + * The epoch when the leadership was assumed. |
| 40 | - * @return leadership term | 40 | + * @return leadership epoch |
| 41 | */ | 41 | */ |
| 42 | - public long term() { | 42 | + public long epoch() { |
| 43 | - return term; | 43 | + return epoch; |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | @Override | 46 | @Override |
| 47 | public int hashCode() { | 47 | public int hashCode() { |
| 48 | - return Objects.hash(topic, leader, term); | 48 | + return Objects.hash(topic, leader, epoch); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | @Override | 51 | @Override |
| ... | @@ -53,7 +53,7 @@ public class Leadership { | ... | @@ -53,7 +53,7 @@ public class Leadership { |
| 53 | return MoreObjects.toStringHelper(this.getClass()) | 53 | return MoreObjects.toStringHelper(this.getClass()) |
| 54 | .add("topic", topic) | 54 | .add("topic", topic) |
| 55 | .add("leader", leader) | 55 | .add("leader", leader) |
| 56 | - .add("term", term) | 56 | + .add("epoch", epoch) |
| 57 | .toString(); | 57 | .toString(); |
| 58 | } | 58 | } |
| 59 | } | 59 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -17,14 +17,21 @@ package org.onlab.onos.cluster; | ... | @@ -17,14 +17,21 @@ package org.onlab.onos.cluster; |
| 17 | 17 | ||
| 18 | /** | 18 | /** |
| 19 | * Service for leader election. | 19 | * Service for leader election. |
| 20 | - * Leadership contents are organized around topics. ONOS instance can join the | 20 | + * Leadership contests are organized around topics. A instance can join the |
| 21 | - * leadership race for a topic or withdraw from a race it has previously joined | 21 | + * leadership race for a topic or withdraw from a race it has previously joined. |
| 22 | - * Once in the race, the instance can get asynchronously notified | 22 | + * Listeners can be added to receive notifications asynchronously for various |
| 23 | - * of leadership election results. | 23 | + * leadership contests. |
| 24 | */ | 24 | */ |
| 25 | public interface LeadershipService { | 25 | public interface LeadershipService { |
| 26 | 26 | ||
| 27 | /** | 27 | /** |
| 28 | + * Gets the most recent leader for the topic. | ||
| 29 | + * @param path topic | ||
| 30 | + * @return node who is the leader, null if so such topic exists. | ||
| 31 | + */ | ||
| 32 | + ControllerNode getLeader(String path); | ||
| 33 | + | ||
| 34 | + /** | ||
| 28 | * Joins the leadership contest. | 35 | * Joins the leadership contest. |
| 29 | * @param path topic for which this controller node wishes to be a leader. | 36 | * @param path topic for which this controller node wishes to be a leader. |
| 30 | */ | 37 | */ | ... | ... |
| ... | @@ -76,6 +76,15 @@ public interface Lock { | ... | @@ -76,6 +76,15 @@ public interface Lock { |
| 76 | boolean isLocked(); | 76 | boolean isLocked(); |
| 77 | 77 | ||
| 78 | /** | 78 | /** |
| 79 | + * Returns the epoch for this lock. | ||
| 80 | + * If this lock is currently locked i.e. isLocked() returns true, epoch signifies the logical time | ||
| 81 | + * when the lock was acquired. The concept of epoch lets one come up with a global ordering for all | ||
| 82 | + * lock acquisition events | ||
| 83 | + * @return epoch | ||
| 84 | + */ | ||
| 85 | + long epoch(); | ||
| 86 | + | ||
| 87 | + /** | ||
| 79 | * Releases the lock. | 88 | * Releases the lock. |
| 80 | */ | 89 | */ |
| 81 | void unlock(); | 90 | void unlock(); | ... | ... |
| ... | @@ -5,6 +5,7 @@ import static com.google.common.base.Verify.verifyNotNull; | ... | @@ -5,6 +5,7 @@ import static com.google.common.base.Verify.verifyNotNull; |
| 5 | import static org.onlab.util.Tools.namedThreads; | 5 | import static org.onlab.util.Tools.namedThreads; |
| 6 | import static org.slf4j.LoggerFactory.getLogger; | 6 | import static org.slf4j.LoggerFactory.getLogger; |
| 7 | 7 | ||
| 8 | +import java.io.IOException; | ||
| 8 | import java.util.Map; | 9 | import java.util.Map; |
| 9 | import java.util.Set; | 10 | import java.util.Set; |
| 10 | import java.util.concurrent.Executors; | 11 | import java.util.concurrent.Executors; |
| ... | @@ -23,9 +24,16 @@ import org.onlab.onos.cluster.Leadership; | ... | @@ -23,9 +24,16 @@ import org.onlab.onos.cluster.Leadership; |
| 23 | import org.onlab.onos.cluster.LeadershipEvent; | 24 | import org.onlab.onos.cluster.LeadershipEvent; |
| 24 | import org.onlab.onos.cluster.LeadershipEventListener; | 25 | import org.onlab.onos.cluster.LeadershipEventListener; |
| 25 | import org.onlab.onos.cluster.LeadershipService; | 26 | import org.onlab.onos.cluster.LeadershipService; |
| 27 | +import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; | ||
| 28 | +import org.onlab.onos.store.cluster.messaging.ClusterMessage; | ||
| 29 | +import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; | ||
| 30 | +import org.onlab.onos.store.cluster.messaging.MessageSubject; | ||
| 31 | +import org.onlab.onos.store.serializers.KryoNamespaces; | ||
| 32 | +import org.onlab.onos.store.serializers.KryoSerializer; | ||
| 26 | import org.onlab.onos.store.service.Lock; | 33 | import org.onlab.onos.store.service.Lock; |
| 27 | import org.onlab.onos.store.service.LockService; | 34 | import org.onlab.onos.store.service.LockService; |
| 28 | import org.onlab.onos.store.service.impl.DistributedLockManager; | 35 | import org.onlab.onos.store.service.impl.DistributedLockManager; |
| 36 | +import org.onlab.util.KryoNamespace; | ||
| 29 | import org.slf4j.Logger; | 37 | import org.slf4j.Logger; |
| 30 | 38 | ||
| 31 | import com.google.common.collect.Maps; | 39 | import com.google.common.collect.Maps; |
| ... | @@ -45,32 +53,83 @@ public class LeadershipManager implements LeadershipService { | ... | @@ -45,32 +53,83 @@ public class LeadershipManager implements LeadershipService { |
| 45 | private static final int TERM_DURATION_MS = | 53 | private static final int TERM_DURATION_MS = |
| 46 | DistributedLockManager.DEAD_LOCK_TIMEOUT_MS; | 54 | DistributedLockManager.DEAD_LOCK_TIMEOUT_MS; |
| 47 | 55 | ||
| 56 | + // Time to wait before retrying leadership after | ||
| 57 | + // a unexpected error. | ||
| 58 | + private static final int WAIT_BEFORE_RETRY_MS = 2000; | ||
| 59 | + | ||
| 48 | // TODO: Appropriate Thread pool sizing. | 60 | // TODO: Appropriate Thread pool sizing. |
| 49 | private static final ScheduledExecutorService THREAD_POOL = | 61 | private static final ScheduledExecutorService THREAD_POOL = |
| 50 | Executors.newScheduledThreadPool(25, namedThreads("leadership-manager-%d")); | 62 | Executors.newScheduledThreadPool(25, namedThreads("leadership-manager-%d")); |
| 51 | 63 | ||
| 64 | + private static final MessageSubject LEADERSHIP_UPDATES = | ||
| 65 | + new MessageSubject("leadership-contest-updates"); | ||
| 66 | + | ||
| 52 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 67 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 53 | private ClusterService clusterService; | 68 | private ClusterService clusterService; |
| 54 | 69 | ||
| 55 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 70 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 71 | + private ClusterCommunicationService clusterCommunicator; | ||
| 72 | + | ||
| 73 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 56 | private LockService lockService; | 74 | private LockService lockService; |
| 57 | 75 | ||
| 58 | - private Map<String, Lock> openContests = Maps.newHashMap(); | 76 | + private final Map<String, Leadership> leaderBoard = Maps.newHashMap(); |
| 59 | - private Set<LeadershipEventListener> listeners = Sets.newIdentityHashSet(); | 77 | + |
| 78 | + private final Map<String, Lock> openContests = Maps.newHashMap(); | ||
| 79 | + private final Set<LeadershipEventListener> listeners = Sets.newIdentityHashSet(); | ||
| 60 | private ControllerNode localNode; | 80 | private ControllerNode localNode; |
| 61 | 81 | ||
| 82 | + private final LeadershipEventListener peerAdvertiser = new PeerAdvertiser(); | ||
| 83 | + private final LeadershipEventListener leaderBoardUpdater = new LeaderBoardUpdater(); | ||
| 84 | + | ||
| 85 | + public static final KryoSerializer SERIALIZER = new KryoSerializer() { | ||
| 86 | + @Override | ||
| 87 | + protected void setupKryoPool() { | ||
| 88 | + serializerPool = KryoNamespace.newBuilder() | ||
| 89 | + .register(KryoNamespaces.API) | ||
| 90 | + .build() | ||
| 91 | + .populate(1); | ||
| 92 | + } | ||
| 93 | + }; | ||
| 94 | + | ||
| 62 | @Activate | 95 | @Activate |
| 63 | public void activate() { | 96 | public void activate() { |
| 64 | localNode = clusterService.getLocalNode(); | 97 | localNode = clusterService.getLocalNode(); |
| 98 | + | ||
| 99 | + addListener(peerAdvertiser); | ||
| 100 | + addListener(leaderBoardUpdater); | ||
| 101 | + | ||
| 102 | + clusterCommunicator.addSubscriber( | ||
| 103 | + LEADERSHIP_UPDATES, | ||
| 104 | + new PeerAdvertisementHandler()); | ||
| 105 | + | ||
| 65 | log.info("Started."); | 106 | log.info("Started."); |
| 66 | } | 107 | } |
| 67 | 108 | ||
| 68 | @Deactivate | 109 | @Deactivate |
| 69 | public void deactivate() { | 110 | public void deactivate() { |
| 111 | + removeListener(peerAdvertiser); | ||
| 112 | + removeListener(leaderBoardUpdater); | ||
| 113 | + | ||
| 114 | + clusterCommunicator.removeSubscriber(LEADERSHIP_UPDATES); | ||
| 115 | + | ||
| 70 | THREAD_POOL.shutdown(); | 116 | THREAD_POOL.shutdown(); |
| 117 | + | ||
| 71 | log.info("Stopped."); | 118 | log.info("Stopped."); |
| 72 | } | 119 | } |
| 73 | 120 | ||
| 121 | + | ||
| 122 | + @Override | ||
| 123 | + public ControllerNode getLeader(String path) { | ||
| 124 | + synchronized (leaderBoard) { | ||
| 125 | + Leadership leadership = leaderBoard.get(path); | ||
| 126 | + if (leadership != null) { | ||
| 127 | + return leadership.leader(); | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + return null; | ||
| 131 | + } | ||
| 132 | + | ||
| 74 | @Override | 133 | @Override |
| 75 | public void runForLeadership(String path) { | 134 | public void runForLeadership(String path) { |
| 76 | checkArgument(path != null); | 135 | checkArgument(path != null); |
| ... | @@ -94,8 +153,7 @@ public class LeadershipManager implements LeadershipService { | ... | @@ -94,8 +153,7 @@ public class LeadershipManager implements LeadershipService { |
| 94 | notifyListeners( | 153 | notifyListeners( |
| 95 | new LeadershipEvent( | 154 | new LeadershipEvent( |
| 96 | LeadershipEvent.Type.LEADER_BOOTED, | 155 | LeadershipEvent.Type.LEADER_BOOTED, |
| 97 | - new Leadership(lock.path(), localNode, 0))); | 156 | + new Leadership(lock.path(), localNode, lock.epoch()))); |
| 98 | - // FIXME: Should set the correct term information. | ||
| 99 | } | 157 | } |
| 100 | } | 158 | } |
| 101 | 159 | ||
| ... | @@ -123,26 +181,31 @@ public class LeadershipManager implements LeadershipService { | ... | @@ -123,26 +181,31 @@ public class LeadershipManager implements LeadershipService { |
| 123 | lock.lockAsync(TERM_DURATION_MS).whenComplete((response, error) -> { | 181 | lock.lockAsync(TERM_DURATION_MS).whenComplete((response, error) -> { |
| 124 | if (error == null) { | 182 | if (error == null) { |
| 125 | THREAD_POOL.schedule( | 183 | THREAD_POOL.schedule( |
| 126 | - new RelectionTask(lock), | 184 | + new ReelectionTask(lock), |
| 127 | TERM_DURATION_MS / 2, | 185 | TERM_DURATION_MS / 2, |
| 128 | TimeUnit.MILLISECONDS); | 186 | TimeUnit.MILLISECONDS); |
| 129 | notifyListeners( | 187 | notifyListeners( |
| 130 | new LeadershipEvent( | 188 | new LeadershipEvent( |
| 131 | LeadershipEvent.Type.LEADER_ELECTED, | 189 | LeadershipEvent.Type.LEADER_ELECTED, |
| 132 | - new Leadership(lock.path(), localNode, 0))); | 190 | + new Leadership(lock.path(), localNode, lock.epoch()))); |
| 191 | + return; | ||
| 133 | } else { | 192 | } else { |
| 134 | - log.error("Failed to acquire lock for {}", path, error); | 193 | + log.warn("Failed to acquire lock for {}. Will retry in {} sec", path, WAIT_BEFORE_RETRY_MS, error); |
| 135 | - // retry | 194 | + try { |
| 136 | - tryAcquireLeadership(path); | 195 | + Thread.sleep(WAIT_BEFORE_RETRY_MS); |
| 196 | + tryAcquireLeadership(path); | ||
| 197 | + } catch (InterruptedException e) { | ||
| 198 | + Thread.currentThread().interrupt(); | ||
| 199 | + } | ||
| 137 | } | 200 | } |
| 138 | }); | 201 | }); |
| 139 | } | 202 | } |
| 140 | 203 | ||
| 141 | - private class RelectionTask implements Runnable { | 204 | + private class ReelectionTask implements Runnable { |
| 142 | 205 | ||
| 143 | private final Lock lock; | 206 | private final Lock lock; |
| 144 | 207 | ||
| 145 | - public RelectionTask(Lock lock) { | 208 | + public ReelectionTask(Lock lock) { |
| 146 | this.lock = lock; | 209 | this.lock = lock; |
| 147 | } | 210 | } |
| 148 | 211 | ||
| ... | @@ -152,17 +215,69 @@ public class LeadershipManager implements LeadershipService { | ... | @@ -152,17 +215,69 @@ public class LeadershipManager implements LeadershipService { |
| 152 | notifyListeners( | 215 | notifyListeners( |
| 153 | new LeadershipEvent( | 216 | new LeadershipEvent( |
| 154 | LeadershipEvent.Type.LEADER_REELECTED, | 217 | LeadershipEvent.Type.LEADER_REELECTED, |
| 155 | - new Leadership(lock.path(), localNode, 0))); | 218 | + new Leadership(lock.path(), localNode, lock.epoch()))); |
| 156 | THREAD_POOL.schedule(this, TERM_DURATION_MS / 2, TimeUnit.MILLISECONDS); | 219 | THREAD_POOL.schedule(this, TERM_DURATION_MS / 2, TimeUnit.MILLISECONDS); |
| 157 | } else { | 220 | } else { |
| 158 | if (openContests.containsKey(lock.path())) { | 221 | if (openContests.containsKey(lock.path())) { |
| 159 | notifyListeners( | 222 | notifyListeners( |
| 160 | new LeadershipEvent( | 223 | new LeadershipEvent( |
| 161 | LeadershipEvent.Type.LEADER_BOOTED, | 224 | LeadershipEvent.Type.LEADER_BOOTED, |
| 162 | - new Leadership(lock.path(), localNode, 0))); | 225 | + new Leadership(lock.path(), localNode, lock.epoch()))); |
| 163 | tryAcquireLeadership(lock.path()); | 226 | tryAcquireLeadership(lock.path()); |
| 164 | } | 227 | } |
| 165 | } | 228 | } |
| 166 | } | 229 | } |
| 167 | } | 230 | } |
| 231 | + | ||
| 232 | + private class PeerAdvertiser implements LeadershipEventListener { | ||
| 233 | + @Override | ||
| 234 | + public void event(LeadershipEvent event) { | ||
| 235 | + // publish events originating on this host. | ||
| 236 | + if (event.subject().leader().equals(localNode)) { | ||
| 237 | + try { | ||
| 238 | + clusterCommunicator.broadcast( | ||
| 239 | + new ClusterMessage( | ||
| 240 | + localNode.id(), | ||
| 241 | + LEADERSHIP_UPDATES, | ||
| 242 | + SERIALIZER.encode(event))); | ||
| 243 | + } catch (IOException e) { | ||
| 244 | + log.error("Failed to broadcast leadership update message", e); | ||
| 245 | + } | ||
| 246 | + } | ||
| 247 | + } | ||
| 248 | + } | ||
| 249 | + | ||
| 250 | + private class PeerAdvertisementHandler implements ClusterMessageHandler { | ||
| 251 | + @Override | ||
| 252 | + public void handle(ClusterMessage message) { | ||
| 253 | + LeadershipEvent event = SERIALIZER.decode(message.payload()); | ||
| 254 | + log.debug("Received {} from {}", event, message.sender()); | ||
| 255 | + notifyListeners(event); | ||
| 256 | + } | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + private class LeaderBoardUpdater implements LeadershipEventListener { | ||
| 260 | + @Override | ||
| 261 | + public void event(LeadershipEvent event) { | ||
| 262 | + Leadership leadershipUpdate = event.subject(); | ||
| 263 | + synchronized (leaderBoard) { | ||
| 264 | + Leadership currentLeadership = leaderBoard.get(leadershipUpdate.topic()); | ||
| 265 | + switch (event.type()) { | ||
| 266 | + case LEADER_ELECTED: | ||
| 267 | + case LEADER_REELECTED: | ||
| 268 | + if (currentLeadership == null || currentLeadership.epoch() < leadershipUpdate.epoch()) { | ||
| 269 | + leaderBoard.put(leadershipUpdate.topic(), leadershipUpdate); | ||
| 270 | + } | ||
| 271 | + break; | ||
| 272 | + case LEADER_BOOTED: | ||
| 273 | + if (currentLeadership != null && currentLeadership.epoch() <= leadershipUpdate.epoch()) { | ||
| 274 | + leaderBoard.remove(leadershipUpdate.topic()); | ||
| 275 | + } | ||
| 276 | + break; | ||
| 277 | + default: | ||
| 278 | + break; | ||
| 279 | + } | ||
| 280 | + } | ||
| 281 | + } | ||
| 282 | + } | ||
| 168 | } | 283 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| 1 | package org.onlab.onos.store.service.impl; | 1 | package org.onlab.onos.store.service.impl; |
| 2 | 2 | ||
| 3 | +import static com.google.common.base.Verify.verify; | ||
| 3 | import static org.slf4j.LoggerFactory.getLogger; | 4 | import static org.slf4j.LoggerFactory.getLogger; |
| 4 | 5 | ||
| 5 | import java.nio.charset.StandardCharsets; | 6 | import java.nio.charset.StandardCharsets; |
| 7 | +import java.util.Arrays; | ||
| 6 | import java.util.UUID; | 8 | import java.util.UUID; |
| 7 | import java.util.concurrent.CompletableFuture; | 9 | import java.util.concurrent.CompletableFuture; |
| 8 | import java.util.concurrent.ExecutionException; | 10 | import java.util.concurrent.ExecutionException; |
| ... | @@ -15,6 +17,7 @@ import org.onlab.onos.cluster.ClusterService; | ... | @@ -15,6 +17,7 @@ import org.onlab.onos.cluster.ClusterService; |
| 15 | import org.onlab.onos.store.service.DatabaseException; | 17 | import org.onlab.onos.store.service.DatabaseException; |
| 16 | import org.onlab.onos.store.service.DatabaseService; | 18 | import org.onlab.onos.store.service.DatabaseService; |
| 17 | import org.onlab.onos.store.service.Lock; | 19 | import org.onlab.onos.store.service.Lock; |
| 20 | +import org.onlab.onos.store.service.VersionedValue; | ||
| 18 | import org.slf4j.Logger; | 21 | import org.slf4j.Logger; |
| 19 | 22 | ||
| 20 | /** | 23 | /** |
| ... | @@ -29,6 +32,7 @@ public class DistributedLock implements Lock { | ... | @@ -29,6 +32,7 @@ public class DistributedLock implements Lock { |
| 29 | private final String path; | 32 | private final String path; |
| 30 | private DateTime lockExpirationTime; | 33 | private DateTime lockExpirationTime; |
| 31 | private AtomicBoolean isLocked = new AtomicBoolean(false); | 34 | private AtomicBoolean isLocked = new AtomicBoolean(false); |
| 35 | + private volatile long epoch = 0; | ||
| 32 | private byte[] lockId; | 36 | private byte[] lockId; |
| 33 | 37 | ||
| 34 | public DistributedLock( | 38 | public DistributedLock( |
| ... | @@ -74,6 +78,10 @@ public class DistributedLock implements Lock { | ... | @@ -74,6 +78,10 @@ public class DistributedLock implements Lock { |
| 74 | DistributedLockManager.ONOS_LOCK_TABLE_NAME, | 78 | DistributedLockManager.ONOS_LOCK_TABLE_NAME, |
| 75 | path, | 79 | path, |
| 76 | lockId)) { | 80 | lockId)) { |
| 81 | + VersionedValue vv = | ||
| 82 | + databaseService.get(DistributedLockManager.ONOS_LOCK_TABLE_NAME, path); | ||
| 83 | + verify(Arrays.equals(vv.value(), lockId)); | ||
| 84 | + epoch = vv.version(); | ||
| 77 | isLocked.set(true); | 85 | isLocked.set(true); |
| 78 | lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis); | 86 | lockExpirationTime = DateTime.now().plusMillis(leaseDurationMillis); |
| 79 | return true; | 87 | return true; |
| ... | @@ -121,6 +129,11 @@ public class DistributedLock implements Lock { | ... | @@ -121,6 +129,11 @@ public class DistributedLock implements Lock { |
| 121 | } | 129 | } |
| 122 | 130 | ||
| 123 | @Override | 131 | @Override |
| 132 | + public long epoch() { | ||
| 133 | + return epoch; | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + @Override | ||
| 124 | public void unlock() { | 137 | public void unlock() { |
| 125 | if (!isLocked()) { | 138 | if (!isLocked()) { |
| 126 | return; | 139 | return; | ... | ... |
| ... | @@ -25,6 +25,8 @@ import java.util.LinkedList; | ... | @@ -25,6 +25,8 @@ import java.util.LinkedList; |
| 25 | 25 | ||
| 26 | import org.onlab.onos.cluster.ControllerNode; | 26 | import org.onlab.onos.cluster.ControllerNode; |
| 27 | import org.onlab.onos.cluster.DefaultControllerNode; | 27 | import org.onlab.onos.cluster.DefaultControllerNode; |
| 28 | +import org.onlab.onos.cluster.Leadership; | ||
| 29 | +import org.onlab.onos.cluster.LeadershipEvent; | ||
| 28 | import org.onlab.onos.cluster.NodeId; | 30 | import org.onlab.onos.cluster.NodeId; |
| 29 | import org.onlab.onos.cluster.RoleInfo; | 31 | import org.onlab.onos.cluster.RoleInfo; |
| 30 | import org.onlab.onos.core.DefaultApplicationId; | 32 | import org.onlab.onos.core.DefaultApplicationId; |
| ... | @@ -166,6 +168,9 @@ public final class KryoNamespaces { | ... | @@ -166,6 +168,9 @@ public final class KryoNamespaces { |
| 166 | Link.Type.class, | 168 | Link.Type.class, |
| 167 | Link.State.class, | 169 | Link.State.class, |
| 168 | Timestamp.class, | 170 | Timestamp.class, |
| 171 | + Leadership.class, | ||
| 172 | + LeadershipEvent.class, | ||
| 173 | + LeadershipEvent.Type.class, | ||
| 169 | HostId.class, | 174 | HostId.class, |
| 170 | HostDescription.class, | 175 | HostDescription.class, |
| 171 | DefaultHostDescription.class, | 176 | DefaultHostDescription.class, | ... | ... |
-
Please register or login to post a comment