Added creationTime to Versioned object. This enables supporting a electedTime in…
… leadership, which in turn helps us track how stable leadership terms are. Change-Id: Ib051027625324646152ed85535ba337e95f8a061
Showing
8 changed files
with
148 additions
and
39 deletions
| ... | @@ -19,6 +19,7 @@ import java.util.Comparator; | ... | @@ -19,6 +19,7 @@ import java.util.Comparator; |
| 19 | import java.util.Map; | 19 | import java.util.Map; |
| 20 | 20 | ||
| 21 | import org.apache.karaf.shell.commands.Command; | 21 | import org.apache.karaf.shell.commands.Command; |
| 22 | +import org.onlab.util.Tools; | ||
| 22 | import org.onosproject.cli.AbstractShellCommand; | 23 | import org.onosproject.cli.AbstractShellCommand; |
| 23 | import org.onosproject.cluster.Leadership; | 24 | import org.onosproject.cluster.Leadership; |
| 24 | import org.onosproject.cluster.LeadershipService; | 25 | import org.onosproject.cluster.LeadershipService; |
| ... | @@ -30,15 +31,16 @@ import org.onosproject.cluster.LeadershipService; | ... | @@ -30,15 +31,16 @@ import org.onosproject.cluster.LeadershipService; |
| 30 | description = "Finds the leader for particular topic.") | 31 | description = "Finds the leader for particular topic.") |
| 31 | public class LeaderCommand extends AbstractShellCommand { | 32 | public class LeaderCommand extends AbstractShellCommand { |
| 32 | 33 | ||
| 33 | - private static final String FMT = "%-20s | %-15s | %-6s |"; | 34 | + private static final String FMT = "%-20s | %-15s | %-6s | %-10s |"; |
| 34 | 35 | ||
| 35 | @Override | 36 | @Override |
| 36 | protected void execute() { | 37 | protected void execute() { |
| 37 | LeadershipService leaderService = get(LeadershipService.class); | 38 | LeadershipService leaderService = get(LeadershipService.class); |
| 38 | Map<String, Leadership> leaderBoard = leaderService.getLeaderBoard(); | 39 | Map<String, Leadership> leaderBoard = leaderService.getLeaderBoard(); |
| 39 | - print("-------------------------------------------------"); | 40 | + print("--------------------------------------------------------------"); |
| 40 | - print(FMT, "Topic", "Leader", "Epoch"); | 41 | + print(FMT, "Topic", "Leader", "Epoch", "Elected"); |
| 41 | - print("-------------------------------------------------"); | 42 | + print("--------------------------------------------------------------"); |
| 43 | + | ||
| 42 | 44 | ||
| 43 | Comparator<Leadership> leadershipComparator = | 45 | Comparator<Leadership> leadershipComparator = |
| 44 | (e1, e2) -> { | 46 | (e1, e2) -> { |
| ... | @@ -57,8 +59,11 @@ public class LeaderCommand extends AbstractShellCommand { | ... | @@ -57,8 +59,11 @@ public class LeaderCommand extends AbstractShellCommand { |
| 57 | leaderBoard.values() | 59 | leaderBoard.values() |
| 58 | .stream() | 60 | .stream() |
| 59 | .sorted(leadershipComparator) | 61 | .sorted(leadershipComparator) |
| 60 | - .forEach(l -> print(FMT, l.topic(), l.leader(), l.epoch())); | 62 | + .forEach(l -> print(FMT, |
| 61 | - print("-------------------------------------------------"); | 63 | + l.topic(), |
| 64 | + l.leader(), | ||
| 65 | + l.epoch(), | ||
| 66 | + Tools.timeAgo(l.electedTime()))); | ||
| 67 | + print("--------------------------------------------------------------"); | ||
| 62 | } | 68 | } |
| 63 | - | 69 | +} |
| 64 | -} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -17,6 +17,8 @@ package org.onosproject.cluster; | ... | @@ -17,6 +17,8 @@ package org.onosproject.cluster; |
| 17 | 17 | ||
| 18 | import java.util.Objects; | 18 | import java.util.Objects; |
| 19 | 19 | ||
| 20 | +import org.joda.time.DateTime; | ||
| 21 | + | ||
| 20 | import com.google.common.base.MoreObjects; | 22 | import com.google.common.base.MoreObjects; |
| 21 | 23 | ||
| 22 | /** | 24 | /** |
| ... | @@ -27,11 +29,13 @@ public class Leadership { | ... | @@ -27,11 +29,13 @@ public class Leadership { |
| 27 | private final String topic; | 29 | private final String topic; |
| 28 | private final NodeId leader; | 30 | private final NodeId leader; |
| 29 | private final long epoch; | 31 | private final long epoch; |
| 32 | + private final long electedTime; | ||
| 30 | 33 | ||
| 31 | - public Leadership(String topic, NodeId leader, long epoch) { | 34 | + public Leadership(String topic, NodeId leader, long epoch, long electedTime) { |
| 32 | this.topic = topic; | 35 | this.topic = topic; |
| 33 | this.leader = leader; | 36 | this.leader = leader; |
| 34 | this.epoch = epoch; | 37 | this.epoch = epoch; |
| 38 | + this.electedTime = electedTime; | ||
| 35 | } | 39 | } |
| 36 | 40 | ||
| 37 | /** | 41 | /** |
| ... | @@ -52,12 +56,31 @@ public class Leadership { | ... | @@ -52,12 +56,31 @@ public class Leadership { |
| 52 | 56 | ||
| 53 | /** | 57 | /** |
| 54 | * The epoch when the leadership was assumed. | 58 | * The epoch when the leadership was assumed. |
| 59 | + * <p> | ||
| 60 | + * Comparing epochs is only appropriate for leadership | ||
| 61 | + * events for the same topic. The system guarantees that | ||
| 62 | + * for any given topic the epoch for a new term is higher | ||
| 63 | + * (not necessarily by 1) than the epoch for any previous term. | ||
| 55 | * @return leadership epoch | 64 | * @return leadership epoch |
| 56 | */ | 65 | */ |
| 57 | public long epoch() { | 66 | public long epoch() { |
| 58 | return epoch; | 67 | return epoch; |
| 59 | } | 68 | } |
| 60 | 69 | ||
| 70 | + /** | ||
| 71 | + * The system time when the term started. | ||
| 72 | + * <p> | ||
| 73 | + * The elected time is initially set on the node coordinating | ||
| 74 | + * the leader election using its local system time. Due to possible | ||
| 75 | + * clock skew, relying on this value for determining event ordering | ||
| 76 | + * is discouraged. Epoch is more appropriate for determining | ||
| 77 | + * event ordering. | ||
| 78 | + * @return elected time. | ||
| 79 | + */ | ||
| 80 | + public long electedTime() { | ||
| 81 | + return electedTime; | ||
| 82 | + } | ||
| 83 | + | ||
| 61 | @Override | 84 | @Override |
| 62 | public int hashCode() { | 85 | public int hashCode() { |
| 63 | return Objects.hash(topic, leader, epoch); | 86 | return Objects.hash(topic, leader, epoch); |
| ... | @@ -72,7 +95,8 @@ public class Leadership { | ... | @@ -72,7 +95,8 @@ public class Leadership { |
| 72 | final Leadership other = (Leadership) obj; | 95 | final Leadership other = (Leadership) obj; |
| 73 | return Objects.equals(this.topic, other.topic) && | 96 | return Objects.equals(this.topic, other.topic) && |
| 74 | Objects.equals(this.leader, other.leader) && | 97 | Objects.equals(this.leader, other.leader) && |
| 75 | - Objects.equals(this.epoch, other.epoch); | 98 | + Objects.equals(this.epoch, other.epoch) && |
| 99 | + Objects.equals(this.electedTime, other.electedTime); | ||
| 76 | } | 100 | } |
| 77 | return false; | 101 | return false; |
| 78 | } | 102 | } |
| ... | @@ -83,6 +107,7 @@ public class Leadership { | ... | @@ -83,6 +107,7 @@ public class Leadership { |
| 83 | .add("topic", topic) | 107 | .add("topic", topic) |
| 84 | .add("leader", leader) | 108 | .add("leader", leader) |
| 85 | .add("epoch", epoch) | 109 | .add("epoch", epoch) |
| 110 | + .add("electedTime", new DateTime(electedTime)) | ||
| 86 | .toString(); | 111 | .toString(); |
| 87 | } | 112 | } |
| 88 | } | 113 | } | ... | ... |
| ... | @@ -16,6 +16,8 @@ | ... | @@ -16,6 +16,8 @@ |
| 16 | 16 | ||
| 17 | package org.onosproject.store.service; | 17 | package org.onosproject.store.service; |
| 18 | 18 | ||
| 19 | +import org.joda.time.DateTime; | ||
| 20 | + | ||
| 19 | import com.google.common.base.MoreObjects; | 21 | import com.google.common.base.MoreObjects; |
| 20 | 22 | ||
| 21 | /** | 23 | /** |
| ... | @@ -27,15 +29,28 @@ public class Versioned<V> { | ... | @@ -27,15 +29,28 @@ public class Versioned<V> { |
| 27 | 29 | ||
| 28 | private final V value; | 30 | private final V value; |
| 29 | private final long version; | 31 | private final long version; |
| 32 | + private final long creationTime; | ||
| 30 | 33 | ||
| 31 | /** | 34 | /** |
| 32 | * Constructs a new versioned value. | 35 | * Constructs a new versioned value. |
| 33 | * @param value value | 36 | * @param value value |
| 34 | * @param version version | 37 | * @param version version |
| 38 | + * @param creationTime milliseconds of the creation event | ||
| 39 | + * from the Java epoch of 1970-01-01T00:00:00Z | ||
| 35 | */ | 40 | */ |
| 36 | - public Versioned(V value, long version) { | 41 | + public Versioned(V value, long version, long creationTime) { |
| 37 | this.value = value; | 42 | this.value = value; |
| 38 | this.version = version; | 43 | this.version = version; |
| 44 | + this.creationTime = System.currentTimeMillis(); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + /** | ||
| 48 | + * Constructs a new versioned value. | ||
| 49 | + * @param value value | ||
| 50 | + * @param version version | ||
| 51 | + */ | ||
| 52 | + public Versioned(V value, long version) { | ||
| 53 | + this(value, version, System.currentTimeMillis()); | ||
| 39 | } | 54 | } |
| 40 | 55 | ||
| 41 | /** | 56 | /** |
| ... | @@ -56,11 +71,26 @@ public class Versioned<V> { | ... | @@ -56,11 +71,26 @@ public class Versioned<V> { |
| 56 | return version; | 71 | return version; |
| 57 | } | 72 | } |
| 58 | 73 | ||
| 74 | + /** | ||
| 75 | + * Returns the system time when this version was created. | ||
| 76 | + * <p> | ||
| 77 | + * Care should be taken when relying on creationTime to | ||
| 78 | + * implement any behavior in a distributed setting. Due | ||
| 79 | + * to the possibility of clock skew it is likely that | ||
| 80 | + * even creationTimes of causally related versions can be | ||
| 81 | + * out or order. | ||
| 82 | + * @return creation time | ||
| 83 | + */ | ||
| 84 | + public long creationTime() { | ||
| 85 | + return creationTime; | ||
| 86 | + } | ||
| 87 | + | ||
| 59 | @Override | 88 | @Override |
| 60 | public String toString() { | 89 | public String toString() { |
| 61 | return MoreObjects.toStringHelper(this) | 90 | return MoreObjects.toStringHelper(this) |
| 62 | .add("value", value) | 91 | .add("value", value) |
| 63 | .add("version", version) | 92 | .add("version", version) |
| 93 | + .add("creationTime", new DateTime(creationTime)) | ||
| 64 | .toString(); | 94 | .toString(); |
| 65 | } | 95 | } |
| 66 | } | 96 | } | ... | ... |
| ... | @@ -170,7 +170,8 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -170,7 +170,8 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 170 | if (topic != null) { | 170 | if (topic != null) { |
| 171 | return new Leadership(topic.topicName(), | 171 | return new Leadership(topic.topicName(), |
| 172 | topic.leader(), | 172 | topic.leader(), |
| 173 | - topic.term()); | 173 | + topic.term(), |
| 174 | + 0); | ||
| 174 | } | 175 | } |
| 175 | return null; | 176 | return null; |
| 176 | } | 177 | } |
| ... | @@ -215,7 +216,8 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -215,7 +216,8 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 215 | for (Topic topic : topics.values()) { | 216 | for (Topic topic : topics.values()) { |
| 216 | Leadership leadership = new Leadership(topic.topicName(), | 217 | Leadership leadership = new Leadership(topic.topicName(), |
| 217 | topic.leader(), | 218 | topic.leader(), |
| 218 | - topic.term()); | 219 | + topic.term(), |
| 220 | + 0); | ||
| 219 | result.put(topic.topicName(), leadership); | 221 | result.put(topic.topicName(), leadership); |
| 220 | } | 222 | } |
| 221 | return result; | 223 | return result; |
| ... | @@ -412,7 +414,7 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -412,7 +414,7 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 412 | // | 414 | // |
| 413 | leadershipEvent = new LeadershipEvent( | 415 | leadershipEvent = new LeadershipEvent( |
| 414 | LeadershipEvent.Type.LEADER_REELECTED, | 416 | LeadershipEvent.Type.LEADER_REELECTED, |
| 415 | - new Leadership(topicName, localNodeId, myLastLeaderTerm)); | 417 | + new Leadership(topicName, localNodeId, myLastLeaderTerm, 0)); |
| 416 | // Dispatch to all instances | 418 | // Dispatch to all instances |
| 417 | 419 | ||
| 418 | clusterCommunicator.broadcastIncludeSelf( | 420 | clusterCommunicator.broadcastIncludeSelf( |
| ... | @@ -431,7 +433,7 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -431,7 +433,7 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 431 | topicName, leader); | 433 | topicName, leader); |
| 432 | leadershipEvent = new LeadershipEvent( | 434 | leadershipEvent = new LeadershipEvent( |
| 433 | LeadershipEvent.Type.LEADER_BOOTED, | 435 | LeadershipEvent.Type.LEADER_BOOTED, |
| 434 | - new Leadership(topicName, leader, myLastLeaderTerm)); | 436 | + new Leadership(topicName, leader, myLastLeaderTerm, 0)); |
| 435 | // Dispatch only to the local listener(s) | 437 | // Dispatch only to the local listener(s) |
| 436 | eventDispatcher.post(leadershipEvent); | 438 | eventDispatcher.post(leadershipEvent); |
| 437 | leader = null; | 439 | leader = null; |
| ... | @@ -486,8 +488,8 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -486,8 +488,8 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 486 | 488 | ||
| 487 | leader = localNodeId; | 489 | leader = localNodeId; |
| 488 | leadershipEvent = new LeadershipEvent( | 490 | leadershipEvent = new LeadershipEvent( |
| 489 | - LeadershipEvent.Type.LEADER_ELECTED, | 491 | + LeadershipEvent.Type.LEADER_ELECTED, |
| 490 | - new Leadership(topicName, localNodeId, myLastLeaderTerm)); | 492 | + new Leadership(topicName, localNodeId, myLastLeaderTerm, 0)); |
| 491 | clusterCommunicator.broadcastIncludeSelf( | 493 | clusterCommunicator.broadcastIncludeSelf( |
| 492 | new ClusterMessage( | 494 | new ClusterMessage( |
| 493 | clusterService.getLocalNode().id(), | 495 | clusterService.getLocalNode().id(), |
| ... | @@ -514,8 +516,8 @@ public class HazelcastLeadershipService implements LeadershipService { | ... | @@ -514,8 +516,8 @@ public class HazelcastLeadershipService implements LeadershipService { |
| 514 | leader = null; | 516 | leader = null; |
| 515 | } | 517 | } |
| 516 | leadershipEvent = new LeadershipEvent( | 518 | leadershipEvent = new LeadershipEvent( |
| 517 | - LeadershipEvent.Type.LEADER_BOOTED, | 519 | + LeadershipEvent.Type.LEADER_BOOTED, |
| 518 | - new Leadership(topicName, localNodeId, myLastLeaderTerm)); | 520 | + new Leadership(topicName, localNodeId, myLastLeaderTerm, 0)); |
| 519 | clusterCommunicator.broadcastIncludeSelf( | 521 | clusterCommunicator.broadcastIncludeSelf( |
| 520 | new ClusterMessage( | 522 | new ClusterMessage( |
| 521 | clusterService.getLocalNode().id(), | 523 | clusterService.getLocalNode().id(), | ... | ... |
| ... | @@ -105,7 +105,13 @@ public class ConsistentMapImpl<K, V> implements ConsistentMap<K, V> { | ... | @@ -105,7 +105,13 @@ public class ConsistentMapImpl<K, V> implements ConsistentMap<K, V> { |
| 105 | public Versioned<V> get(K key) { | 105 | public Versioned<V> get(K key) { |
| 106 | checkNotNull(key, ERROR_NULL_KEY); | 106 | checkNotNull(key, ERROR_NULL_KEY); |
| 107 | Versioned<byte[]> value = complete(proxy.get(name, keyCache.getUnchecked(key))); | 107 | Versioned<byte[]> value = complete(proxy.get(name, keyCache.getUnchecked(key))); |
| 108 | - return (value != null) ? new Versioned<>(serializer.decode(value.value()), value.version()) : null; | 108 | + if (value == null) { |
| 109 | + return null; | ||
| 110 | + } | ||
| 111 | + return new Versioned<>( | ||
| 112 | + serializer.decode(value.value()), | ||
| 113 | + value.version(), | ||
| 114 | + value.creationTime()); | ||
| 109 | } | 115 | } |
| 110 | 116 | ||
| 111 | @Override | 117 | @Override |
| ... | @@ -114,16 +120,26 @@ public class ConsistentMapImpl<K, V> implements ConsistentMap<K, V> { | ... | @@ -114,16 +120,26 @@ public class ConsistentMapImpl<K, V> implements ConsistentMap<K, V> { |
| 114 | checkNotNull(value, ERROR_NULL_VALUE); | 120 | checkNotNull(value, ERROR_NULL_VALUE); |
| 115 | Versioned<byte[]> previousValue = | 121 | Versioned<byte[]> previousValue = |
| 116 | complete(proxy.put(name, keyCache.getUnchecked(key), serializer.encode(value))); | 122 | complete(proxy.put(name, keyCache.getUnchecked(key), serializer.encode(value))); |
| 117 | - return (previousValue != null) ? | 123 | + if (previousValue == null) { |
| 118 | - new Versioned<>(serializer.decode(previousValue.value()), previousValue.version()) : null; | 124 | + return null; |
| 119 | - | 125 | + } |
| 126 | + return new Versioned<>( | ||
| 127 | + serializer.decode(previousValue.value()), | ||
| 128 | + previousValue.version(), | ||
| 129 | + previousValue.creationTime()); | ||
| 120 | } | 130 | } |
| 121 | 131 | ||
| 122 | @Override | 132 | @Override |
| 123 | public Versioned<V> remove(K key) { | 133 | public Versioned<V> remove(K key) { |
| 124 | checkNotNull(key, ERROR_NULL_KEY); | 134 | checkNotNull(key, ERROR_NULL_KEY); |
| 125 | Versioned<byte[]> value = complete(proxy.remove(name, keyCache.getUnchecked(key))); | 135 | Versioned<byte[]> value = complete(proxy.remove(name, keyCache.getUnchecked(key))); |
| 126 | - return (value != null) ? new Versioned<>(serializer.decode(value.value()), value.version()) : null; | 136 | + if (value == null) { |
| 137 | + return null; | ||
| 138 | + } | ||
| 139 | + return new Versioned<>( | ||
| 140 | + serializer.decode(value.value()), | ||
| 141 | + value.version(), | ||
| 142 | + value.creationTime()); | ||
| 127 | } | 143 | } |
| 128 | 144 | ||
| 129 | @Override | 145 | @Override |
| ... | @@ -143,7 +159,7 @@ public class ConsistentMapImpl<K, V> implements ConsistentMap<K, V> { | ... | @@ -143,7 +159,7 @@ public class ConsistentMapImpl<K, V> implements ConsistentMap<K, V> { |
| 143 | public Collection<Versioned<V>> values() { | 159 | public Collection<Versioned<V>> values() { |
| 144 | return Collections.unmodifiableList(complete(proxy.values(name)) | 160 | return Collections.unmodifiableList(complete(proxy.values(name)) |
| 145 | .stream() | 161 | .stream() |
| 146 | - .map(v -> new Versioned<V>(serializer.decode(v.value()), v.version())) | 162 | + .map(v -> new Versioned<V>(serializer.decode(v.value()), v.version(), v.creationTime())) |
| 147 | .collect(Collectors.toList())); | 163 | .collect(Collectors.toList())); |
| 148 | } | 164 | } |
| 149 | 165 | ||
| ... | @@ -161,8 +177,13 @@ public class ConsistentMapImpl<K, V> implements ConsistentMap<K, V> { | ... | @@ -161,8 +177,13 @@ public class ConsistentMapImpl<K, V> implements ConsistentMap<K, V> { |
| 161 | checkNotNull(value, ERROR_NULL_VALUE); | 177 | checkNotNull(value, ERROR_NULL_VALUE); |
| 162 | Versioned<byte[]> existingValue = complete(proxy.putIfAbsent( | 178 | Versioned<byte[]> existingValue = complete(proxy.putIfAbsent( |
| 163 | name, keyCache.getUnchecked(key), serializer.encode(value))); | 179 | name, keyCache.getUnchecked(key), serializer.encode(value))); |
| 164 | - return (existingValue != null) ? | 180 | + if (existingValue == null) { |
| 165 | - new Versioned<>(serializer.decode(existingValue.value()), existingValue.version()) : null; | 181 | + return null; |
| 182 | + } | ||
| 183 | + return new Versioned<>( | ||
| 184 | + serializer.decode(existingValue.value()), | ||
| 185 | + existingValue.version(), | ||
| 186 | + existingValue.creationTime()); | ||
| 166 | } | 187 | } |
| 167 | 188 | ||
| 168 | @Override | 189 | @Override |
| ... | @@ -212,6 +233,7 @@ public class ConsistentMapImpl<K, V> implements ConsistentMap<K, V> { | ... | @@ -212,6 +233,7 @@ public class ConsistentMapImpl<K, V> implements ConsistentMap<K, V> { |
| 212 | dK(e.getKey()), | 233 | dK(e.getKey()), |
| 213 | new Versioned<>( | 234 | new Versioned<>( |
| 214 | serializer.decode(e.getValue().value()), | 235 | serializer.decode(e.getValue().value()), |
| 215 | - e.getValue().version())); | 236 | + e.getValue().version(), |
| 237 | + e.getValue().creationTime())); | ||
| 216 | } | 238 | } |
| 217 | } | 239 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -227,7 +227,7 @@ public class DistributedLeadershipManager implements LeadershipService { | ... | @@ -227,7 +227,7 @@ public class DistributedLeadershipManager implements LeadershipService { |
| 227 | if (currentLeader != null) { | 227 | if (currentLeader != null) { |
| 228 | if (localNodeId.equals(currentLeader.value())) { | 228 | if (localNodeId.equals(currentLeader.value())) { |
| 229 | log.info("Already has leadership for {}", path); | 229 | log.info("Already has leadership for {}", path); |
| 230 | - notifyNewLeader(path, localNodeId, currentLeader.version()); | 230 | + notifyNewLeader(path, localNodeId, currentLeader.version(), currentLeader.creationTime()); |
| 231 | } else { | 231 | } else { |
| 232 | // someone else has leadership. will retry after sometime. | 232 | // someone else has leadership. will retry after sometime. |
| 233 | retry(path); | 233 | retry(path); |
| ... | @@ -237,7 +237,7 @@ public class DistributedLeadershipManager implements LeadershipService { | ... | @@ -237,7 +237,7 @@ public class DistributedLeadershipManager implements LeadershipService { |
| 237 | log.info("Assumed leadership for {}", path); | 237 | log.info("Assumed leadership for {}", path); |
| 238 | // do a get again to get the version (epoch) | 238 | // do a get again to get the version (epoch) |
| 239 | Versioned<NodeId> newLeader = lockMap.get(path); | 239 | Versioned<NodeId> newLeader = lockMap.get(path); |
| 240 | - notifyNewLeader(path, localNodeId, newLeader.version()); | 240 | + notifyNewLeader(path, localNodeId, newLeader.version(), newLeader.creationTime()); |
| 241 | } else { | 241 | } else { |
| 242 | // someone beat us to it. | 242 | // someone beat us to it. |
| 243 | retry(path); | 243 | retry(path); |
| ... | @@ -249,8 +249,8 @@ public class DistributedLeadershipManager implements LeadershipService { | ... | @@ -249,8 +249,8 @@ public class DistributedLeadershipManager implements LeadershipService { |
| 249 | } | 249 | } |
| 250 | } | 250 | } |
| 251 | 251 | ||
| 252 | - private void notifyNewLeader(String path, NodeId leader, long epoch) { | 252 | + private void notifyNewLeader(String path, NodeId leader, long epoch, long electedTime) { |
| 253 | - Leadership newLeadership = new Leadership(path, leader, epoch); | 253 | + Leadership newLeadership = new Leadership(path, leader, epoch, electedTime); |
| 254 | boolean updatedLeader = false; | 254 | boolean updatedLeader = false; |
| 255 | synchronized (leaderBoard) { | 255 | synchronized (leaderBoard) { |
| 256 | Leadership currentLeader = leaderBoard.get(path); | 256 | Leadership currentLeader = leaderBoard.get(path); |
| ... | @@ -271,8 +271,8 @@ public class DistributedLeadershipManager implements LeadershipService { | ... | @@ -271,8 +271,8 @@ public class DistributedLeadershipManager implements LeadershipService { |
| 271 | } | 271 | } |
| 272 | } | 272 | } |
| 273 | 273 | ||
| 274 | - private void notifyRemovedLeader(String path, NodeId leader, long epoch) { | 274 | + private void notifyRemovedLeader(String path, NodeId leader, long epoch, long electedTime) { |
| 275 | - Leadership oldLeadership = new Leadership(path, leader, epoch); | 275 | + Leadership oldLeadership = new Leadership(path, leader, epoch, electedTime); |
| 276 | boolean updatedLeader = false; | 276 | boolean updatedLeader = false; |
| 277 | synchronized (leaderBoard) { | 277 | synchronized (leaderBoard) { |
| 278 | Leadership currentLeader = leaderBoard.get(path); | 278 | Leadership currentLeader = leaderBoard.get(path); |
| ... | @@ -346,12 +346,13 @@ public class DistributedLeadershipManager implements LeadershipService { | ... | @@ -346,12 +346,13 @@ public class DistributedLeadershipManager implements LeadershipService { |
| 346 | String path = entry.getKey(); | 346 | String path = entry.getKey(); |
| 347 | NodeId nodeId = entry.getValue().value(); | 347 | NodeId nodeId = entry.getValue().value(); |
| 348 | long epoch = entry.getValue().version(); | 348 | long epoch = entry.getValue().version(); |
| 349 | + long creationTime = entry.getValue().creationTime(); | ||
| 349 | if (clusterService.getState(nodeId) == ControllerNode.State.INACTIVE) { | 350 | if (clusterService.getState(nodeId) == ControllerNode.State.INACTIVE) { |
| 350 | log.info("Lock for {} is held by {} which is currently inactive", path, nodeId); | 351 | log.info("Lock for {} is held by {} which is currently inactive", path, nodeId); |
| 351 | try { | 352 | try { |
| 352 | if (lockMap.remove(path, epoch)) { | 353 | if (lockMap.remove(path, epoch)) { |
| 353 | log.info("Purged stale lock held by {} for {}", nodeId, path); | 354 | log.info("Purged stale lock held by {} for {}", nodeId, path); |
| 354 | - notifyRemovedLeader(path, nodeId, epoch); | 355 | + notifyRemovedLeader(path, nodeId, epoch, creationTime); |
| 355 | } | 356 | } |
| 356 | } catch (Exception e) { | 357 | } catch (Exception e) { |
| 357 | log.warn("Failed to purge stale lock held by {} for {}", nodeId, path, e); | 358 | log.warn("Failed to purge stale lock held by {} for {}", nodeId, path, e); |
| ... | @@ -362,7 +363,7 @@ public class DistributedLeadershipManager implements LeadershipService { | ... | @@ -362,7 +363,7 @@ public class DistributedLeadershipManager implements LeadershipService { |
| 362 | try { | 363 | try { |
| 363 | if (lockMap.remove(path, epoch)) { | 364 | if (lockMap.remove(path, epoch)) { |
| 364 | log.info("Purged stale lock held by {} for {}", nodeId, path); | 365 | log.info("Purged stale lock held by {} for {}", nodeId, path); |
| 365 | - notifyRemovedLeader(path, nodeId, epoch); | 366 | + notifyRemovedLeader(path, nodeId, epoch, creationTime); |
| 366 | } | 367 | } |
| 367 | } catch (Exception e) { | 368 | } catch (Exception e) { |
| 368 | log.warn("Failed to purge stale lock held by {} for {}", nodeId, path, e); | 369 | log.warn("Failed to purge stale lock held by {} for {}", nodeId, path, e); | ... | ... |
| ... | @@ -61,7 +61,7 @@ public class SimpleLeadershipManager implements LeadershipService { | ... | @@ -61,7 +61,7 @@ public class SimpleLeadershipManager implements LeadershipService { |
| 61 | @Override | 61 | @Override |
| 62 | public Leadership getLeadership(String path) { | 62 | public Leadership getLeadership(String path) { |
| 63 | checkArgument(path != null); | 63 | checkArgument(path != null); |
| 64 | - return elections.get(path) ? new Leadership(path, clusterService.getLocalNode().id(), 0) : null; | 64 | + return elections.get(path) ? new Leadership(path, clusterService.getLocalNode().id(), 0, 0) : null; |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | @Override | 67 | @Override |
| ... | @@ -79,7 +79,7 @@ public class SimpleLeadershipManager implements LeadershipService { | ... | @@ -79,7 +79,7 @@ public class SimpleLeadershipManager implements LeadershipService { |
| 79 | elections.put(path, true); | 79 | elections.put(path, true); |
| 80 | for (LeadershipEventListener listener : listeners) { | 80 | for (LeadershipEventListener listener : listeners) { |
| 81 | listener.event(new LeadershipEvent(Type.LEADER_ELECTED, | 81 | listener.event(new LeadershipEvent(Type.LEADER_ELECTED, |
| 82 | - new Leadership(path, clusterService.getLocalNode().id(), 0))); | 82 | + new Leadership(path, clusterService.getLocalNode().id(), 0, 0))); |
| 83 | } | 83 | } |
| 84 | } | 84 | } |
| 85 | 85 | ||
| ... | @@ -88,7 +88,7 @@ public class SimpleLeadershipManager implements LeadershipService { | ... | @@ -88,7 +88,7 @@ public class SimpleLeadershipManager implements LeadershipService { |
| 88 | elections.remove(path); | 88 | elections.remove(path); |
| 89 | for (LeadershipEventListener listener : listeners) { | 89 | for (LeadershipEventListener listener : listeners) { |
| 90 | listener.event(new LeadershipEvent(Type.LEADER_BOOTED, | 90 | listener.event(new LeadershipEvent(Type.LEADER_BOOTED, |
| 91 | - new Leadership(path, clusterService.getLocalNode().id(), 0))); | 91 | + new Leadership(path, clusterService.getLocalNode().id(), 0, 0))); |
| 92 | } | 92 | } |
| 93 | } | 93 | } |
| 94 | 94 | ... | ... |
| ... | @@ -18,6 +18,7 @@ package org.onlab.util; | ... | @@ -18,6 +18,7 @@ package org.onlab.util; |
| 18 | import com.google.common.base.Strings; | 18 | import com.google.common.base.Strings; |
| 19 | import com.google.common.primitives.UnsignedLongs; | 19 | import com.google.common.primitives.UnsignedLongs; |
| 20 | import com.google.common.util.concurrent.ThreadFactoryBuilder; | 20 | import com.google.common.util.concurrent.ThreadFactoryBuilder; |
| 21 | + | ||
| 21 | import org.slf4j.Logger; | 22 | import org.slf4j.Logger; |
| 22 | 23 | ||
| 23 | import java.io.BufferedReader; | 24 | import java.io.BufferedReader; |
| ... | @@ -239,6 +240,29 @@ public abstract class Tools { | ... | @@ -239,6 +240,29 @@ public abstract class Tools { |
| 239 | } | 240 | } |
| 240 | } | 241 | } |
| 241 | 242 | ||
| 243 | + /** | ||
| 244 | + * Returns a human friendly time ago string for a specified system time. | ||
| 245 | + * @param unixTime system time in millis | ||
| 246 | + * @return human friendly time ago | ||
| 247 | + */ | ||
| 248 | + public static String timeAgo(long unixTime) { | ||
| 249 | + long deltaMillis = System.currentTimeMillis() - unixTime; | ||
| 250 | + long secondsSince = (long) (deltaMillis / 1000.0); | ||
| 251 | + long minsSince = (long) (deltaMillis / (1000.0 * 60)); | ||
| 252 | + long hoursSince = (long) (deltaMillis / (1000.0 * 60 * 60)); | ||
| 253 | + long daysSince = (long) (deltaMillis / (1000.0 * 60 * 60 * 24)); | ||
| 254 | + if (daysSince > 0) { | ||
| 255 | + return String.format("%dd ago", daysSince); | ||
| 256 | + } else if (hoursSince > 0) { | ||
| 257 | + return String.format("%dh ago", hoursSince); | ||
| 258 | + } else if (minsSince > 0) { | ||
| 259 | + return String.format("%dm ago", minsSince); | ||
| 260 | + } else if (secondsSince > 0) { | ||
| 261 | + return String.format("%ds ago", secondsSince); | ||
| 262 | + } else { | ||
| 263 | + return "just now"; | ||
| 264 | + } | ||
| 265 | + } | ||
| 242 | 266 | ||
| 243 | /** | 267 | /** |
| 244 | * Copies the specified directory path. Use with great caution since | 268 | * Copies the specified directory path. Use with great caution since | ... | ... |
-
Please register or login to post a comment