Madan Jampani
Committed by Brian O'Connor

Support for a distributed queue primitive.

Change-Id: I13abb93ec1703105ff0137e137738483a5b6a143
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.store.service;
17 +
18 +import java.util.concurrent.CompletableFuture;
19 +
20 +/**
21 + * A distributed collection designed for holding elements prior to processing.
22 + * A queue provides insertion, extraction and inspection operations. The extraction operation
23 + * is designed to be non-blocking.
24 + *
25 + * @param <E> queue entry type
26 + */
27 +public interface DistributedQueue<E> {
28 +
29 + /**
30 + * Returns total number of entries in the queue.
31 + * @return queue size
32 + */
33 + long size();
34 +
35 + /**
36 + * Returns true if queue has elements in it.
37 + * @return true is queue has elements, false otherwise
38 + */
39 + default boolean isEmpty() {
40 + return size() == 0;
41 + }
42 +
43 + /**
44 + * Inserts an entry into the queue.
45 + * @param entry entry to insert
46 + */
47 + void push(E entry);
48 +
49 + /**
50 + * If the queue is non-empty, an entry will be removed from the queue and the returned future
51 + * will be immediately completed with it. If queue is empty when this call is made, the returned
52 + * future will be eventually completed when an entry is added to the queue.
53 + * @return queue entry
54 + */
55 + CompletableFuture<E> pop();
56 +
57 + /**
58 + * Returns an entry from the queue without removing it. If the queue is empty returns null.
59 + * @return queue entry or null if queue is empty
60 + */
61 + E peek();
62 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.store.service;
17 +
18 +/**
19 + * Builder for distributed queue.
20 + *
21 + * @param <E> type queue elements.
22 + */
23 +public interface DistributedQueueBuilder<E> {
24 +
25 + /**
26 + * Sets the name of the queue.
27 + * <p>
28 + * Each queue is identified by a unique name.
29 + * </p>
30 + * <p>
31 + * Note: This is a mandatory parameter.
32 + * </p>
33 + *
34 + * @param name name of the queue
35 + * @return this DistributedQueueBuilder for method chaining
36 + */
37 + DistributedQueueBuilder<E> withName(String name);
38 +
39 + /**
40 + * Sets a serializer that can be used to serialize
41 + * the elements pushed into the queue. The serializer
42 + * builder should be pre-populated with any classes that will be
43 + * put into the queue.
44 + * <p>
45 + * Note: This is a mandatory parameter.
46 + * </p>
47 + *
48 + * @param serializer serializer
49 + * @return this DistributedQueueBuilder for method chaining
50 + */
51 + DistributedQueueBuilder<E> withSerializer(Serializer serializer);
52 +
53 + /**
54 + * Disables persistence of queues entries.
55 + * <p>
56 + * When persistence is disabled, a full cluster restart will wipe out all
57 + * queue entries.
58 + * </p>
59 + * @return this DistributedQueueBuilder for method chaining
60 + */
61 + DistributedQueueBuilder<E> withPersistenceDisabled();
62 +
63 + /**
64 + * Builds a queue based on the configuration options
65 + * supplied to this builder.
66 + *
67 + * @return new distributed queue
68 + * @throws java.lang.RuntimeException if a mandatory parameter is missing
69 + */
70 + DistributedQueue<E> build();
71 +}
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
1 package org.onosproject.store.service; 16 package org.onosproject.store.service;
2 17
3 import java.util.Set; 18 import java.util.Set;
......
...@@ -56,6 +56,14 @@ public interface StorageService { ...@@ -56,6 +56,14 @@ public interface StorageService {
56 <E> DistributedSetBuilder<E> setBuilder(); 56 <E> DistributedSetBuilder<E> setBuilder();
57 57
58 /** 58 /**
59 + * Creates a new distributed queue builder.
60 + *
61 + * @param <E> queue entry type
62 + * @return builder for an distributed queue
63 + */
64 + <E> DistributedQueueBuilder<E> queueBuilder();
65 +
66 + /**
59 * Creates a new AtomicCounterBuilder. 67 * Creates a new AtomicCounterBuilder.
60 * 68 *
61 * @return atomic counter builder 69 * @return atomic counter builder
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 16
17 package org.onosproject.store.consistent.impl; 17 package org.onosproject.store.consistent.impl;
18 18
19 +import com.google.common.base.Charsets;
19 import com.google.common.collect.ImmutableSet; 20 import com.google.common.collect.ImmutableSet;
20 import com.google.common.collect.Lists; 21 import com.google.common.collect.Lists;
21 import com.google.common.collect.Maps; 22 import com.google.common.collect.Maps;
...@@ -46,6 +47,7 @@ import org.apache.felix.scr.annotations.Service; ...@@ -46,6 +47,7 @@ import org.apache.felix.scr.annotations.Service;
46 import static org.onlab.util.Tools.groupedThreads; 47 import static org.onlab.util.Tools.groupedThreads;
47 48
48 import org.onosproject.cluster.ClusterService; 49 import org.onosproject.cluster.ClusterService;
50 +import org.onosproject.cluster.NodeId;
49 import org.onosproject.core.IdGenerator; 51 import org.onosproject.core.IdGenerator;
50 import org.onosproject.store.cluster.impl.ClusterDefinitionManager; 52 import org.onosproject.store.cluster.impl.ClusterDefinitionManager;
51 import org.onosproject.store.cluster.impl.NodeInfo; 53 import org.onosproject.store.cluster.impl.NodeInfo;
...@@ -55,6 +57,7 @@ import org.onosproject.store.ecmap.EventuallyConsistentMapBuilderImpl; ...@@ -55,6 +57,7 @@ import org.onosproject.store.ecmap.EventuallyConsistentMapBuilderImpl;
55 import org.onosproject.store.service.AtomicCounterBuilder; 57 import org.onosproject.store.service.AtomicCounterBuilder;
56 import org.onosproject.store.service.ConsistentMapBuilder; 58 import org.onosproject.store.service.ConsistentMapBuilder;
57 import org.onosproject.store.service.ConsistentMapException; 59 import org.onosproject.store.service.ConsistentMapException;
60 +import org.onosproject.store.service.DistributedQueueBuilder;
58 import org.onosproject.store.service.EventuallyConsistentMapBuilder; 61 import org.onosproject.store.service.EventuallyConsistentMapBuilder;
59 import org.onosproject.store.service.MapEvent; 62 import org.onosproject.store.service.MapEvent;
60 import org.onosproject.store.service.MapInfo; 63 import org.onosproject.store.service.MapInfo;
...@@ -98,16 +101,21 @@ public class DatabaseManager implements StorageService, StorageAdminService { ...@@ -98,16 +101,21 @@ public class DatabaseManager implements StorageService, StorageAdminService {
98 private static final int RAFT_ELECTION_TIMEOUT_MILLIS = 3000; 101 private static final int RAFT_ELECTION_TIMEOUT_MILLIS = 3000;
99 private static final int DATABASE_OPERATION_TIMEOUT_MILLIS = 5000; 102 private static final int DATABASE_OPERATION_TIMEOUT_MILLIS = 5000;
100 103
104 + protected static final MessageSubject QUEUE_UPDATED_TOPIC = new MessageSubject("distributed-queue-updated");
105 +
101 private ClusterCoordinator coordinator; 106 private ClusterCoordinator coordinator;
102 protected PartitionedDatabase partitionedDatabase; 107 protected PartitionedDatabase partitionedDatabase;
103 protected Database inMemoryDatabase; 108 protected Database inMemoryDatabase;
109 + protected NodeId localNodeId;
104 110
105 private TransactionManager transactionManager; 111 private TransactionManager transactionManager;
106 private final IdGenerator transactionIdGenerator = () -> RandomUtils.nextLong(); 112 private final IdGenerator transactionIdGenerator = () -> RandomUtils.nextLong();
107 113
108 private ExecutorService eventDispatcher; 114 private ExecutorService eventDispatcher;
115 + private ExecutorService queuePollExecutor;
109 116
110 - private final Set<DefaultAsyncConsistentMap> maps = Sets.newCopyOnWriteArraySet(); 117 + private final Map<String, DefaultAsyncConsistentMap> maps = Maps.newConcurrentMap();
118 + private final Map<String, DefaultDistributedQueue> queues = Maps.newConcurrentMap();
111 119
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected ClusterService clusterService; 121 protected ClusterService clusterService;
...@@ -121,6 +129,7 @@ public class DatabaseManager implements StorageService, StorageAdminService { ...@@ -121,6 +129,7 @@ public class DatabaseManager implements StorageService, StorageAdminService {
121 129
122 @Activate 130 @Activate
123 public void activate() { 131 public void activate() {
132 + localNodeId = clusterService.getLocalNode().id();
124 // load database configuration 133 // load database configuration
125 File databaseDefFile = new File(PARTITION_DEFINITION_FILE); 134 File databaseDefFile = new File(PARTITION_DEFINITION_FILE);
126 log.info("Loading database definition: {}", databaseDefFile.getAbsolutePath()); 135 log.info("Loading database definition: {}", databaseDefFile.getAbsolutePath());
...@@ -201,6 +210,19 @@ public class DatabaseManager implements StorageService, StorageAdminService { ...@@ -201,6 +210,19 @@ public class DatabaseManager implements StorageService, StorageAdminService {
201 210
202 eventDispatcher = Executors.newSingleThreadExecutor( 211 eventDispatcher = Executors.newSingleThreadExecutor(
203 groupedThreads("onos/store/manager", "map-event-dispatcher")); 212 groupedThreads("onos/store/manager", "map-event-dispatcher"));
213 +
214 + queuePollExecutor = Executors.newFixedThreadPool(4,
215 + groupedThreads("onos/store/manager", "queue-poll-handler"));
216 +
217 + clusterCommunicator.<String>addSubscriber(QUEUE_UPDATED_TOPIC,
218 + data -> new String(data, Charsets.UTF_8),
219 + name -> {
220 + DefaultDistributedQueue q = queues.get(name);
221 + if (q != null) {
222 + q.tryPoll();
223 + }
224 + },
225 + queuePollExecutor);
204 log.info("Started"); 226 log.info("Started");
205 } 227 }
206 228
...@@ -226,8 +248,10 @@ public class DatabaseManager implements StorageService, StorageAdminService { ...@@ -226,8 +248,10 @@ public class DatabaseManager implements StorageService, StorageAdminService {
226 log.info("Successfully closed databases."); 248 log.info("Successfully closed databases.");
227 } 249 }
228 }); 250 });
229 - maps.forEach(map -> clusterCommunicator.removeSubscriber(mapUpdatesSubject(map.name()))); 251 + clusterCommunicator.removeSubscriber(QUEUE_UPDATED_TOPIC);
252 + maps.values().forEach(this::unregisterMap);
230 eventDispatcher.shutdown(); 253 eventDispatcher.shutdown();
254 + queuePollExecutor.shutdown();
231 log.info("Stopped"); 255 log.info("Stopped");
232 } 256 }
233 257
...@@ -318,6 +342,12 @@ public class DatabaseManager implements StorageService, StorageAdminService { ...@@ -318,6 +342,12 @@ public class DatabaseManager implements StorageService, StorageAdminService {
318 return new DefaultDistributedSetBuilder<>(this); 342 return new DefaultDistributedSetBuilder<>(this);
319 } 343 }
320 344
345 +
346 + @Override
347 + public <E> DistributedQueueBuilder<E> queueBuilder() {
348 + return new DefaultDistributedQueueBuilder<>(this);
349 + }
350 +
321 @Override 351 @Override
322 public AtomicCounterBuilder atomicCounterBuilder() { 352 public AtomicCounterBuilder atomicCounterBuilder() {
323 return new DefaultAtomicCounterBuilder(inMemoryDatabase, partitionedDatabase); 353 return new DefaultAtomicCounterBuilder(inMemoryDatabase, partitionedDatabase);
...@@ -386,8 +416,8 @@ public class DatabaseManager implements StorageService, StorageAdminService { ...@@ -386,8 +416,8 @@ public class DatabaseManager implements StorageService, StorageAdminService {
386 } 416 }
387 417
388 protected <K, V> void registerMap(DefaultAsyncConsistentMap<K, V> map) { 418 protected <K, V> void registerMap(DefaultAsyncConsistentMap<K, V> map) {
389 - // TODO: Support different local instances of the same map. 419 + // TODO: Support multiple local instances of the same map.
390 - if (!maps.add(map)) { 420 + if (maps.putIfAbsent(map.name(), map) != null) {
391 throw new IllegalStateException("Map by name " + map.name() + " already exists"); 421 throw new IllegalStateException("Map by name " + map.name() + " already exists");
392 } 422 }
393 423
...@@ -397,6 +427,19 @@ public class DatabaseManager implements StorageService, StorageAdminService { ...@@ -397,6 +427,19 @@ public class DatabaseManager implements StorageService, StorageAdminService {
397 eventDispatcher); 427 eventDispatcher);
398 } 428 }
399 429
430 + protected <K, V> void unregisterMap(DefaultAsyncConsistentMap<K, V> map) {
431 + if (maps.remove(map.name()) != null) {
432 + clusterCommunicator.removeSubscriber(mapUpdatesSubject(map.name()));
433 + }
434 + }
435 +
436 + protected <E> void registerQueue(DefaultDistributedQueue<E> queue) {
437 + // TODO: Support multiple local instances of the same queue.
438 + if (queues.putIfAbsent(queue.name(), queue) != null) {
439 + throw new IllegalStateException("Queue by name " + queue.name() + " already exists");
440 + }
441 + }
442 +
400 protected static MessageSubject mapUpdatesSubject(String mapName) { 443 protected static MessageSubject mapUpdatesSubject(String mapName) {
401 return new MessageSubject(mapName + "-map-updates"); 444 return new MessageSubject(mapName + "-map-updates");
402 } 445 }
......
...@@ -21,6 +21,7 @@ import java.util.Map; ...@@ -21,6 +21,7 @@ import java.util.Map;
21 import java.util.Set; 21 import java.util.Set;
22 import java.util.concurrent.CompletableFuture; 22 import java.util.concurrent.CompletableFuture;
23 23
24 +import org.onosproject.cluster.NodeId;
24 import org.onosproject.store.service.Transaction; 25 import org.onosproject.store.service.Transaction;
25 import org.onosproject.store.service.Versioned; 26 import org.onosproject.store.service.Versioned;
26 27
...@@ -249,6 +250,36 @@ public interface DatabaseProxy<K, V> { ...@@ -249,6 +250,36 @@ public interface DatabaseProxy<K, V> {
249 CompletableFuture<Long> counterGet(String counterName); 250 CompletableFuture<Long> counterGet(String counterName);
250 251
251 /** 252 /**
253 + * Returns the size of queue.
254 + * @param queueName queue name
255 + * @return queue size
256 + */
257 + CompletableFuture<Long> queueSize(String queueName);
258 +
259 + /**
260 + * Inserts an entry into the queue.
261 + * @param queueName queue name
262 + * @param entry queue entry
263 + * @return set of nodes to notify about the queue update
264 + */
265 + CompletableFuture<Set<NodeId>> queuePush(String queueName, byte[] entry);
266 +
267 + /**
268 + * Removes an entry from the queue if the queue is non-empty.
269 + * @param queueName queue name
270 + * @param nodeId If the queue is empty the identifier of node to notify when an entry becomes available
271 + * @return entry. Can be null if queue is empty
272 + */
273 + CompletableFuture<byte[]> queuePop(String queueName, NodeId nodeId);
274 +
275 + /**
276 + * Returns but does not remove an entry from the queue.
277 + * @param queueName queue name
278 + * @return entry. Can be null if queue is empty
279 + */
280 + CompletableFuture<byte[]> queuePeek(String queueName);
281 +
282 + /**
252 * Prepare and commit the specified transaction. 283 * Prepare and commit the specified transaction.
253 * 284 *
254 * @param transaction transaction to commit (after preparation) 285 * @param transaction transaction to commit (after preparation)
......
...@@ -21,6 +21,7 @@ import java.nio.ByteBuffer; ...@@ -21,6 +21,7 @@ import java.nio.ByteBuffer;
21 import org.apache.commons.lang3.tuple.ImmutablePair; 21 import org.apache.commons.lang3.tuple.ImmutablePair;
22 import org.apache.commons.lang3.tuple.Pair; 22 import org.apache.commons.lang3.tuple.Pair;
23 import org.onlab.util.KryoNamespace; 23 import org.onlab.util.KryoNamespace;
24 +import org.onosproject.cluster.NodeId;
24 import org.onosproject.store.serializers.KryoNamespaces; 25 import org.onosproject.store.serializers.KryoNamespaces;
25 import org.onosproject.store.serializers.KryoSerializer; 26 import org.onosproject.store.serializers.KryoSerializer;
26 import org.onosproject.store.service.DatabaseUpdate; 27 import org.onosproject.store.service.DatabaseUpdate;
...@@ -78,6 +79,7 @@ public class DatabaseSerializer extends SerializerConfig { ...@@ -78,6 +79,7 @@ public class DatabaseSerializer extends SerializerConfig {
78 .register(Result.Status.class) 79 .register(Result.Status.class)
79 .register(DefaultTransaction.class) 80 .register(DefaultTransaction.class)
80 .register(Transaction.State.class) 81 .register(Transaction.State.class)
82 + .register(NodeId.class)
81 .build(); 83 .build();
82 84
83 private static final KryoSerializer SERIALIZER = new KryoSerializer() { 85 private static final KryoSerializer SERIALIZER = new KryoSerializer() {
......
...@@ -21,6 +21,7 @@ import java.util.Map; ...@@ -21,6 +21,7 @@ import java.util.Map;
21 import java.util.Map.Entry; 21 import java.util.Map.Entry;
22 import java.util.Set; 22 import java.util.Set;
23 23
24 +import org.onosproject.cluster.NodeId;
24 import org.onosproject.store.service.Transaction; 25 import org.onosproject.store.service.Transaction;
25 import org.onosproject.store.service.Versioned; 26 import org.onosproject.store.service.Versioned;
26 27
...@@ -113,6 +114,18 @@ public interface DatabaseState<K, V> { ...@@ -113,6 +114,18 @@ public interface DatabaseState<K, V> {
113 Long counterGetAndAdd(String counterName, long delta); 114 Long counterGetAndAdd(String counterName, long delta);
114 115
115 @Query 116 @Query
117 + Long queueSize(String queueName);
118 +
119 + @Query
120 + byte[] queuePeek(String queueName);
121 +
122 + @Command
123 + byte[] queuePop(String queueName, NodeId requestor);
124 +
125 + @Command
126 + Set<NodeId> queuePush(String queueName, byte[] entry);
127 +
128 + @Query
116 Long counterGet(String counterName); 129 Long counterGet(String counterName);
117 130
118 @Command 131 @Command
......
...@@ -28,6 +28,7 @@ import java.util.Set; ...@@ -28,6 +28,7 @@ import java.util.Set;
28 import java.util.concurrent.CompletableFuture; 28 import java.util.concurrent.CompletableFuture;
29 import java.util.function.Supplier; 29 import java.util.function.Supplier;
30 30
31 +import org.onosproject.cluster.NodeId;
31 import org.onosproject.store.service.Transaction; 32 import org.onosproject.store.service.Transaction;
32 import org.onosproject.store.service.Versioned; 33 import org.onosproject.store.service.Versioned;
33 34
...@@ -187,6 +188,26 @@ public class DefaultDatabase extends AbstractResource<Database> implements Datab ...@@ -187,6 +188,26 @@ public class DefaultDatabase extends AbstractResource<Database> implements Datab
187 } 188 }
188 189
189 @Override 190 @Override
191 + public CompletableFuture<Long> queueSize(String queueName) {
192 + return checkOpen(() -> proxy.queueSize(queueName));
193 + }
194 +
195 + @Override
196 + public CompletableFuture<Set<NodeId>> queuePush(String queueName, byte[] entry) {
197 + return checkOpen(() -> proxy.queuePush(queueName, entry));
198 + }
199 +
200 + @Override
201 + public CompletableFuture<byte[]> queuePop(String queueName, NodeId nodeId) {
202 + return checkOpen(() -> proxy.queuePop(queueName, nodeId));
203 + }
204 +
205 + @Override
206 + public CompletableFuture<byte[]> queuePeek(String queueName) {
207 + return checkOpen(() -> proxy.queuePeek(queueName));
208 + }
209 +
210 + @Override
190 public CompletableFuture<Boolean> prepareAndCommit(Transaction transaction) { 211 public CompletableFuture<Boolean> prepareAndCommit(Transaction transaction) {
191 return checkOpen(() -> proxy.prepareAndCommit(transaction)); 212 return checkOpen(() -> proxy.prepareAndCommit(transaction));
192 } 213 }
......
...@@ -19,13 +19,16 @@ package org.onosproject.store.consistent.impl; ...@@ -19,13 +19,16 @@ package org.onosproject.store.consistent.impl;
19 import java.util.Arrays; 19 import java.util.Arrays;
20 import java.util.Collection; 20 import java.util.Collection;
21 import java.util.HashSet; 21 import java.util.HashSet;
22 +import java.util.LinkedList;
22 import java.util.Map; 23 import java.util.Map;
23 import java.util.Map.Entry; 24 import java.util.Map.Entry;
25 +import java.util.Queue;
24 import java.util.concurrent.atomic.AtomicLong; 26 import java.util.concurrent.atomic.AtomicLong;
25 import java.util.stream.Collectors; 27 import java.util.stream.Collectors;
26 import java.util.Set; 28 import java.util.Set;
27 29
28 import org.apache.commons.lang3.tuple.Pair; 30 import org.apache.commons.lang3.tuple.Pair;
31 +import org.onosproject.cluster.NodeId;
29 import org.onosproject.store.service.DatabaseUpdate; 32 import org.onosproject.store.service.DatabaseUpdate;
30 import org.onosproject.store.service.Transaction; 33 import org.onosproject.store.service.Transaction;
31 import org.onosproject.store.service.Versioned; 34 import org.onosproject.store.service.Versioned;
...@@ -46,6 +49,8 @@ public class DefaultDatabaseState implements DatabaseState<String, byte[]> { ...@@ -46,6 +49,8 @@ public class DefaultDatabaseState implements DatabaseState<String, byte[]> {
46 private Long nextVersion; 49 private Long nextVersion;
47 private Map<String, AtomicLong> counters; 50 private Map<String, AtomicLong> counters;
48 private Map<String, Map<String, Versioned<byte[]>>> tables; 51 private Map<String, Map<String, Versioned<byte[]>>> tables;
52 + private Map<String, Queue<byte[]>> queues;
53 + private Map<String, Set<NodeId>> queueUpdateNotificationTargets;
49 54
50 /** 55 /**
51 * This locks map has a structure similar to the "tables" map above and 56 * This locks map has a structure similar to the "tables" map above and
...@@ -77,6 +82,16 @@ public class DefaultDatabaseState implements DatabaseState<String, byte[]> { ...@@ -77,6 +82,16 @@ public class DefaultDatabaseState implements DatabaseState<String, byte[]> {
77 locks = Maps.newConcurrentMap(); 82 locks = Maps.newConcurrentMap();
78 context.put("locks", locks); 83 context.put("locks", locks);
79 } 84 }
85 + queues = context.get("queues");
86 + if (queues == null) {
87 + queues = Maps.newConcurrentMap();
88 + context.put("queues", queues);
89 + }
90 + queueUpdateNotificationTargets = context.get("queueUpdateNotificationTargets");
91 + if (queueUpdateNotificationTargets == null) {
92 + queueUpdateNotificationTargets = Maps.newConcurrentMap();
93 + context.put("queueUpdateNotificationTargets", queueUpdateNotificationTargets);
94 + }
80 nextVersion = context.get("nextVersion"); 95 nextVersion = context.get("nextVersion");
81 if (nextVersion == null) { 96 if (nextVersion == null) {
82 nextVersion = new Long(0); 97 nextVersion = new Long(0);
...@@ -288,6 +303,36 @@ public class DefaultDatabaseState implements DatabaseState<String, byte[]> { ...@@ -288,6 +303,36 @@ public class DefaultDatabaseState implements DatabaseState<String, byte[]> {
288 } 303 }
289 304
290 @Override 305 @Override
306 + public Long queueSize(String queueName) {
307 + return Long.valueOf(getQueue(queueName).size());
308 + }
309 +
310 + @Override
311 + public byte[] queuePeek(String queueName) {
312 + Queue<byte[]> queue = getQueue(queueName);
313 + return queue.peek();
314 + }
315 +
316 + @Override
317 + public byte[] queuePop(String queueName, NodeId requestor) {
318 + Queue<byte[]> queue = getQueue(queueName);
319 + if (queue.size() == 0 && requestor != null) {
320 + getQueueUpdateNotificationTargets(queueName).add(requestor);
321 + return null;
322 + } else {
323 + return queue.remove();
324 + }
325 + }
326 +
327 + @Override
328 + public Set<NodeId> queuePush(String queueName, byte[] entry) {
329 + getQueue(queueName).add(entry);
330 + Set<NodeId> notifyList = ImmutableSet.copyOf(getQueueUpdateNotificationTargets(queueName));
331 + getQueueUpdateNotificationTargets(queueName).clear();
332 + return notifyList;
333 + }
334 +
335 + @Override
291 public boolean prepareAndCommit(Transaction transaction) { 336 public boolean prepareAndCommit(Transaction transaction) {
292 if (prepare(transaction)) { 337 if (prepare(transaction)) {
293 return commit(transaction); 338 return commit(transaction);
...@@ -335,6 +380,14 @@ public class DefaultDatabaseState implements DatabaseState<String, byte[]> { ...@@ -335,6 +380,14 @@ public class DefaultDatabaseState implements DatabaseState<String, byte[]> {
335 return counters.computeIfAbsent(counterName, name -> new AtomicLong(0)); 380 return counters.computeIfAbsent(counterName, name -> new AtomicLong(0));
336 } 381 }
337 382
383 + private Queue<byte[]> getQueue(String queueName) {
384 + return queues.computeIfAbsent(queueName, name -> new LinkedList<>());
385 + }
386 +
387 + private Set<NodeId> getQueueUpdateNotificationTargets(String queueName) {
388 + return queueUpdateNotificationTargets.computeIfAbsent(queueName, name -> new HashSet<>());
389 + }
390 +
338 private boolean isUpdatePossible(DatabaseUpdate update) { 391 private boolean isUpdatePossible(DatabaseUpdate update) {
339 Versioned<byte[]> existingEntry = get(update.tableName(), update.key()); 392 Versioned<byte[]> existingEntry = get(update.tableName(), update.key());
340 switch (update.type()) { 393 switch (update.type()) {
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.store.consistent.impl;
17 +
18 +import static com.google.common.base.Preconditions.checkNotNull;
19 +
20 +import java.util.Set;
21 +import java.util.concurrent.CompletableFuture;
22 +import java.util.function.Consumer;
23 +
24 +import org.onosproject.cluster.NodeId;
25 +import org.onosproject.store.service.DistributedQueue;
26 +import org.onosproject.store.service.Serializer;
27 +
28 +import com.google.common.collect.Sets;
29 +import com.google.common.util.concurrent.Futures;
30 +
31 +/**
32 + * DistributedQueue implementation that provides FIFO ordering semantics.
33 + *
34 + * @param <E> queue entry type
35 + */
36 +public class DefaultDistributedQueue<E> implements DistributedQueue<E> {
37 +
38 + private final String name;
39 + private final Database database;
40 + private final Serializer serializer;
41 + private final NodeId localNodeId;
42 + private final Set<CompletableFuture<E>> pendingFutures = Sets.newIdentityHashSet();
43 + private final Consumer<Set<NodeId>> notifyConsumers;
44 +
45 + private static final String ERROR_NULL_ENTRY = "Null entries are not allowed";
46 +
47 + public DefaultDistributedQueue(String name,
48 + Database database,
49 + Serializer serializer,
50 + NodeId localNodeId,
51 + Consumer<Set<NodeId>> notifyConsumers) {
52 + this.name = checkNotNull(name, "queue name cannot be null");
53 + this.database = checkNotNull(database, "database cannot be null");
54 + this.serializer = checkNotNull(serializer, "serializer cannot be null");
55 + this.localNodeId = localNodeId;
56 + this.notifyConsumers = notifyConsumers;
57 + }
58 +
59 + @Override
60 + public long size() {
61 + return Futures.getUnchecked(database.queueSize(name));
62 + }
63 +
64 + @Override
65 + public void push(E entry) {
66 + checkNotNull(entry, ERROR_NULL_ENTRY);
67 + Futures.getUnchecked(database.queuePush(name, serializer.encode(entry))
68 + .thenAccept(notifyConsumers)
69 + .thenApply(v -> null));
70 + }
71 +
72 + @Override
73 + public CompletableFuture<E> pop() {
74 + return database.queuePop(name, localNodeId)
75 + .thenCompose(v -> {
76 + if (v != null) {
77 + return CompletableFuture.completedFuture(serializer.decode(v));
78 + } else {
79 + CompletableFuture<E> newPendingFuture = new CompletableFuture<>();
80 + pendingFutures.add(newPendingFuture);
81 + return newPendingFuture;
82 + }
83 + });
84 + }
85 +
86 + @Override
87 + public E peek() {
88 + return Futures.getUnchecked(database.queuePeek(name)
89 + .thenApply(v -> v != null ? serializer.decode(v) : null));
90 + }
91 +
92 + public String name() {
93 + return name;
94 + }
95 +
96 + protected void tryPoll() {
97 + Set<CompletableFuture<E>> completedFutures = Sets.newHashSet();
98 + for (CompletableFuture<E> future : pendingFutures) {
99 + E entry = Futures.getUnchecked(database.queuePop(name, localNodeId)
100 + .thenApply(v -> v != null ? serializer.decode(v) : null));
101 + if (entry != null) {
102 + future.complete(entry);
103 + completedFutures.add(future);
104 + } else {
105 + break;
106 + }
107 + }
108 + pendingFutures.removeAll(completedFutures);
109 + }
110 +}
...\ No newline at end of file ...\ No newline at end of file
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onosproject.store.consistent.impl;
17 +
18 +import static com.google.common.base.Preconditions.checkArgument;
19 +import static com.google.common.base.Preconditions.checkState;
20 +
21 +import java.util.Set;
22 +import java.util.function.Consumer;
23 +
24 +import org.onosproject.cluster.NodeId;
25 +import org.onosproject.store.service.DistributedQueue;
26 +import org.onosproject.store.service.DistributedQueueBuilder;
27 +import org.onosproject.store.service.Serializer;
28 +
29 +import com.google.common.base.Charsets;
30 +
31 +/**
32 + * Default implementation of a {@code DistributedQueueBuilder}.
33 + *
34 + * @param <E> queue entry type
35 + */
36 +public class DefaultDistributedQueueBuilder<E> implements DistributedQueueBuilder<E> {
37 +
38 + private Serializer serializer;
39 + private String name;
40 + private boolean persistenceEnabled = true;
41 + private final DatabaseManager databaseManager;
42 +
43 + public DefaultDistributedQueueBuilder(
44 + DatabaseManager databaseManager) {
45 + this.databaseManager = databaseManager;
46 + }
47 +
48 + @Override
49 + public DistributedQueueBuilder<E> withName(String name) {
50 + checkArgument(name != null && !name.isEmpty());
51 + this.name = name;
52 + return this;
53 + }
54 +
55 + @Override
56 + public DistributedQueueBuilder<E> withSerializer(Serializer serializer) {
57 + checkArgument(serializer != null);
58 + this.serializer = serializer;
59 + return this;
60 + }
61 +
62 + @Override
63 + public DistributedQueueBuilder<E> withPersistenceDisabled() {
64 + persistenceEnabled = false;
65 + return this;
66 + }
67 +
68 + private boolean validInputs() {
69 + return name != null && serializer != null;
70 + }
71 +
72 + @Override
73 + public DistributedQueue<E> build() {
74 + checkState(validInputs());
75 + Consumer<Set<NodeId>> notifyOthers = nodes -> databaseManager.clusterCommunicator.multicast(name,
76 + DatabaseManager.QUEUE_UPDATED_TOPIC,
77 + s -> s.getBytes(Charsets.UTF_8),
78 + nodes);
79 + DefaultDistributedQueue<E> queue = new DefaultDistributedQueue<>(
80 + name,
81 + persistenceEnabled ? databaseManager.partitionedDatabase : databaseManager.inMemoryDatabase,
82 + serializer,
83 + databaseManager.localNodeId,
84 + notifyOthers);
85 + databaseManager.registerQueue(queue);
86 + return queue;
87 + }
88 +}
...@@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
27 import java.util.concurrent.atomic.AtomicInteger; 27 import java.util.concurrent.atomic.AtomicInteger;
28 import java.util.stream.Collectors; 28 import java.util.stream.Collectors;
29 29
30 +import org.onosproject.cluster.NodeId;
30 import org.onosproject.store.service.DatabaseUpdate; 31 import org.onosproject.store.service.DatabaseUpdate;
31 import org.onosproject.store.service.Transaction; 32 import org.onosproject.store.service.Transaction;
32 import org.onosproject.store.service.Versioned; 33 import org.onosproject.store.service.Versioned;
...@@ -276,6 +277,31 @@ public class PartitionedDatabase implements Database { ...@@ -276,6 +277,31 @@ public class PartitionedDatabase implements Database {
276 return partitioner.getPartition(counterName, counterName).counterGetAndAdd(counterName, delta); 277 return partitioner.getPartition(counterName, counterName).counterGetAndAdd(counterName, delta);
277 } 278 }
278 279
280 +
281 + @Override
282 + public CompletableFuture<Long> queueSize(String queueName) {
283 + checkState(isOpen.get(), DB_NOT_OPEN);
284 + return partitioner.getPartition(queueName, queueName).queueSize(queueName);
285 + }
286 +
287 + @Override
288 + public CompletableFuture<Set<NodeId>> queuePush(String queueName, byte[] entry) {
289 + checkState(isOpen.get(), DB_NOT_OPEN);
290 + return partitioner.getPartition(queueName, queueName).queuePush(queueName, entry);
291 + }
292 +
293 + @Override
294 + public CompletableFuture<byte[]> queuePop(String queueName, NodeId nodeId) {
295 + checkState(isOpen.get(), DB_NOT_OPEN);
296 + return partitioner.getPartition(queueName, queueName).queuePop(queueName, nodeId);
297 + }
298 +
299 + @Override
300 + public CompletableFuture<byte[]> queuePeek(String queueName) {
301 + checkState(isOpen.get(), DB_NOT_OPEN);
302 + return partitioner.getPartition(queueName, queueName).queuePeek(queueName);
303 + }
304 +
279 @Override 305 @Override
280 public CompletableFuture<Boolean> prepareAndCommit(Transaction transaction) { 306 public CompletableFuture<Boolean> prepareAndCommit(Transaction transaction) {
281 Map<Database, Transaction> subTransactions = createSubTransactions(transaction); 307 Map<Database, Transaction> subTransactions = createSubTransactions(transaction);
......