Madan Jampani
Committed by Gerrit Code Review

Added a LogicalClockService for ordering arbitrary events in the cluster. Update…

…d couple of areas that are currently vulnerable to clock skew

Change-Id: I14548ecb3c783104de8d72cbb5eb21de6ece08ed
...@@ -33,7 +33,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; ...@@ -33,7 +33,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
33 description = "Lists information about atomic counters in the system") 33 description = "Lists information about atomic counters in the system")
34 public class CountersListCommand extends AbstractShellCommand { 34 public class CountersListCommand extends AbstractShellCommand {
35 35
36 - private static final String FMT = "name=%s next_value=%d"; 36 + private static final String FMT = "name=%s value=%d";
37 37
38 /** 38 /**
39 * Displays counters as text. 39 * Displays counters as text.
...@@ -41,7 +41,7 @@ public class CountersListCommand extends AbstractShellCommand { ...@@ -41,7 +41,7 @@ public class CountersListCommand extends AbstractShellCommand {
41 * @param mapInfo map descriptions 41 * @param mapInfo map descriptions
42 */ 42 */
43 private void displayCounters(Map<String, Long> counters) { 43 private void displayCounters(Map<String, Long> counters) {
44 - counters.forEach((name, nextValue) -> print(FMT, name, nextValue)); 44 + counters.forEach((name, value) -> print(FMT, name, value));
45 } 45 }
46 46
47 /** 47 /**
......
...@@ -37,7 +37,7 @@ public class MapsListCommand extends AbstractShellCommand { ...@@ -37,7 +37,7 @@ public class MapsListCommand extends AbstractShellCommand {
37 // TODO: Add support to display different eventually 37 // TODO: Add support to display different eventually
38 // consistent maps as well. 38 // consistent maps as well.
39 39
40 - private static final String FMT = "%-20s %8s"; 40 + private static final String FMT = "name=%s size=%d";
41 41
42 /** 42 /**
43 * Displays map info as text. 43 * Displays map info as text.
...@@ -45,17 +45,9 @@ public class MapsListCommand extends AbstractShellCommand { ...@@ -45,17 +45,9 @@ public class MapsListCommand extends AbstractShellCommand {
45 * @param mapInfo map descriptions 45 * @param mapInfo map descriptions
46 */ 46 */
47 private void displayMaps(List<MapInfo> mapInfo) { 47 private void displayMaps(List<MapInfo> mapInfo) {
48 - print("------------------------------");
49 - print(FMT, "Name", "Size");
50 - print("------------------------------");
51 -
52 -
53 for (MapInfo info : mapInfo) { 48 for (MapInfo info : mapInfo) {
54 print(FMT, info.name(), info.size()); 49 print(FMT, info.name(), info.size());
55 } 50 }
56 - if (mapInfo.size() > 0) {
57 - print("------------------------------");
58 - }
59 } 51 }
60 52
61 /** 53 /**
......
1 +package org.onosproject.store.service;
2 +
3 +import org.onosproject.store.Timestamp;
4 +
5 +/**
6 + * Service that issues logical timestamps.
7 + * <p>
8 + * The logical timestamps are useful for establishing a total ordering of
9 + * arbitrary cluster wide events without relying on a fully synchronized
10 + * system clock (wall clock)
11 + */
12 +public interface LogicalClockService {
13 +
14 + /**
15 + * Generates a new logical timestamp.
16 + *
17 + * @return timestamp
18 + */
19 + Timestamp getTimestamp();
20 +}
...@@ -18,6 +18,7 @@ package org.onosproject.store.app; ...@@ -18,6 +18,7 @@ package org.onosproject.store.app;
18 import com.google.common.base.Charsets; 18 import com.google.common.base.Charsets;
19 import com.google.common.collect.ImmutableSet; 19 import com.google.common.collect.ImmutableSet;
20 import com.google.common.util.concurrent.ListenableFuture; 20 import com.google.common.util.concurrent.ListenableFuture;
21 +
21 import org.apache.felix.scr.annotations.Activate; 22 import org.apache.felix.scr.annotations.Activate;
22 import org.apache.felix.scr.annotations.Component; 23 import org.apache.felix.scr.annotations.Component;
23 import org.apache.felix.scr.annotations.Deactivate; 24 import org.apache.felix.scr.annotations.Deactivate;
...@@ -44,12 +45,11 @@ import org.onosproject.store.cluster.messaging.ClusterMessage; ...@@ -44,12 +45,11 @@ import org.onosproject.store.cluster.messaging.ClusterMessage;
44 import org.onosproject.store.cluster.messaging.ClusterMessageHandler; 45 import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
45 import org.onosproject.store.cluster.messaging.MessageSubject; 46 import org.onosproject.store.cluster.messaging.MessageSubject;
46 import org.onosproject.store.impl.MultiValuedTimestamp; 47 import org.onosproject.store.impl.MultiValuedTimestamp;
47 -import org.onosproject.store.impl.WallclockClockManager;
48 import org.onosproject.store.serializers.KryoNamespaces; 48 import org.onosproject.store.serializers.KryoNamespaces;
49 -import org.onosproject.store.service.ClockService;
50 import org.onosproject.store.service.EventuallyConsistentMap; 49 import org.onosproject.store.service.EventuallyConsistentMap;
51 import org.onosproject.store.service.EventuallyConsistentMapEvent; 50 import org.onosproject.store.service.EventuallyConsistentMapEvent;
52 import org.onosproject.store.service.EventuallyConsistentMapListener; 51 import org.onosproject.store.service.EventuallyConsistentMapListener;
52 +import org.onosproject.store.service.LogicalClockService;
53 import org.onosproject.store.service.StorageService; 53 import org.onosproject.store.service.StorageService;
54 import org.slf4j.Logger; 54 import org.slf4j.Logger;
55 55
...@@ -61,8 +61,6 @@ import java.util.concurrent.CountDownLatch; ...@@ -61,8 +61,6 @@ import java.util.concurrent.CountDownLatch;
61 import java.util.concurrent.ExecutorService; 61 import java.util.concurrent.ExecutorService;
62 import java.util.concurrent.Executors; 62 import java.util.concurrent.Executors;
63 import java.util.concurrent.ScheduledExecutorService; 63 import java.util.concurrent.ScheduledExecutorService;
64 -import java.util.concurrent.atomic.AtomicLong;
65 -
66 import static com.google.common.io.ByteStreams.toByteArray; 64 import static com.google.common.io.ByteStreams.toByteArray;
67 import static java.util.concurrent.TimeUnit.MILLISECONDS; 65 import static java.util.concurrent.TimeUnit.MILLISECONDS;
68 import static org.onlab.util.Tools.groupedThreads; 66 import static org.onlab.util.Tools.groupedThreads;
...@@ -115,9 +113,10 @@ public class GossipApplicationStore extends ApplicationArchive ...@@ -115,9 +113,10 @@ public class GossipApplicationStore extends ApplicationArchive
115 protected StorageService storageService; 113 protected StorageService storageService;
116 114
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 - protected ApplicationIdStore idStore; 116 + protected LogicalClockService clockService;
119 117
120 - private final AtomicLong sequence = new AtomicLong(); 118 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
119 + protected ApplicationIdStore idStore;
121 120
122 @Activate 121 @Activate
123 public void activate() { 122 public void activate() {
...@@ -135,24 +134,16 @@ public class GossipApplicationStore extends ApplicationArchive ...@@ -135,24 +134,16 @@ public class GossipApplicationStore extends ApplicationArchive
135 134
136 // FIXME: Consider consolidating into a single map. 135 // FIXME: Consider consolidating into a single map.
137 136
138 - ClockService<ApplicationId, Application> appsClockService = (appId, app) ->
139 - new MultiValuedTimestamp<>(getUpdateTime(appId.name()),
140 - sequence.incrementAndGet());
141 -
142 apps = storageService.<ApplicationId, Application>eventuallyConsistentMapBuilder() 137 apps = storageService.<ApplicationId, Application>eventuallyConsistentMapBuilder()
143 .withName("apps") 138 .withName("apps")
144 .withSerializer(serializer) 139 .withSerializer(serializer)
145 - .withClockService(appsClockService) 140 + .withClockService((k, v) -> clockService.getTimestamp())
146 .build(); 141 .build();
147 142
148 - ClockService<Application, InternalState> statesClockService = (app, state) ->
149 - new MultiValuedTimestamp<>(getUpdateTime(app.id().name()),
150 - sequence.incrementAndGet());
151 -
152 states = storageService.<Application, InternalState>eventuallyConsistentMapBuilder() 143 states = storageService.<Application, InternalState>eventuallyConsistentMapBuilder()
153 .withName("app-states") 144 .withName("app-states")
154 .withSerializer(serializer) 145 .withSerializer(serializer)
155 - .withClockService(statesClockService) 146 + .withClockService((k, v) -> clockService.getTimestamp())
156 .build(); 147 .build();
157 148
158 states.addListener(new InternalAppStatesListener()); 149 states.addListener(new InternalAppStatesListener());
...@@ -160,7 +151,7 @@ public class GossipApplicationStore extends ApplicationArchive ...@@ -160,7 +151,7 @@ public class GossipApplicationStore extends ApplicationArchive
160 permissions = storageService.<Application, Set<Permission>>eventuallyConsistentMapBuilder() 151 permissions = storageService.<Application, Set<Permission>>eventuallyConsistentMapBuilder()
161 .withName("app-permissions") 152 .withName("app-permissions")
162 .withSerializer(serializer) 153 .withSerializer(serializer)
163 - .withClockService(new WallclockClockManager<>()) 154 + .withClockService((k, v) -> clockService.getTimestamp())
164 .build(); 155 .build();
165 156
166 log.info("Started"); 157 log.info("Started");
......
...@@ -26,11 +26,11 @@ import org.onosproject.cfg.ComponentConfigEvent; ...@@ -26,11 +26,11 @@ import org.onosproject.cfg.ComponentConfigEvent;
26 import org.onosproject.cfg.ComponentConfigStore; 26 import org.onosproject.cfg.ComponentConfigStore;
27 import org.onosproject.cfg.ComponentConfigStoreDelegate; 27 import org.onosproject.cfg.ComponentConfigStoreDelegate;
28 import org.onosproject.store.AbstractStore; 28 import org.onosproject.store.AbstractStore;
29 -import org.onosproject.store.impl.WallclockClockManager;
30 import org.onosproject.store.serializers.KryoNamespaces; 29 import org.onosproject.store.serializers.KryoNamespaces;
31 import org.onosproject.store.service.EventuallyConsistentMap; 30 import org.onosproject.store.service.EventuallyConsistentMap;
32 import org.onosproject.store.service.EventuallyConsistentMapEvent; 31 import org.onosproject.store.service.EventuallyConsistentMapEvent;
33 import org.onosproject.store.service.EventuallyConsistentMapListener; 32 import org.onosproject.store.service.EventuallyConsistentMapListener;
33 +import org.onosproject.store.service.LogicalClockService;
34 import org.onosproject.store.service.StorageService; 34 import org.onosproject.store.service.StorageService;
35 import org.slf4j.Logger; 35 import org.slf4j.Logger;
36 36
...@@ -59,6 +59,9 @@ public class GossipComponentConfigStore ...@@ -59,6 +59,9 @@ public class GossipComponentConfigStore
59 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 59 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
60 protected StorageService storageService; 60 protected StorageService storageService;
61 61
62 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
63 + protected LogicalClockService clockService;
64 +
62 @Activate 65 @Activate
63 public void activate() { 66 public void activate() {
64 KryoNamespace.Builder serializer = KryoNamespace.newBuilder() 67 KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
...@@ -67,7 +70,7 @@ public class GossipComponentConfigStore ...@@ -67,7 +70,7 @@ public class GossipComponentConfigStore
67 properties = storageService.<String, String>eventuallyConsistentMapBuilder() 70 properties = storageService.<String, String>eventuallyConsistentMapBuilder()
68 .withName("cfg") 71 .withName("cfg")
69 .withSerializer(serializer) 72 .withSerializer(serializer)
70 - .withClockService(new WallclockClockManager<>()) 73 + .withClockService((k, v) -> clockService.getTimestamp())
71 .build(); 74 .build();
72 75
73 properties.addListener(new InternalPropertiesListener()); 76 properties.addListener(new InternalPropertiesListener());
......
1 +package org.onosproject.store.core.impl;
2 +
3 +import static org.slf4j.LoggerFactory.getLogger;
4 +
5 +import org.apache.felix.scr.annotations.Activate;
6 +import org.apache.felix.scr.annotations.Component;
7 +import org.apache.felix.scr.annotations.Deactivate;
8 +import org.apache.felix.scr.annotations.Reference;
9 +import org.apache.felix.scr.annotations.ReferenceCardinality;
10 +import org.apache.felix.scr.annotations.Service;
11 +import org.onosproject.store.Timestamp;
12 +import org.onosproject.store.impl.LogicalTimestamp;
13 +import org.onosproject.store.service.AtomicCounter;
14 +import org.onosproject.store.service.LogicalClockService;
15 +import org.onosproject.store.service.StorageService;
16 +import org.slf4j.Logger;
17 +
18 +/**
19 + * LogicalClockService implementation based on a AtomicCounter.
20 + */
21 +@Component(immediate = true, enabled = true)
22 +@Service
23 +public class LogicalClockManager implements LogicalClockService {
24 +
25 + private final Logger log = getLogger(getClass());
26 +
27 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
28 + protected StorageService storageService;
29 +
30 + private static final String SYSTEM_LOGICAL_CLOCK_COUNTER_NAME = "sys-clock-counter";
31 + private AtomicCounter atomicCounter;
32 +
33 + @Activate
34 + public void activate() {
35 + atomicCounter = storageService.atomicCounterBuilder()
36 + .withName(SYSTEM_LOGICAL_CLOCK_COUNTER_NAME)
37 + .withPartitionsDisabled()
38 + .build();
39 + log.info("Started.");
40 + }
41 +
42 + @Deactivate
43 + public void deactivate() {
44 + log.info("Stopped.");
45 + }
46 +
47 + @Override
48 + public Timestamp getTimestamp() {
49 + return new LogicalTimestamp(atomicCounter.incrementAndGet());
50 + }
51 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -18,6 +18,7 @@ package org.onosproject.store.ecmap; ...@@ -18,6 +18,7 @@ package org.onosproject.store.ecmap;
18 import com.google.common.collect.ImmutableList; 18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.Lists; 19 import com.google.common.collect.Lists;
20 import com.google.common.collect.Maps; 20 import com.google.common.collect.Maps;
21 +
21 import org.apache.commons.lang3.RandomUtils; 22 import org.apache.commons.lang3.RandomUtils;
22 import org.apache.commons.lang3.mutable.MutableBoolean; 23 import org.apache.commons.lang3.mutable.MutableBoolean;
23 import org.apache.commons.lang3.tuple.Pair; 24 import org.apache.commons.lang3.tuple.Pair;
...@@ -32,6 +33,7 @@ import org.onosproject.store.cluster.messaging.ClusterCommunicationService; ...@@ -32,6 +33,7 @@ import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
32 import org.onosproject.store.cluster.messaging.ClusterMessage; 33 import org.onosproject.store.cluster.messaging.ClusterMessage;
33 import org.onosproject.store.cluster.messaging.ClusterMessageHandler; 34 import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
34 import org.onosproject.store.cluster.messaging.MessageSubject; 35 import org.onosproject.store.cluster.messaging.MessageSubject;
36 +import org.onosproject.store.impl.LogicalTimestamp;
35 import org.onosproject.store.impl.Timestamped; 37 import org.onosproject.store.impl.Timestamped;
36 import org.onosproject.store.impl.WallClockTimestamp; 38 import org.onosproject.store.impl.WallClockTimestamp;
37 import org.onosproject.store.serializers.KryoSerializer; 39 import org.onosproject.store.serializers.KryoSerializer;
...@@ -223,6 +225,7 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -223,6 +225,7 @@ public class EventuallyConsistentMapImpl<K, V>
223 protected void setupKryoPool() { 225 protected void setupKryoPool() {
224 // Add the map's internal helper classes to the user-supplied serializer 226 // Add the map's internal helper classes to the user-supplied serializer
225 serializerPool = builder 227 serializerPool = builder
228 + .register(LogicalTimestamp.class)
226 .register(WallClockTimestamp.class) 229 .register(WallClockTimestamp.class)
227 .register(PutEntry.class) 230 .register(PutEntry.class)
228 .register(RemoveEntry.class) 231 .register(RemoveEntry.class)
......
1 +package org.onosproject.store.impl;
2 +
3 +import static com.google.common.base.Preconditions.checkArgument;
4 +
5 +import java.util.Objects;
6 +
7 +import org.onosproject.store.Timestamp;
8 +
9 +import com.google.common.base.MoreObjects;
10 +import com.google.common.collect.ComparisonChain;
11 +
12 +/**
13 + * Timestamp based on logical sequence value.
14 + * <p>
15 + * LogicalTimestamps are ordered by their sequence values.
16 + */
17 +public class LogicalTimestamp implements Timestamp {
18 +
19 + private final long value;
20 +
21 + public LogicalTimestamp(long value) {
22 + this.value = value;
23 + }
24 +
25 + @Override
26 + public int compareTo(Timestamp o) {
27 + checkArgument(o instanceof LogicalTimestamp,
28 + "Must be LogicalTimestamp", o);
29 + LogicalTimestamp that = (LogicalTimestamp) o;
30 +
31 + return ComparisonChain.start()
32 + .compare(this.value, that.value)
33 + .result();
34 + }
35 +
36 + @Override
37 + public int hashCode() {
38 + return Objects.hash(value);
39 + }
40 +
41 + @Override
42 + public boolean equals(Object obj) {
43 + if (this == obj) {
44 + return true;
45 + }
46 + if (!(obj instanceof LogicalTimestamp)) {
47 + return false;
48 + }
49 + LogicalTimestamp that = (LogicalTimestamp) obj;
50 + return Objects.equals(this.value, that.value);
51 + }
52 +
53 + @Override
54 + public String toString() {
55 + return MoreObjects.toStringHelper(getClass())
56 + .add("value", value)
57 + .toString();
58 + }
59 +
60 + /**
61 + * Returns the sequence value.
62 + *
63 + * @return sequence value
64 + */
65 + public long value() {
66 + return this.value;
67 + }
68 +}
...@@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableSet; ...@@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableSet;
20 import com.google.common.collect.Lists; 20 import com.google.common.collect.Lists;
21 import com.google.common.util.concurrent.ListenableFuture; 21 import com.google.common.util.concurrent.ListenableFuture;
22 import com.google.common.util.concurrent.MoreExecutors; 22 import com.google.common.util.concurrent.MoreExecutors;
23 +
23 import org.junit.After; 24 import org.junit.After;
24 import org.junit.Before; 25 import org.junit.Before;
25 import org.junit.Test; 26 import org.junit.Test;
...@@ -35,6 +36,7 @@ import org.onosproject.store.cluster.messaging.ClusterMessage; ...@@ -35,6 +36,7 @@ import org.onosproject.store.cluster.messaging.ClusterMessage;
35 import org.onosproject.store.cluster.messaging.ClusterMessageHandler; 36 import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
36 import org.onosproject.store.cluster.messaging.MessageSubject; 37 import org.onosproject.store.cluster.messaging.MessageSubject;
37 import org.onosproject.store.service.ClockService; 38 import org.onosproject.store.service.ClockService;
39 +import org.onosproject.store.impl.LogicalTimestamp;
38 import org.onosproject.store.impl.WallClockTimestamp; 40 import org.onosproject.store.impl.WallClockTimestamp;
39 import org.onosproject.store.serializers.KryoNamespaces; 41 import org.onosproject.store.serializers.KryoNamespaces;
40 import org.onosproject.store.serializers.KryoSerializer; 42 import org.onosproject.store.serializers.KryoSerializer;
...@@ -102,6 +104,7 @@ public class EventuallyConsistentMapImplTest { ...@@ -102,6 +104,7 @@ public class EventuallyConsistentMapImplTest {
102 .register(KryoNamespaces.API) 104 .register(KryoNamespaces.API)
103 .register(TestTimestamp.class) 105 .register(TestTimestamp.class)
104 // Below is the classes that the map internally registers 106 // Below is the classes that the map internally registers
107 + .register(LogicalTimestamp.class)
105 .register(WallClockTimestamp.class) 108 .register(WallClockTimestamp.class)
106 .register(PutEntry.class) 109 .register(PutEntry.class)
107 .register(RemoveEntry.class) 110 .register(RemoveEntry.class)
......