Committed by
Brian O'Connor
Notification support for Consistent datastructures (ConsitentMap and DistributedSet)
Change-Id: If74cdc411c79c42c7643420e6369cf656849bb6a
Showing
21 changed files
with
585 additions
and
93 deletions
| ... | @@ -288,4 +288,19 @@ public interface AsyncConsistentMap<K, V> { | ... | @@ -288,4 +288,19 @@ public interface AsyncConsistentMap<K, V> { |
| 288 | * @return optional updated value. Will be empty if update did not happen. | 288 | * @return optional updated value. Will be empty if update did not happen. |
| 289 | */ | 289 | */ |
| 290 | CompletableFuture<Optional<Versioned<V>>> replaceAndGet(K key, long oldVersion, V newValue); | 290 | CompletableFuture<Optional<Versioned<V>>> replaceAndGet(K key, long oldVersion, V newValue); |
| 291 | + | ||
| 292 | + /** | ||
| 293 | + * Registers the specified listener to be notified whenever the map is updated. | ||
| 294 | + * | ||
| 295 | + * @param listener listener to notify about map events | ||
| 296 | + */ | ||
| 297 | + void addListener(MapEventListener<K, V> listener); | ||
| 298 | + | ||
| 299 | + /** | ||
| 300 | + * Unregisters the specified listener such that it will no longer | ||
| 301 | + * receive map change notifications. | ||
| 302 | + * | ||
| 303 | + * @param listener listener to unregister | ||
| 304 | + */ | ||
| 305 | + void removeListener(MapEventListener<K, V> listener); | ||
| 291 | } | 306 | } | ... | ... |
| ... | @@ -289,4 +289,19 @@ public interface ConsistentMap<K, V> { | ... | @@ -289,4 +289,19 @@ public interface ConsistentMap<K, V> { |
| 289 | * @return optional new value. Will be empty if replace did not happen | 289 | * @return optional new value. Will be empty if replace did not happen |
| 290 | */ | 290 | */ |
| 291 | Optional<Versioned<V>> replaceAndGet(K key, long oldVersion, V newValue); | 291 | Optional<Versioned<V>> replaceAndGet(K key, long oldVersion, V newValue); |
| 292 | + | ||
| 293 | + /** | ||
| 294 | + * Registers the specified listener to be notified whenever the map is updated. | ||
| 295 | + * | ||
| 296 | + * @param listener listener to notify about map events | ||
| 297 | + */ | ||
| 298 | + void addListener(MapEventListener<K, V> listener); | ||
| 299 | + | ||
| 300 | + /** | ||
| 301 | + * Unregisters the specified listener such that it will no longer | ||
| 302 | + * receive map change notifications. | ||
| 303 | + * | ||
| 304 | + * @param listener listener to unregister | ||
| 305 | + */ | ||
| 306 | + void removeListener(MapEventListener<K, V> listener); | ||
| 292 | } | 307 | } | ... | ... |
| 1 | +package org.onosproject.store.service; | ||
| 2 | + | ||
| 3 | +import java.util.Set; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * A distributed collection designed for holding unique elements. | ||
| 7 | + * | ||
| 8 | + * @param <E> set entry type | ||
| 9 | + */ | ||
| 10 | +public interface DistributedSet<E> extends Set<E> { | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * Registers the specified listener to be notified whenever | ||
| 14 | + * the set is updated. | ||
| 15 | + * | ||
| 16 | + * @param listener listener to notify about set update events | ||
| 17 | + */ | ||
| 18 | + void addListener(SetEventListener<E> listener); | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * Unregisters the specified listener. | ||
| 22 | + * | ||
| 23 | + * @param listener listener to unregister. | ||
| 24 | + */ | ||
| 25 | + void removeListener(SetEventListener<E> listener); | ||
| 26 | +} |
| ... | @@ -15,14 +15,12 @@ | ... | @@ -15,14 +15,12 @@ |
| 15 | */ | 15 | */ |
| 16 | package org.onosproject.store.service; | 16 | package org.onosproject.store.service; |
| 17 | 17 | ||
| 18 | -import java.util.Set; | ||
| 19 | - | ||
| 20 | /** | 18 | /** |
| 21 | * Builder for distributed set. | 19 | * Builder for distributed set. |
| 22 | * | 20 | * |
| 23 | * @param <E> type set elements. | 21 | * @param <E> type set elements. |
| 24 | */ | 22 | */ |
| 25 | -public interface SetBuilder<E> { | 23 | +public interface DistributedSetBuilder<E> { |
| 26 | 24 | ||
| 27 | /** | 25 | /** |
| 28 | * Sets the name of the set. | 26 | * Sets the name of the set. |
| ... | @@ -34,9 +32,9 @@ public interface SetBuilder<E> { | ... | @@ -34,9 +32,9 @@ public interface SetBuilder<E> { |
| 34 | * </p> | 32 | * </p> |
| 35 | * | 33 | * |
| 36 | * @param name name of the set | 34 | * @param name name of the set |
| 37 | - * @return this SetBuilder | 35 | + * @return this DistributedSetBuilder |
| 38 | */ | 36 | */ |
| 39 | - SetBuilder<E> withName(String name); | 37 | + DistributedSetBuilder<E> withName(String name); |
| 40 | 38 | ||
| 41 | /** | 39 | /** |
| 42 | * Sets a serializer that can be used to serialize | 40 | * Sets a serializer that can be used to serialize |
| ... | @@ -48,18 +46,36 @@ public interface SetBuilder<E> { | ... | @@ -48,18 +46,36 @@ public interface SetBuilder<E> { |
| 48 | * </p> | 46 | * </p> |
| 49 | * | 47 | * |
| 50 | * @param serializer serializer | 48 | * @param serializer serializer |
| 51 | - * @return this SetBuilder | 49 | + * @return this DistributedSetBuilder |
| 52 | */ | 50 | */ |
| 53 | - SetBuilder<E> withSerializer(Serializer serializer); | 51 | + DistributedSetBuilder<E> withSerializer(Serializer serializer); |
| 54 | 52 | ||
| 55 | /** | 53 | /** |
| 56 | * Disables set updates. | 54 | * Disables set updates. |
| 57 | * <p> | 55 | * <p> |
| 58 | * Attempt to update the built set will throw {@code UnsupportedOperationException}. | 56 | * Attempt to update the built set will throw {@code UnsupportedOperationException}. |
| 59 | * | 57 | * |
| 60 | - * @return this SetBuilder | 58 | + * @return this DistributedSetBuilder |
| 59 | + */ | ||
| 60 | + DistributedSetBuilder<E> withUpdatesDisabled(); | ||
| 61 | + | ||
| 62 | + /** | ||
| 63 | + * Disables distribution of set entries across multiple database partitions. | ||
| 64 | + * <p> | ||
| 65 | + * When partitioning is disabled, the returned set will have a single partition | ||
| 66 | + * that spans the entire cluster. Furthermore, the changes made to the set are | ||
| 67 | + * ephemeral and do not survive a full cluster restart. | ||
| 68 | + * </p> | ||
| 69 | + * <p> | ||
| 70 | + * Disabling partitions is more appropriate when the returned set is used for | ||
| 71 | + * simple coordination activities and not for long term data persistence. | ||
| 72 | + * </p> | ||
| 73 | + * <p> | ||
| 74 | + * Note: By default partitions are enabled and entries in the set are durable. | ||
| 75 | + * </p> | ||
| 76 | + * @return this DistributedSetBuilder | ||
| 61 | */ | 77 | */ |
| 62 | - SetBuilder<E> withUpdatesDisabled(); | 78 | + DistributedSetBuilder<E> withPartitionsDisabled(); |
| 63 | 79 | ||
| 64 | /** | 80 | /** |
| 65 | * Builds an set based on the configuration options | 81 | * Builds an set based on the configuration options |
| ... | @@ -68,5 +84,5 @@ public interface SetBuilder<E> { | ... | @@ -68,5 +84,5 @@ public interface SetBuilder<E> { |
| 68 | * @return new set | 84 | * @return new set |
| 69 | * @throws java.lang.RuntimeException if a mandatory parameter is missing | 85 | * @throws java.lang.RuntimeException if a mandatory parameter is missing |
| 70 | */ | 86 | */ |
| 71 | - Set<E> build(); | 87 | + DistributedSet<E> build(); |
| 72 | } | 88 | } | ... | ... |
| 1 | +package org.onosproject.store.service; | ||
| 2 | + | ||
| 3 | +import java.util.Objects; | ||
| 4 | + | ||
| 5 | +import com.google.common.base.MoreObjects; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * Representation of a ConsistentMap update notification. | ||
| 9 | + * | ||
| 10 | + * @param <K> key type | ||
| 11 | + * @param <V> value type | ||
| 12 | + */ | ||
| 13 | +public class MapEvent<K, V> { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * MapEvent type. | ||
| 17 | + */ | ||
| 18 | + public enum Type { | ||
| 19 | + /** | ||
| 20 | + * Entry inserted into the map. | ||
| 21 | + */ | ||
| 22 | + INSERT, | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * Existing map entry updated. | ||
| 26 | + */ | ||
| 27 | + UPDATE, | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * Entry removed from map. | ||
| 31 | + */ | ||
| 32 | + REMOVE | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + private final String name; | ||
| 36 | + private final Type type; | ||
| 37 | + private final K key; | ||
| 38 | + private final Versioned<V> value; | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * Creates a new event object. | ||
| 42 | + * | ||
| 43 | + * @param name map name | ||
| 44 | + * @param type the type of the event | ||
| 45 | + * @param key the key the event concerns | ||
| 46 | + * @param value the value related to the key, or null for remove events | ||
| 47 | + */ | ||
| 48 | + public MapEvent(String name, Type type, K key, Versioned<V> value) { | ||
| 49 | + this.name = name; | ||
| 50 | + this.type = type; | ||
| 51 | + this.key = key; | ||
| 52 | + this.value = value; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * Returns the map name. | ||
| 57 | + * | ||
| 58 | + * @return name of map | ||
| 59 | + */ | ||
| 60 | + public String name() { | ||
| 61 | + return name; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * Returns the type of the event. | ||
| 66 | + * | ||
| 67 | + * @return the type of the event | ||
| 68 | + */ | ||
| 69 | + public Type type() { | ||
| 70 | + return type; | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * Returns the key this event concerns. | ||
| 75 | + * | ||
| 76 | + * @return the key | ||
| 77 | + */ | ||
| 78 | + public K key() { | ||
| 79 | + return key; | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + /** | ||
| 83 | + * Returns the value associated with this event. If type is REMOVE, | ||
| 84 | + * this is the value that was removed. If type is INSERT/UPDATE, this is | ||
| 85 | + * the new value. | ||
| 86 | + * | ||
| 87 | + * @return the value | ||
| 88 | + */ | ||
| 89 | + public Versioned<V> value() { | ||
| 90 | + return value; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + @Override | ||
| 94 | + public boolean equals(Object o) { | ||
| 95 | + if (!(o instanceof MapEvent)) { | ||
| 96 | + return false; | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + MapEvent<K, V> that = (MapEvent) o; | ||
| 100 | + return Objects.equals(this.name, that.name) && | ||
| 101 | + Objects.equals(this.type, that.type) && | ||
| 102 | + Objects.equals(this.key, that.key) && | ||
| 103 | + Objects.equals(this.value, that.value); | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + @Override | ||
| 107 | + public int hashCode() { | ||
| 108 | + return Objects.hash(type, key, value); | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + @Override | ||
| 112 | + public String toString() { | ||
| 113 | + return MoreObjects.toStringHelper(getClass()) | ||
| 114 | + .add("name", name) | ||
| 115 | + .add("type", type) | ||
| 116 | + .add("key", key) | ||
| 117 | + .add("value", value) | ||
| 118 | + .toString(); | ||
| 119 | + } | ||
| 120 | +} |
| 1 | +package org.onosproject.store.service; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * Listener to be notified about updates to a ConsitentMap. | ||
| 5 | + */ | ||
| 6 | +public interface MapEventListener<K, V> { | ||
| 7 | + /** | ||
| 8 | + * Reacts to the specified event. | ||
| 9 | + * | ||
| 10 | + * @param event the event | ||
| 11 | + */ | ||
| 12 | + void event(MapEvent<K, V> event); | ||
| 13 | +} |
| ... | @@ -16,8 +16,13 @@ | ... | @@ -16,8 +16,13 @@ |
| 16 | 16 | ||
| 17 | package org.onosproject.store.service; | 17 | package org.onosproject.store.service; |
| 18 | 18 | ||
| 19 | +import java.util.Arrays; | ||
| 20 | +import java.util.List; | ||
| 21 | + | ||
| 19 | import org.onlab.util.KryoNamespace; | 22 | import org.onlab.util.KryoNamespace; |
| 20 | 23 | ||
| 24 | +import com.google.common.collect.Lists; | ||
| 25 | + | ||
| 21 | /** | 26 | /** |
| 22 | * Interface for serialization for store artifacts. | 27 | * Interface for serialization for store artifacts. |
| 23 | */ | 28 | */ |
| ... | @@ -45,16 +50,32 @@ public interface Serializer { | ... | @@ -45,16 +50,32 @@ public interface Serializer { |
| 45 | * @return Serializer instance | 50 | * @return Serializer instance |
| 46 | */ | 51 | */ |
| 47 | static Serializer using(KryoNamespace kryo) { | 52 | static Serializer using(KryoNamespace kryo) { |
| 53 | + return using(Arrays.asList(kryo)); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + static Serializer using(List<KryoNamespace> namespaces, Class<?>... classes) { | ||
| 57 | + KryoNamespace.Builder builder = new KryoNamespace.Builder(); | ||
| 58 | + namespaces.forEach(builder::register); | ||
| 59 | + Lists.newArrayList(classes).forEach(builder::register); | ||
| 60 | + builder.register(MapEvent.class, MapEvent.Type.class); | ||
| 61 | + KryoNamespace namespace = builder.build(); | ||
| 48 | return new Serializer() { | 62 | return new Serializer() { |
| 49 | @Override | 63 | @Override |
| 50 | public <T> byte[] encode(T object) { | 64 | public <T> byte[] encode(T object) { |
| 51 | - return kryo.serialize(object); | 65 | + return namespace.serialize(object); |
| 52 | } | 66 | } |
| 53 | 67 | ||
| 54 | @Override | 68 | @Override |
| 55 | public <T> T decode(byte[] bytes) { | 69 | public <T> T decode(byte[] bytes) { |
| 56 | - return kryo.deserialize(bytes); | 70 | + return namespace.deserialize(bytes); |
| 57 | } | 71 | } |
| 58 | }; | 72 | }; |
| 59 | } | 73 | } |
| 74 | + | ||
| 75 | + static Serializer forTypes(Class<?>... classes) { | ||
| 76 | + return using(KryoNamespace.newBuilder() | ||
| 77 | + .register(classes) | ||
| 78 | + .register(MapEvent.class, MapEvent.Type.class) | ||
| 79 | + .build()); | ||
| 80 | + } | ||
| 60 | } | 81 | } | ... | ... |
| 1 | +package org.onosproject.store.service; | ||
| 2 | + | ||
| 3 | +import java.util.Objects; | ||
| 4 | + | ||
| 5 | +import com.google.common.base.MoreObjects; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * Representation of a DistributedSet update notification. | ||
| 9 | + * | ||
| 10 | + * @param <E> element type | ||
| 11 | + */ | ||
| 12 | +public class SetEvent<E> { | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * SetEvent type. | ||
| 16 | + */ | ||
| 17 | + public enum Type { | ||
| 18 | + /** | ||
| 19 | + * Entry added to the set. | ||
| 20 | + */ | ||
| 21 | + ADD, | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * Entry removed from the set. | ||
| 25 | + */ | ||
| 26 | + REMOVE | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + private final String name; | ||
| 30 | + private final Type type; | ||
| 31 | + private final E entry; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * Creates a new event object. | ||
| 35 | + * | ||
| 36 | + * @param name set name | ||
| 37 | + * @param type the type of the event | ||
| 38 | + * @param entry the entry the event concerns | ||
| 39 | + */ | ||
| 40 | + public SetEvent(String name, Type type, E entry) { | ||
| 41 | + this.name = name; | ||
| 42 | + this.type = type; | ||
| 43 | + this.entry = entry; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * Returns the set name. | ||
| 48 | + * | ||
| 49 | + * @return name of set | ||
| 50 | + */ | ||
| 51 | + public String name() { | ||
| 52 | + return name; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * Returns the type of the event. | ||
| 57 | + * | ||
| 58 | + * @return the type of the event | ||
| 59 | + */ | ||
| 60 | + public Type type() { | ||
| 61 | + return type; | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + /** | ||
| 65 | + * Returns the entry this event concerns. | ||
| 66 | + * | ||
| 67 | + * @return the entry | ||
| 68 | + */ | ||
| 69 | + public E entry() { | ||
| 70 | + return entry; | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + @Override | ||
| 74 | + public boolean equals(Object o) { | ||
| 75 | + if (!(o instanceof SetEvent)) { | ||
| 76 | + return false; | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + SetEvent<E> that = (SetEvent) o; | ||
| 80 | + return Objects.equals(this.name, that.name) && | ||
| 81 | + Objects.equals(this.type, that.type) && | ||
| 82 | + Objects.equals(this.entry, that.entry); | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + @Override | ||
| 86 | + public int hashCode() { | ||
| 87 | + return Objects.hash(name, type, entry); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + @Override | ||
| 91 | + public String toString() { | ||
| 92 | + return MoreObjects.toStringHelper(getClass()) | ||
| 93 | + .add("name", name) | ||
| 94 | + .add("type", type) | ||
| 95 | + .add("entry", entry) | ||
| 96 | + .toString(); | ||
| 97 | + } | ||
| 98 | +} |
| ... | @@ -53,7 +53,7 @@ public interface StorageService { | ... | @@ -53,7 +53,7 @@ public interface StorageService { |
| 53 | * @param <E> set element type | 53 | * @param <E> set element type |
| 54 | * @return builder for an distributed set | 54 | * @return builder for an distributed set |
| 55 | */ | 55 | */ |
| 56 | - <E> SetBuilder<E> setBuilder(); | 56 | + <E> DistributedSetBuilder<E> setBuilder(); |
| 57 | 57 | ||
| 58 | /** | 58 | /** |
| 59 | * Creates a new AtomicCounterBuilder. | 59 | * Creates a new AtomicCounterBuilder. | ... | ... |
| ... | @@ -42,19 +42,24 @@ import org.apache.felix.scr.annotations.Deactivate; | ... | @@ -42,19 +42,24 @@ import org.apache.felix.scr.annotations.Deactivate; |
| 42 | import org.apache.felix.scr.annotations.Reference; | 42 | import org.apache.felix.scr.annotations.Reference; |
| 43 | import org.apache.felix.scr.annotations.ReferenceCardinality; | 43 | import org.apache.felix.scr.annotations.ReferenceCardinality; |
| 44 | import org.apache.felix.scr.annotations.Service; | 44 | import org.apache.felix.scr.annotations.Service; |
| 45 | + | ||
| 46 | +import static org.onlab.util.Tools.groupedThreads; | ||
| 47 | + | ||
| 45 | import org.onosproject.cluster.ClusterService; | 48 | import org.onosproject.cluster.ClusterService; |
| 46 | import org.onosproject.core.IdGenerator; | 49 | import org.onosproject.core.IdGenerator; |
| 47 | import org.onosproject.store.cluster.impl.ClusterDefinitionManager; | 50 | import org.onosproject.store.cluster.impl.ClusterDefinitionManager; |
| 48 | import org.onosproject.store.cluster.impl.NodeInfo; | 51 | import org.onosproject.store.cluster.impl.NodeInfo; |
| 49 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; | 52 | import org.onosproject.store.cluster.messaging.ClusterCommunicationService; |
| 53 | +import org.onosproject.store.cluster.messaging.MessageSubject; | ||
| 50 | import org.onosproject.store.ecmap.EventuallyConsistentMapBuilderImpl; | 54 | import org.onosproject.store.ecmap.EventuallyConsistentMapBuilderImpl; |
| 51 | import org.onosproject.store.service.AtomicCounterBuilder; | 55 | import org.onosproject.store.service.AtomicCounterBuilder; |
| 52 | import org.onosproject.store.service.ConsistentMapBuilder; | 56 | import org.onosproject.store.service.ConsistentMapBuilder; |
| 53 | import org.onosproject.store.service.ConsistentMapException; | 57 | import org.onosproject.store.service.ConsistentMapException; |
| 54 | import org.onosproject.store.service.EventuallyConsistentMapBuilder; | 58 | import org.onosproject.store.service.EventuallyConsistentMapBuilder; |
| 59 | +import org.onosproject.store.service.MapEvent; | ||
| 55 | import org.onosproject.store.service.MapInfo; | 60 | import org.onosproject.store.service.MapInfo; |
| 56 | import org.onosproject.store.service.PartitionInfo; | 61 | import org.onosproject.store.service.PartitionInfo; |
| 57 | -import org.onosproject.store.service.SetBuilder; | 62 | +import org.onosproject.store.service.DistributedSetBuilder; |
| 58 | import org.onosproject.store.service.StorageAdminService; | 63 | import org.onosproject.store.service.StorageAdminService; |
| 59 | import org.onosproject.store.service.StorageService; | 64 | import org.onosproject.store.service.StorageService; |
| 60 | import org.onosproject.store.service.Transaction; | 65 | import org.onosproject.store.service.Transaction; |
| ... | @@ -69,6 +74,7 @@ import java.util.Map; | ... | @@ -69,6 +74,7 @@ import java.util.Map; |
| 69 | import java.util.Set; | 74 | import java.util.Set; |
| 70 | import java.util.concurrent.CompletableFuture; | 75 | import java.util.concurrent.CompletableFuture; |
| 71 | import java.util.concurrent.ExecutionException; | 76 | import java.util.concurrent.ExecutionException; |
| 77 | +import java.util.concurrent.ExecutorService; | ||
| 72 | import java.util.concurrent.Executors; | 78 | import java.util.concurrent.Executors; |
| 73 | import java.util.concurrent.TimeUnit; | 79 | import java.util.concurrent.TimeUnit; |
| 74 | import java.util.concurrent.TimeoutException; | 80 | import java.util.concurrent.TimeoutException; |
| ... | @@ -93,12 +99,16 @@ public class DatabaseManager implements StorageService, StorageAdminService { | ... | @@ -93,12 +99,16 @@ public class DatabaseManager implements StorageService, StorageAdminService { |
| 93 | private static final int DATABASE_OPERATION_TIMEOUT_MILLIS = 5000; | 99 | private static final int DATABASE_OPERATION_TIMEOUT_MILLIS = 5000; |
| 94 | 100 | ||
| 95 | private ClusterCoordinator coordinator; | 101 | private ClusterCoordinator coordinator; |
| 96 | - private PartitionedDatabase partitionedDatabase; | 102 | + protected PartitionedDatabase partitionedDatabase; |
| 97 | - private Database inMemoryDatabase; | 103 | + protected Database inMemoryDatabase; |
| 98 | 104 | ||
| 99 | private TransactionManager transactionManager; | 105 | private TransactionManager transactionManager; |
| 100 | private final IdGenerator transactionIdGenerator = () -> RandomUtils.nextLong(); | 106 | private final IdGenerator transactionIdGenerator = () -> RandomUtils.nextLong(); |
| 101 | 107 | ||
| 108 | + private ExecutorService eventDispatcher; | ||
| 109 | + | ||
| 110 | + private final Set<DefaultAsyncConsistentMap> maps = Sets.newCopyOnWriteArraySet(); | ||
| 111 | + | ||
| 102 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 112 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 103 | protected ClusterService clusterService; | 113 | protected ClusterService clusterService; |
| 104 | 114 | ||
| ... | @@ -187,7 +197,10 @@ public class DatabaseManager implements StorageService, StorageAdminService { | ... | @@ -187,7 +197,10 @@ public class DatabaseManager implements StorageService, StorageAdminService { |
| 187 | 197 | ||
| 188 | Futures.getUnchecked(status); | 198 | Futures.getUnchecked(status); |
| 189 | 199 | ||
| 190 | - transactionManager = new TransactionManager(partitionedDatabase); | 200 | + transactionManager = new TransactionManager(partitionedDatabase, consistentMapBuilder()); |
| 201 | + | ||
| 202 | + eventDispatcher = Executors.newSingleThreadExecutor( | ||
| 203 | + groupedThreads("onos/store/manager", "map-event-dispatcher")); | ||
| 191 | log.info("Started"); | 204 | log.info("Started"); |
| 192 | } | 205 | } |
| 193 | 206 | ||
| ... | @@ -213,13 +226,14 @@ public class DatabaseManager implements StorageService, StorageAdminService { | ... | @@ -213,13 +226,14 @@ public class DatabaseManager implements StorageService, StorageAdminService { |
| 213 | log.info("Successfully closed databases."); | 226 | log.info("Successfully closed databases."); |
| 214 | } | 227 | } |
| 215 | }); | 228 | }); |
| 229 | + maps.forEach(map -> clusterCommunicator.removeSubscriber(mapUpdatesSubject(map.name()))); | ||
| 230 | + eventDispatcher.shutdown(); | ||
| 216 | log.info("Stopped"); | 231 | log.info("Stopped"); |
| 217 | } | 232 | } |
| 218 | 233 | ||
| 219 | @Override | 234 | @Override |
| 220 | public TransactionContextBuilder transactionContextBuilder() { | 235 | public TransactionContextBuilder transactionContextBuilder() { |
| 221 | - return new DefaultTransactionContextBuilder( | 236 | + return new DefaultTransactionContextBuilder(this, transactionIdGenerator.getNewId()); |
| 222 | - inMemoryDatabase, partitionedDatabase, transactionIdGenerator.getNewId()); | ||
| 223 | } | 237 | } |
| 224 | 238 | ||
| 225 | @Override | 239 | @Override |
| ... | @@ -296,12 +310,12 @@ public class DatabaseManager implements StorageService, StorageAdminService { | ... | @@ -296,12 +310,12 @@ public class DatabaseManager implements StorageService, StorageAdminService { |
| 296 | 310 | ||
| 297 | @Override | 311 | @Override |
| 298 | public <K, V> ConsistentMapBuilder<K, V> consistentMapBuilder() { | 312 | public <K, V> ConsistentMapBuilder<K, V> consistentMapBuilder() { |
| 299 | - return new DefaultConsistentMapBuilder<>(inMemoryDatabase, partitionedDatabase); | 313 | + return new DefaultConsistentMapBuilder<>(this); |
| 300 | } | 314 | } |
| 301 | 315 | ||
| 302 | @Override | 316 | @Override |
| 303 | - public <E> SetBuilder<E> setBuilder() { | 317 | + public <E> DistributedSetBuilder<E> setBuilder() { |
| 304 | - return new DefaultSetBuilder<>(partitionedDatabase); | 318 | + return new DefaultDistributedSetBuilder<>(this); |
| 305 | } | 319 | } |
| 306 | 320 | ||
| 307 | @Override | 321 | @Override |
| ... | @@ -370,4 +384,20 @@ public class DatabaseManager implements StorageService, StorageAdminService { | ... | @@ -370,4 +384,20 @@ public class DatabaseManager implements StorageService, StorageAdminService { |
| 370 | public void redriveTransactions() { | 384 | public void redriveTransactions() { |
| 371 | getTransactions().stream().forEach(transactionManager::execute); | 385 | getTransactions().stream().forEach(transactionManager::execute); |
| 372 | } | 386 | } |
| 387 | + | ||
| 388 | + protected <K, V> void registerMap(DefaultAsyncConsistentMap<K, V> map) { | ||
| 389 | + // TODO: Support different local instances of the same map. | ||
| 390 | + if (!maps.add(map)) { | ||
| 391 | + throw new IllegalStateException("Map by name " + map.name() + " already exists"); | ||
| 392 | + } | ||
| 393 | + | ||
| 394 | + clusterCommunicator.<MapEvent<K, V>>addSubscriber(mapUpdatesSubject(map.name()), | ||
| 395 | + map.serializer()::decode, | ||
| 396 | + map::notifyLocalListeners, | ||
| 397 | + eventDispatcher); | ||
| 398 | + } | ||
| 399 | + | ||
| 400 | + protected static MessageSubject mapUpdatesSubject(String mapName) { | ||
| 401 | + return new MessageSubject(mapName + "-map-updates"); | ||
| 402 | + } | ||
| 373 | } | 403 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -17,6 +17,7 @@ | ... | @@ -17,6 +17,7 @@ |
| 17 | package org.onosproject.store.consistent.impl; | 17 | package org.onosproject.store.consistent.impl; |
| 18 | 18 | ||
| 19 | import static com.google.common.base.Preconditions.*; | 19 | import static com.google.common.base.Preconditions.*; |
| 20 | +import static org.slf4j.LoggerFactory.getLogger; | ||
| 20 | 21 | ||
| 21 | import java.util.Collection; | 22 | import java.util.Collection; |
| 22 | import java.util.Map; | 23 | import java.util.Map; |
| ... | @@ -24,8 +25,10 @@ import java.util.Map.Entry; | ... | @@ -24,8 +25,10 @@ import java.util.Map.Entry; |
| 24 | import java.util.Objects; | 25 | import java.util.Objects; |
| 25 | import java.util.Optional; | 26 | import java.util.Optional; |
| 26 | import java.util.concurrent.CompletableFuture; | 27 | import java.util.concurrent.CompletableFuture; |
| 28 | +import java.util.concurrent.CopyOnWriteArraySet; | ||
| 27 | import java.util.concurrent.atomic.AtomicReference; | 29 | import java.util.concurrent.atomic.AtomicReference; |
| 28 | import java.util.function.BiFunction; | 30 | import java.util.function.BiFunction; |
| 31 | +import java.util.function.Consumer; | ||
| 29 | import java.util.function.Function; | 32 | import java.util.function.Function; |
| 30 | import java.util.function.Predicate; | 33 | import java.util.function.Predicate; |
| 31 | import java.util.stream.Collectors; | 34 | import java.util.stream.Collectors; |
| ... | @@ -36,8 +39,11 @@ import org.onlab.util.HexString; | ... | @@ -36,8 +39,11 @@ import org.onlab.util.HexString; |
| 36 | import org.onlab.util.Tools; | 39 | import org.onlab.util.Tools; |
| 37 | import org.onosproject.store.service.AsyncConsistentMap; | 40 | import org.onosproject.store.service.AsyncConsistentMap; |
| 38 | import org.onosproject.store.service.ConsistentMapException; | 41 | import org.onosproject.store.service.ConsistentMapException; |
| 42 | +import org.onosproject.store.service.MapEvent; | ||
| 43 | +import org.onosproject.store.service.MapEventListener; | ||
| 39 | import org.onosproject.store.service.Serializer; | 44 | import org.onosproject.store.service.Serializer; |
| 40 | import org.onosproject.store.service.Versioned; | 45 | import org.onosproject.store.service.Versioned; |
| 46 | +import org.slf4j.Logger; | ||
| 41 | 47 | ||
| 42 | import com.google.common.cache.CacheBuilder; | 48 | import com.google.common.cache.CacheBuilder; |
| 43 | import com.google.common.cache.CacheLoader; | 49 | import com.google.common.cache.CacheLoader; |
| ... | @@ -56,6 +62,11 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -56,6 +62,11 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
| 56 | private final Database database; | 62 | private final Database database; |
| 57 | private final Serializer serializer; | 63 | private final Serializer serializer; |
| 58 | private final boolean readOnly; | 64 | private final boolean readOnly; |
| 65 | + private final Consumer<MapEvent<K, V>> eventPublisher; | ||
| 66 | + | ||
| 67 | + private final Set<MapEventListener<K, V>> listeners = new CopyOnWriteArraySet<>(); | ||
| 68 | + | ||
| 69 | + private final Logger log = getLogger(getClass()); | ||
| 59 | 70 | ||
| 60 | private static final String ERROR_NULL_KEY = "Key cannot be null"; | 71 | private static final String ERROR_NULL_KEY = "Key cannot be null"; |
| 61 | private static final String ERROR_NULL_VALUE = "Null values are not allowed"; | 72 | private static final String ERROR_NULL_VALUE = "Null values are not allowed"; |
| ... | @@ -77,11 +88,29 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -77,11 +88,29 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
| 77 | public DefaultAsyncConsistentMap(String name, | 88 | public DefaultAsyncConsistentMap(String name, |
| 78 | Database database, | 89 | Database database, |
| 79 | Serializer serializer, | 90 | Serializer serializer, |
| 80 | - boolean readOnly) { | 91 | + boolean readOnly, |
| 92 | + Consumer<MapEvent<K, V>> eventPublisher) { | ||
| 81 | this.name = checkNotNull(name, "map name cannot be null"); | 93 | this.name = checkNotNull(name, "map name cannot be null"); |
| 82 | this.database = checkNotNull(database, "database cannot be null"); | 94 | this.database = checkNotNull(database, "database cannot be null"); |
| 83 | this.serializer = checkNotNull(serializer, "serializer cannot be null"); | 95 | this.serializer = checkNotNull(serializer, "serializer cannot be null"); |
| 84 | this.readOnly = readOnly; | 96 | this.readOnly = readOnly; |
| 97 | + this.eventPublisher = eventPublisher; | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + /** | ||
| 101 | + * Returns this map name. | ||
| 102 | + * @return map name | ||
| 103 | + */ | ||
| 104 | + public String name() { | ||
| 105 | + return name; | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + /** | ||
| 109 | + * Returns the serializer for map entries. | ||
| 110 | + * @return map entry serializer | ||
| 111 | + */ | ||
| 112 | + public Serializer serializer() { | ||
| 113 | + return serializer; | ||
| 85 | } | 114 | } |
| 86 | 115 | ||
| 87 | @Override | 116 | @Override |
| ... | @@ -139,6 +168,7 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -139,6 +168,7 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
| 139 | checkNotNull(key, ERROR_NULL_KEY); | 168 | checkNotNull(key, ERROR_NULL_KEY); |
| 140 | checkNotNull(condition, "predicate function cannot be null"); | 169 | checkNotNull(condition, "predicate function cannot be null"); |
| 141 | checkNotNull(remappingFunction, "Remapping function cannot be null"); | 170 | checkNotNull(remappingFunction, "Remapping function cannot be null"); |
| 171 | + AtomicReference<MapEvent<K, V>> mapEvent = new AtomicReference<>(); | ||
| 142 | return get(key).thenCompose(r1 -> { | 172 | return get(key).thenCompose(r1 -> { |
| 143 | V existingValue = r1 == null ? null : r1.value(); | 173 | V existingValue = r1 == null ? null : r1.value(); |
| 144 | // if the condition evaluates to false, return existing value. | 174 | // if the condition evaluates to false, return existing value. |
| ... | @@ -160,6 +190,7 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -160,6 +190,7 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
| 160 | if (r1 != null) { | 190 | if (r1 != null) { |
| 161 | return remove(key, r1.version()).thenApply(result -> { | 191 | return remove(key, r1.version()).thenApply(result -> { |
| 162 | if (result) { | 192 | if (result) { |
| 193 | + mapEvent.set(new MapEvent<>(name, MapEvent.Type.REMOVE, key, r1)); | ||
| 163 | return null; | 194 | return null; |
| 164 | } else { | 195 | } else { |
| 165 | throw new ConsistentMapException.ConcurrentModification(); | 196 | throw new ConsistentMapException.ConcurrentModification(); |
| ... | @@ -174,6 +205,7 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -174,6 +205,7 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
| 174 | return replaceAndGet(key, r1.version(), computedValue.get()) | 205 | return replaceAndGet(key, r1.version(), computedValue.get()) |
| 175 | .thenApply(v -> { | 206 | .thenApply(v -> { |
| 176 | if (v.isPresent()) { | 207 | if (v.isPresent()) { |
| 208 | + mapEvent.set(new MapEvent<>(name, MapEvent.Type.UPDATE, key, v.get())); | ||
| 177 | return v.get(); | 209 | return v.get(); |
| 178 | } else { | 210 | } else { |
| 179 | throw new ConsistentMapException.ConcurrentModification(); | 211 | throw new ConsistentMapException.ConcurrentModification(); |
| ... | @@ -184,12 +216,13 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -184,12 +216,13 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
| 184 | if (!result.isPresent()) { | 216 | if (!result.isPresent()) { |
| 185 | throw new ConsistentMapException.ConcurrentModification(); | 217 | throw new ConsistentMapException.ConcurrentModification(); |
| 186 | } else { | 218 | } else { |
| 219 | + mapEvent.set(new MapEvent<>(name, MapEvent.Type.INSERT, key, result.get())); | ||
| 187 | return result.get(); | 220 | return result.get(); |
| 188 | } | 221 | } |
| 189 | }); | 222 | }); |
| 190 | } | 223 | } |
| 191 | } | 224 | } |
| 192 | - }); | 225 | + }).whenComplete((result, error) -> notifyListeners(mapEvent.get())); |
| 193 | } | 226 | } |
| 194 | 227 | ||
| 195 | @Override | 228 | @Override |
| ... | @@ -370,4 +403,35 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> | ... | @@ -370,4 +403,35 @@ public class DefaultAsyncConsistentMap<K, V> implements AsyncConsistentMap<K, V> |
| 370 | throw new UnsupportedOperationException(); | 403 | throw new UnsupportedOperationException(); |
| 371 | } | 404 | } |
| 372 | } | 405 | } |
| 406 | + | ||
| 407 | + @Override | ||
| 408 | + public void addListener(MapEventListener<K, V> listener) { | ||
| 409 | + listeners.add(listener); | ||
| 410 | + } | ||
| 411 | + | ||
| 412 | + @Override | ||
| 413 | + public void removeListener(MapEventListener<K, V> listener) { | ||
| 414 | + listeners.remove(listener); | ||
| 415 | + } | ||
| 416 | + | ||
| 417 | + protected void notifyListeners(MapEvent<K, V> event) { | ||
| 418 | + try { | ||
| 419 | + if (event != null) { | ||
| 420 | + notifyLocalListeners(event); | ||
| 421 | + notifyRemoteListeners(event); | ||
| 422 | + } | ||
| 423 | + } catch (Exception e) { | ||
| 424 | + log.warn("Failure notifying listeners about {}", event, e); | ||
| 425 | + } | ||
| 426 | + } | ||
| 427 | + | ||
| 428 | + protected void notifyLocalListeners(MapEvent<K, V> event) { | ||
| 429 | + listeners.forEach(listener -> listener.event(event)); | ||
| 430 | + } | ||
| 431 | + | ||
| 432 | + protected void notifyRemoteListeners(MapEvent<K, V> event) { | ||
| 433 | + if (eventPublisher != null) { | ||
| 434 | + eventPublisher.accept(event); | ||
| 435 | + } | ||
| 436 | + } | ||
| 373 | } | 437 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -28,10 +28,9 @@ import java.util.function.Function; | ... | @@ -28,10 +28,9 @@ import java.util.function.Function; |
| 28 | import java.util.function.Predicate; | 28 | import java.util.function.Predicate; |
| 29 | import java.util.Set; | 29 | import java.util.Set; |
| 30 | 30 | ||
| 31 | -import org.onosproject.store.service.AsyncConsistentMap; | ||
| 32 | import org.onosproject.store.service.ConsistentMap; | 31 | import org.onosproject.store.service.ConsistentMap; |
| 33 | import org.onosproject.store.service.ConsistentMapException; | 32 | import org.onosproject.store.service.ConsistentMapException; |
| 34 | -import org.onosproject.store.service.Serializer; | 33 | +import org.onosproject.store.service.MapEventListener; |
| 35 | import org.onosproject.store.service.Versioned; | 34 | import org.onosproject.store.service.Versioned; |
| 36 | 35 | ||
| 37 | /** | 36 | /** |
| ... | @@ -45,13 +44,14 @@ public class DefaultConsistentMap<K, V> implements ConsistentMap<K, V> { | ... | @@ -45,13 +44,14 @@ public class DefaultConsistentMap<K, V> implements ConsistentMap<K, V> { |
| 45 | 44 | ||
| 46 | private static final int OPERATION_TIMEOUT_MILLIS = 5000; | 45 | private static final int OPERATION_TIMEOUT_MILLIS = 5000; |
| 47 | 46 | ||
| 48 | - private final AsyncConsistentMap<K, V> asyncMap; | 47 | + private final DefaultAsyncConsistentMap<K, V> asyncMap; |
| 49 | 48 | ||
| 50 | - public DefaultConsistentMap(String name, | 49 | + public String name() { |
| 51 | - Database database, | 50 | + return asyncMap.name(); |
| 52 | - Serializer serializer, | 51 | + } |
| 53 | - boolean readOnly) { | 52 | + |
| 54 | - asyncMap = new DefaultAsyncConsistentMap<>(name, database, serializer, readOnly); | 53 | + public DefaultConsistentMap(DefaultAsyncConsistentMap<K, V> asyncMap) { |
| 54 | + this.asyncMap = asyncMap; | ||
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | @Override | 57 | @Override |
| ... | @@ -190,4 +190,14 @@ public class DefaultConsistentMap<K, V> implements ConsistentMap<K, V> { | ... | @@ -190,4 +190,14 @@ public class DefaultConsistentMap<K, V> implements ConsistentMap<K, V> { |
| 190 | } | 190 | } |
| 191 | } | 191 | } |
| 192 | } | 192 | } |
| 193 | + | ||
| 194 | + @Override | ||
| 195 | + public void addListener(MapEventListener<K, V> listener) { | ||
| 196 | + asyncMap.addListener(listener); | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + @Override | ||
| 200 | + public void removeListener(MapEventListener<K, V> listener) { | ||
| 201 | + asyncMap.addListener(listener); | ||
| 202 | + } | ||
| 193 | } | 203 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultConsistentMapBuilder.java
| ... | @@ -6,6 +6,7 @@ import static com.google.common.base.Preconditions.checkState; | ... | @@ -6,6 +6,7 @@ import static com.google.common.base.Preconditions.checkState; |
| 6 | import org.onosproject.store.service.AsyncConsistentMap; | 6 | import org.onosproject.store.service.AsyncConsistentMap; |
| 7 | import org.onosproject.store.service.ConsistentMap; | 7 | import org.onosproject.store.service.ConsistentMap; |
| 8 | import org.onosproject.store.service.ConsistentMapBuilder; | 8 | import org.onosproject.store.service.ConsistentMapBuilder; |
| 9 | +import org.onosproject.store.service.MapEvent; | ||
| 9 | import org.onosproject.store.service.Serializer; | 10 | import org.onosproject.store.service.Serializer; |
| 10 | 11 | ||
| 11 | /** | 12 | /** |
| ... | @@ -20,12 +21,10 @@ public class DefaultConsistentMapBuilder<K, V> implements ConsistentMapBuilder<K | ... | @@ -20,12 +21,10 @@ public class DefaultConsistentMapBuilder<K, V> implements ConsistentMapBuilder<K |
| 20 | private String name; | 21 | private String name; |
| 21 | private boolean partitionsEnabled = true; | 22 | private boolean partitionsEnabled = true; |
| 22 | private boolean readOnly = false; | 23 | private boolean readOnly = false; |
| 23 | - private final Database partitionedDatabase; | 24 | + private final DatabaseManager manager; |
| 24 | - private final Database inMemoryDatabase; | ||
| 25 | 25 | ||
| 26 | - public DefaultConsistentMapBuilder(Database inMemoryDatabase, Database partitionedDatabase) { | 26 | + public DefaultConsistentMapBuilder(DatabaseManager manager) { |
| 27 | - this.inMemoryDatabase = inMemoryDatabase; | 27 | + this.manager = manager; |
| 28 | - this.partitionedDatabase = partitionedDatabase; | ||
| 29 | } | 28 | } |
| 30 | 29 | ||
| 31 | @Override | 30 | @Override |
| ... | @@ -60,21 +59,25 @@ public class DefaultConsistentMapBuilder<K, V> implements ConsistentMapBuilder<K | ... | @@ -60,21 +59,25 @@ public class DefaultConsistentMapBuilder<K, V> implements ConsistentMapBuilder<K |
| 60 | 59 | ||
| 61 | @Override | 60 | @Override |
| 62 | public ConsistentMap<K, V> build() { | 61 | public ConsistentMap<K, V> build() { |
| 63 | - checkState(validInputs()); | 62 | + return new DefaultConsistentMap<>(buildAndRegisterMap()); |
| 64 | - return new DefaultConsistentMap<>( | ||
| 65 | - name, | ||
| 66 | - partitionsEnabled ? partitionedDatabase : inMemoryDatabase, | ||
| 67 | - serializer, | ||
| 68 | - readOnly); | ||
| 69 | } | 63 | } |
| 70 | 64 | ||
| 71 | @Override | 65 | @Override |
| 72 | public AsyncConsistentMap<K, V> buildAsyncMap() { | 66 | public AsyncConsistentMap<K, V> buildAsyncMap() { |
| 67 | + return buildAndRegisterMap(); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + private DefaultAsyncConsistentMap<K, V> buildAndRegisterMap() { | ||
| 73 | checkState(validInputs()); | 71 | checkState(validInputs()); |
| 74 | - return new DefaultAsyncConsistentMap<>( | 72 | + DefaultAsyncConsistentMap<K, V> asyncMap = new DefaultAsyncConsistentMap<>( |
| 75 | name, | 73 | name, |
| 76 | - partitionsEnabled ? partitionedDatabase : inMemoryDatabase, | 74 | + partitionsEnabled ? manager.partitionedDatabase : manager.inMemoryDatabase, |
| 77 | serializer, | 75 | serializer, |
| 78 | - readOnly); | 76 | + readOnly, |
| 77 | + event -> manager.clusterCommunicator.<MapEvent<K, V>>broadcast(event, | ||
| 78 | + DatabaseManager.mapUpdatesSubject(name), | ||
| 79 | + serializer::encode)); | ||
| 80 | + manager.registerMap(asyncMap); | ||
| 81 | + return asyncMap; | ||
| 79 | } | 82 | } |
| 80 | } | 83 | } |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -17,11 +17,17 @@ package org.onosproject.store.consistent.impl; | ... | @@ -17,11 +17,17 @@ package org.onosproject.store.consistent.impl; |
| 17 | 17 | ||
| 18 | import java.util.Collection; | 18 | import java.util.Collection; |
| 19 | import java.util.Iterator; | 19 | import java.util.Iterator; |
| 20 | +import java.util.Map; | ||
| 20 | import java.util.Set; | 21 | import java.util.Set; |
| 21 | 22 | ||
| 22 | import org.onosproject.store.service.ConsistentMap; | 23 | import org.onosproject.store.service.ConsistentMap; |
| 23 | -import org.onosproject.store.service.Serializer; | 24 | +import org.onosproject.store.service.DistributedSet; |
| 25 | +import org.onosproject.store.service.MapEvent; | ||
| 26 | +import org.onosproject.store.service.MapEventListener; | ||
| 27 | +import org.onosproject.store.service.SetEvent; | ||
| 28 | +import org.onosproject.store.service.SetEventListener; | ||
| 24 | 29 | ||
| 30 | +import com.google.common.collect.Maps; | ||
| 25 | import com.google.common.collect.Sets; | 31 | import com.google.common.collect.Sets; |
| 26 | 32 | ||
| 27 | /** | 33 | /** |
| ... | @@ -29,12 +35,15 @@ import com.google.common.collect.Sets; | ... | @@ -29,12 +35,15 @@ import com.google.common.collect.Sets; |
| 29 | 35 | ||
| 30 | * @param <E> set element type | 36 | * @param <E> set element type |
| 31 | */ | 37 | */ |
| 32 | -public class DefaultDistributedSet<E> implements Set<E> { | 38 | +public class DefaultDistributedSet<E> implements DistributedSet<E> { |
| 33 | 39 | ||
| 40 | + private final String name; | ||
| 34 | private final ConsistentMap<E, Boolean> backingMap; | 41 | private final ConsistentMap<E, Boolean> backingMap; |
| 42 | + private final Map<SetEventListener<E>, MapEventListener<E, Boolean>> listenerMapping = Maps.newIdentityHashMap(); | ||
| 35 | 43 | ||
| 36 | - public DefaultDistributedSet(String name, Database database, Serializer serializer, boolean readOnly) { | 44 | + public DefaultDistributedSet(String name, ConsistentMap<E, Boolean> backingMap) { |
| 37 | - backingMap = new DefaultConsistentMap<>(name, database, serializer, readOnly); | 45 | + this.name = name; |
| 46 | + this.backingMap = backingMap; | ||
| 38 | } | 47 | } |
| 39 | 48 | ||
| 40 | @Override | 49 | @Override |
| ... | @@ -76,7 +85,7 @@ public class DefaultDistributedSet<E> implements Set<E> { | ... | @@ -76,7 +85,7 @@ public class DefaultDistributedSet<E> implements Set<E> { |
| 76 | @SuppressWarnings("unchecked") | 85 | @SuppressWarnings("unchecked") |
| 77 | @Override | 86 | @Override |
| 78 | public boolean remove(Object o) { | 87 | public boolean remove(Object o) { |
| 79 | - return backingMap.remove((E) o, true); | 88 | + return backingMap.remove((E) o) != null; |
| 80 | } | 89 | } |
| 81 | 90 | ||
| 82 | @Override | 91 | @Override |
| ... | @@ -119,4 +128,26 @@ public class DefaultDistributedSet<E> implements Set<E> { | ... | @@ -119,4 +128,26 @@ public class DefaultDistributedSet<E> implements Set<E> { |
| 119 | public void clear() { | 128 | public void clear() { |
| 120 | backingMap.clear(); | 129 | backingMap.clear(); |
| 121 | } | 130 | } |
| 131 | + | ||
| 132 | + @Override | ||
| 133 | + public void addListener(SetEventListener<E> listener) { | ||
| 134 | + MapEventListener<E, Boolean> mapEventListener = mapEvent -> { | ||
| 135 | + if (mapEvent.type() == MapEvent.Type.INSERT) { | ||
| 136 | + listener.event(new SetEvent<>(name, SetEvent.Type.ADD, mapEvent.key())); | ||
| 137 | + } else if (mapEvent.type() == MapEvent.Type.REMOVE) { | ||
| 138 | + listener.event(new SetEvent<>(name, SetEvent.Type.REMOVE, mapEvent.key())); | ||
| 139 | + } | ||
| 140 | + }; | ||
| 141 | + if (listenerMapping.putIfAbsent(listener, mapEventListener) == null) { | ||
| 142 | + backingMap.addListener(mapEventListener); | ||
| 143 | + } | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + @Override | ||
| 147 | + public void removeListener(SetEventListener<E> listener) { | ||
| 148 | + MapEventListener<E, Boolean> mapEventListener = listenerMapping.remove(listener); | ||
| 149 | + if (mapEventListener != null) { | ||
| 150 | + backingMap.removeListener(mapEventListener); | ||
| 151 | + } | ||
| 152 | + } | ||
| 122 | } | 153 | } | ... | ... |
| ... | @@ -15,58 +15,52 @@ | ... | @@ -15,58 +15,52 @@ |
| 15 | */ | 15 | */ |
| 16 | package org.onosproject.store.consistent.impl; | 16 | package org.onosproject.store.consistent.impl; |
| 17 | 17 | ||
| 18 | -import static com.google.common.base.Preconditions.checkArgument; | 18 | +import org.onosproject.store.service.ConsistentMapBuilder; |
| 19 | -import static com.google.common.base.Preconditions.checkNotNull; | 19 | +import org.onosproject.store.service.DistributedSet; |
| 20 | -import static com.google.common.base.Preconditions.checkState; | ||
| 21 | - | ||
| 22 | -import java.util.Set; | ||
| 23 | - | ||
| 24 | import org.onosproject.store.service.Serializer; | 20 | import org.onosproject.store.service.Serializer; |
| 25 | -import org.onosproject.store.service.SetBuilder; | 21 | +import org.onosproject.store.service.DistributedSetBuilder; |
| 26 | 22 | ||
| 27 | /** | 23 | /** |
| 28 | - * Default Set builder. | 24 | + * Default distributed set builder. |
| 29 | * | 25 | * |
| 30 | * @param <E> type for set elements | 26 | * @param <E> type for set elements |
| 31 | */ | 27 | */ |
| 32 | -public class DefaultSetBuilder<E> implements SetBuilder<E> { | 28 | +public class DefaultDistributedSetBuilder<E> implements DistributedSetBuilder<E> { |
| 33 | 29 | ||
| 34 | - private Serializer serializer; | ||
| 35 | private String name; | 30 | private String name; |
| 36 | - private final Database database; | 31 | + private ConsistentMapBuilder<E, Boolean> mapBuilder; |
| 37 | - private boolean readOnly; | ||
| 38 | 32 | ||
| 39 | - public DefaultSetBuilder(Database database) { | 33 | + public DefaultDistributedSetBuilder(DatabaseManager manager) { |
| 40 | - this.database = checkNotNull(database); | 34 | + this.mapBuilder = manager.consistentMapBuilder(); |
| 41 | } | 35 | } |
| 42 | 36 | ||
| 43 | @Override | 37 | @Override |
| 44 | - public SetBuilder<E> withName(String name) { | 38 | + public DistributedSetBuilder<E> withName(String name) { |
| 45 | - checkArgument(name != null && !name.isEmpty()); | 39 | + mapBuilder.withName(name); |
| 46 | this.name = name; | 40 | this.name = name; |
| 47 | return this; | 41 | return this; |
| 48 | } | 42 | } |
| 49 | 43 | ||
| 50 | @Override | 44 | @Override |
| 51 | - public SetBuilder<E> withSerializer(Serializer serializer) { | 45 | + public DistributedSetBuilder<E> withSerializer(Serializer serializer) { |
| 52 | - checkArgument(serializer != null); | 46 | + mapBuilder.withSerializer(serializer); |
| 53 | - this.serializer = serializer; | ||
| 54 | return this; | 47 | return this; |
| 55 | } | 48 | } |
| 56 | 49 | ||
| 57 | @Override | 50 | @Override |
| 58 | - public SetBuilder<E> withUpdatesDisabled() { | 51 | + public DistributedSetBuilder<E> withUpdatesDisabled() { |
| 59 | - readOnly = true; | 52 | + mapBuilder.withUpdatesDisabled(); |
| 60 | return this; | 53 | return this; |
| 61 | } | 54 | } |
| 62 | 55 | ||
| 63 | - private boolean validInputs() { | 56 | + @Override |
| 64 | - return name != null && serializer != null; | 57 | + public DistributedSetBuilder<E> withPartitionsDisabled() { |
| 58 | + mapBuilder.withPartitionsDisabled(); | ||
| 59 | + return this; | ||
| 65 | } | 60 | } |
| 66 | 61 | ||
| 67 | @Override | 62 | @Override |
| 68 | - public Set<E> build() { | 63 | + public DistributedSet<E> build() { |
| 69 | - checkState(validInputs()); | 64 | + return new DefaultDistributedSet<E>(name, mapBuilder.build()); |
| 70 | - return new DefaultDistributedSet<>(name, database, serializer, readOnly); | ||
| 71 | } | 65 | } |
| 72 | } | 66 | } | ... | ... |
| ... | @@ -18,9 +18,11 @@ package org.onosproject.store.consistent.impl; | ... | @@ -18,9 +18,11 @@ package org.onosproject.store.consistent.impl; |
| 18 | 18 | ||
| 19 | import java.util.List; | 19 | import java.util.List; |
| 20 | import java.util.Map; | 20 | import java.util.Map; |
| 21 | +import java.util.function.Supplier; | ||
| 21 | 22 | ||
| 22 | import static com.google.common.base.Preconditions.*; | 23 | import static com.google.common.base.Preconditions.*; |
| 23 | 24 | ||
| 25 | +import org.onosproject.store.service.ConsistentMapBuilder; | ||
| 24 | import org.onosproject.store.service.DatabaseUpdate; | 26 | import org.onosproject.store.service.DatabaseUpdate; |
| 25 | import org.onosproject.store.service.Serializer; | 27 | import org.onosproject.store.service.Serializer; |
| 26 | import org.onosproject.store.service.TransactionContext; | 28 | import org.onosproject.store.service.TransactionContext; |
| ... | @@ -41,10 +43,14 @@ public class DefaultTransactionContext implements TransactionContext { | ... | @@ -41,10 +43,14 @@ public class DefaultTransactionContext implements TransactionContext { |
| 41 | private boolean isOpen = false; | 43 | private boolean isOpen = false; |
| 42 | private final Database database; | 44 | private final Database database; |
| 43 | private final long transactionId; | 45 | private final long transactionId; |
| 46 | + private final Supplier<ConsistentMapBuilder> mapBuilderSupplier; | ||
| 44 | 47 | ||
| 45 | - public DefaultTransactionContext(Database database, long transactionId) { | 48 | + public DefaultTransactionContext(long transactionId, |
| 46 | - this.database = checkNotNull(database); | 49 | + Database database, |
| 50 | + Supplier<ConsistentMapBuilder> mapBuilderSupplier) { | ||
| 47 | this.transactionId = transactionId; | 51 | this.transactionId = transactionId; |
| 52 | + this.database = checkNotNull(database); | ||
| 53 | + this.mapBuilderSupplier = checkNotNull(mapBuilderSupplier); | ||
| 48 | } | 54 | } |
| 49 | 55 | ||
| 50 | @Override | 56 | @Override |
| ... | @@ -72,7 +78,7 @@ public class DefaultTransactionContext implements TransactionContext { | ... | @@ -72,7 +78,7 @@ public class DefaultTransactionContext implements TransactionContext { |
| 72 | checkNotNull(serializer); | 78 | checkNotNull(serializer); |
| 73 | return txMaps.computeIfAbsent(mapName, name -> new DefaultTransactionalMap<>( | 79 | return txMaps.computeIfAbsent(mapName, name -> new DefaultTransactionalMap<>( |
| 74 | name, | 80 | name, |
| 75 | - new DefaultConsistentMap<>(name, database, serializer, false), | 81 | + mapBuilderSupplier.get().withName(name).withSerializer(serializer).build(), |
| 76 | this, | 82 | this, |
| 77 | serializer)); | 83 | serializer)); |
| 78 | } | 84 | } |
| ... | @@ -85,6 +91,7 @@ public class DefaultTransactionContext implements TransactionContext { | ... | @@ -85,6 +91,7 @@ public class DefaultTransactionContext implements TransactionContext { |
| 85 | List<DatabaseUpdate> updates = Lists.newLinkedList(); | 91 | List<DatabaseUpdate> updates = Lists.newLinkedList(); |
| 86 | txMaps.values() | 92 | txMaps.values() |
| 87 | .forEach(m -> { updates.addAll(m.prepareDatabaseUpdates()); }); | 93 | .forEach(m -> { updates.addAll(m.prepareDatabaseUpdates()); }); |
| 94 | + // FIXME: Updates made via transactional context currently do not result in notifications. (ONOS-2097) | ||
| 88 | database.prepareAndCommit(new DefaultTransaction(transactionId, updates)); | 95 | database.prepareAndCommit(new DefaultTransaction(transactionId, updates)); |
| 89 | } catch (Exception e) { | 96 | } catch (Exception e) { |
| 90 | abort(); | 97 | abort(); | ... | ... |
| ... | @@ -10,14 +10,11 @@ import org.onosproject.store.service.TransactionContextBuilder; | ... | @@ -10,14 +10,11 @@ import org.onosproject.store.service.TransactionContextBuilder; |
| 10 | public class DefaultTransactionContextBuilder implements TransactionContextBuilder { | 10 | public class DefaultTransactionContextBuilder implements TransactionContextBuilder { |
| 11 | 11 | ||
| 12 | private boolean partitionsEnabled = true; | 12 | private boolean partitionsEnabled = true; |
| 13 | - private final Database partitionedDatabase; | 13 | + private final DatabaseManager manager; |
| 14 | - private final Database inMemoryDatabase; | ||
| 15 | private final long transactionId; | 14 | private final long transactionId; |
| 16 | 15 | ||
| 17 | - public DefaultTransactionContextBuilder( | 16 | + public DefaultTransactionContextBuilder(DatabaseManager manager, long transactionId) { |
| 18 | - Database inMemoryDatabase, Database partitionedDatabase, long transactionId) { | 17 | + this.manager = manager; |
| 19 | - this.partitionedDatabase = partitionedDatabase; | ||
| 20 | - this.inMemoryDatabase = inMemoryDatabase; | ||
| 21 | this.transactionId = transactionId; | 18 | this.transactionId = transactionId; |
| 22 | } | 19 | } |
| 23 | 20 | ||
| ... | @@ -30,8 +27,9 @@ public class DefaultTransactionContextBuilder implements TransactionContextBuild | ... | @@ -30,8 +27,9 @@ public class DefaultTransactionContextBuilder implements TransactionContextBuild |
| 30 | @Override | 27 | @Override |
| 31 | public TransactionContext build() { | 28 | public TransactionContext build() { |
| 32 | return new DefaultTransactionContext( | 29 | return new DefaultTransactionContext( |
| 33 | - partitionsEnabled ? partitionedDatabase : inMemoryDatabase, | 30 | + transactionId, |
| 34 | - transactionId); | 31 | + partitionsEnabled ? manager.partitionedDatabase : manager.inMemoryDatabase, |
| 32 | + () -> partitionsEnabled ? manager.consistentMapBuilder() | ||
| 33 | + : manager.consistentMapBuilder().withPartitionsDisabled()); | ||
| 35 | } | 34 | } |
| 36 | - | ||
| 37 | } | 35 | } | ... | ... |
| ... | @@ -50,6 +50,7 @@ public class PartitionedDatabase implements Database { | ... | @@ -50,6 +50,7 @@ public class PartitionedDatabase implements Database { |
| 50 | private final List<Database> partitions; | 50 | private final List<Database> partitions; |
| 51 | private final AtomicBoolean isOpen = new AtomicBoolean(false); | 51 | private final AtomicBoolean isOpen = new AtomicBoolean(false); |
| 52 | private static final String DB_NOT_OPEN = "Partitioned Database is not open"; | 52 | private static final String DB_NOT_OPEN = "Partitioned Database is not open"; |
| 53 | + private TransactionManager transactionManager; | ||
| 53 | 54 | ||
| 54 | public PartitionedDatabase( | 55 | public PartitionedDatabase( |
| 55 | String name, | 56 | String name, |
| ... | @@ -285,7 +286,10 @@ public class PartitionedDatabase implements Database { | ... | @@ -285,7 +286,10 @@ public class PartitionedDatabase implements Database { |
| 285 | subTransactions.entrySet().iterator().next(); | 286 | subTransactions.entrySet().iterator().next(); |
| 286 | return entry.getKey().prepareAndCommit(entry.getValue()); | 287 | return entry.getKey().prepareAndCommit(entry.getValue()); |
| 287 | } else { | 288 | } else { |
| 288 | - return new TransactionManager(this).execute(transaction); | 289 | + if (transactionManager != null) { |
| 290 | + throw new IllegalStateException("TransactionManager is not initialized"); | ||
| 291 | + } | ||
| 292 | + return transactionManager.execute(transaction); | ||
| 289 | } | 293 | } |
| 290 | } | 294 | } |
| 291 | 295 | ||
| ... | @@ -387,4 +391,8 @@ public class PartitionedDatabase implements Database { | ... | @@ -387,4 +391,8 @@ public class PartitionedDatabase implements Database { |
| 387 | perPartitionUpdates.forEach((k, v) -> subTransactions.put(k, new DefaultTransaction(transaction.id(), v))); | 391 | perPartitionUpdates.forEach((k, v) -> subTransactions.put(k, new DefaultTransaction(transaction.id(), v))); |
| 388 | return subTransactions; | 392 | return subTransactions; |
| 389 | } | 393 | } |
| 394 | + | ||
| 395 | + protected void setTransactionManager(TransactionManager tranasactionManager) { | ||
| 396 | + this.transactionManager = transactionManager; | ||
| 397 | + } | ||
| 390 | } | 398 | } | ... | ... |
| ... | @@ -17,6 +17,7 @@ package org.onosproject.store.consistent.impl; | ... | @@ -17,6 +17,7 @@ package org.onosproject.store.consistent.impl; |
| 17 | 17 | ||
| 18 | import static com.google.common.base.Preconditions.checkNotNull; | 18 | import static com.google.common.base.Preconditions.checkNotNull; |
| 19 | 19 | ||
| 20 | +import java.util.Arrays; | ||
| 20 | import java.util.Collection; | 21 | import java.util.Collection; |
| 21 | import java.util.concurrent.CompletableFuture; | 22 | import java.util.concurrent.CompletableFuture; |
| 22 | import java.util.stream.Collectors; | 23 | import java.util.stream.Collectors; |
| ... | @@ -26,6 +27,7 @@ import org.apache.commons.lang3.tuple.Pair; | ... | @@ -26,6 +27,7 @@ import org.apache.commons.lang3.tuple.Pair; |
| 26 | import org.onlab.util.KryoNamespace; | 27 | import org.onlab.util.KryoNamespace; |
| 27 | import org.onosproject.store.serializers.KryoNamespaces; | 28 | import org.onosproject.store.serializers.KryoNamespaces; |
| 28 | import org.onosproject.store.service.AsyncConsistentMap; | 29 | import org.onosproject.store.service.AsyncConsistentMap; |
| 30 | +import org.onosproject.store.service.ConsistentMapBuilder; | ||
| 29 | import org.onosproject.store.service.DatabaseUpdate; | 31 | import org.onosproject.store.service.DatabaseUpdate; |
| 30 | import org.onosproject.store.service.Serializer; | 32 | import org.onosproject.store.service.Serializer; |
| 31 | import org.onosproject.store.service.Transaction; | 33 | import org.onosproject.store.service.Transaction; |
| ... | @@ -49,7 +51,7 @@ public class TransactionManager { | ... | @@ -49,7 +51,7 @@ public class TransactionManager { |
| 49 | .register(ImmutablePair.class) | 51 | .register(ImmutablePair.class) |
| 50 | .build(); | 52 | .build(); |
| 51 | 53 | ||
| 52 | - private final Serializer serializer = Serializer.using(KRYO_NAMESPACE); | 54 | + private final Serializer serializer = Serializer.using(Arrays.asList(KRYO_NAMESPACE)); |
| 53 | private final Database database; | 55 | private final Database database; |
| 54 | private final AsyncConsistentMap<Long, Transaction> transactions; | 56 | private final AsyncConsistentMap<Long, Transaction> transactions; |
| 55 | 57 | ||
| ... | @@ -58,9 +60,11 @@ public class TransactionManager { | ... | @@ -58,9 +60,11 @@ public class TransactionManager { |
| 58 | * | 60 | * |
| 59 | * @param database database | 61 | * @param database database |
| 60 | */ | 62 | */ |
| 61 | - public TransactionManager(Database database) { | 63 | + public TransactionManager(Database database, ConsistentMapBuilder<Long, Transaction> mapBuilder) { |
| 62 | this.database = checkNotNull(database, "database cannot be null"); | 64 | this.database = checkNotNull(database, "database cannot be null"); |
| 63 | - this.transactions = new DefaultAsyncConsistentMap<>("onos-transactions", this.database, serializer, false); | 65 | + this.transactions = mapBuilder.withName("onos-transactions") |
| 66 | + .withSerializer(serializer) | ||
| 67 | + .buildAsyncMap(); | ||
| 64 | } | 68 | } |
| 65 | 69 | ||
| 66 | /** | 70 | /** | ... | ... |
| ... | @@ -170,6 +170,8 @@ import org.onosproject.net.resource.link.MplsLabel; | ... | @@ -170,6 +170,8 @@ import org.onosproject.net.resource.link.MplsLabel; |
| 170 | import org.onosproject.net.resource.link.MplsLabelResourceAllocation; | 170 | import org.onosproject.net.resource.link.MplsLabelResourceAllocation; |
| 171 | import org.onosproject.net.resource.link.MplsLabelResourceRequest; | 171 | import org.onosproject.net.resource.link.MplsLabelResourceRequest; |
| 172 | import org.onosproject.store.Timestamp; | 172 | import org.onosproject.store.Timestamp; |
| 173 | +import org.onosproject.store.service.MapEvent; | ||
| 174 | +import org.onosproject.store.service.SetEvent; | ||
| 173 | import org.onosproject.store.service.Versioned; | 175 | import org.onosproject.store.service.Versioned; |
| 174 | 176 | ||
| 175 | import java.net.URI; | 177 | import java.net.URI; |
| ... | @@ -408,6 +410,10 @@ public final class KryoNamespaces { | ... | @@ -408,6 +410,10 @@ public final class KryoNamespaces { |
| 408 | .register(new HostLocationSerializer(), HostLocation.class) | 410 | .register(new HostLocationSerializer(), HostLocation.class) |
| 409 | .register(new DefaultOutboundPacketSerializer(), DefaultOutboundPacket.class) | 411 | .register(new DefaultOutboundPacketSerializer(), DefaultOutboundPacket.class) |
| 410 | .register(Versioned.class) | 412 | .register(Versioned.class) |
| 413 | + .register(MapEvent.class) | ||
| 414 | + .register(MapEvent.Type.class) | ||
| 415 | + .register(SetEvent.class) | ||
| 416 | + .register(SetEvent.Type.class) | ||
| 411 | .register(DefaultGroupId.class) | 417 | .register(DefaultGroupId.class) |
| 412 | .register(Annotations.class) | 418 | .register(Annotations.class) |
| 413 | .register(OmsPort.class) | 419 | .register(OmsPort.class) | ... | ... |
-
Please register or login to post a comment