Committed by
Brian O'Connor
Support for a distributed queue primitive.
Change-Id: I13abb93ec1703105ff0137e137738483a5b6a143
Showing
13 changed files
with
547 additions
and
4 deletions
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()) { | ... | ... |
core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDistributedQueue.java
0 → 100644
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); | ... | ... |
-
Please register or login to post a comment