From 2bf177cfa3fca0df86733043c8ee44aaac87aa52 Mon Sep 17 00:00:00 2001 From: Jordan Halterman Date: Thu, 29 Jun 2017 01:49:08 -0700 Subject: [PATCH] [ONOS-6594] Upgrade to Atomix 2.0.0 Change-Id: I6534bca1c8570b4e017f682953b876da29146675 --- .../DistributedPrimitivesTest.java | 15 + .../cli/LeaderElectorTestCommand.java | 9 +- .../cli/net/PartitionsListCommand.java | 13 +- .../DistributedPrimitiveBuilder.java | 38 - .../DistributedPrimitiveCreator.java | 177 +---- .../store/service/PartitionClientInfo.java | 14 +- core/store/primitives/BUCK | 4 +- core/store/primitives/pom.xml | 12 +- .../primitives/impl/CatalystSerializers.java | 137 ---- .../primitives/impl/CopycatTransport.java | 86 --- .../impl/CopycatTransportClient.java | 120 --- .../impl/CopycatTransportConnection.java | 411 ---------- .../impl/CopycatTransportServer.java | 113 --- .../impl/DefaultAtomicCounterBuilder.java | 2 +- .../impl/DefaultAtomicCounterMapBuilder.java | 2 +- .../impl/DefaultAtomicIdGeneratorBuilder.java | 2 +- .../impl/DefaultAtomicValueBuilder.java | 7 - .../DefaultCatalystTypeSerializerFactory.java | 81 -- .../impl/DefaultConsistentMapBuilder.java | 2 +- .../DefaultConsistentMultimapBuilder.java | 2 +- .../impl/DefaultConsistentTreeMapBuilder.java | 2 +- .../impl/DefaultDistributedSetBuilder.java | 7 - .../impl/DefaultDocumentTreeBuilder.java | 2 +- .../impl/DefaultLeaderElectorBuilder.java | 2 +- .../impl/DelegatingCopycatClient.java | 108 --- .../impl/ExecutingAsyncAtomicCounter.java | 70 -- .../impl/ExecutingAsyncAtomicCounterMap.java | 111 --- .../impl/ExecutingAsyncAtomicIdGenerator.java | 40 - .../impl/ExecutingAsyncAtomicValue.java | 77 -- .../impl/ExecutingAsyncConsistentMap.java | 183 ----- .../ExecutingAsyncConsistentMultimap.java | 142 ---- .../impl/ExecutingAsyncConsistentTreeMap.java | 267 ------- .../impl/ExecutingAsyncDocumentTree.java | 104 --- .../impl/ExecutingAsyncLeaderElector.java | 95 --- .../impl/ExecutingDistributedPrimitive.java | 78 -- .../primitives/impl/ExecutingWorkQueue.java | 70 -- .../FederatedDistributedPrimitiveCreator.java | 53 +- .../primitives/impl/OnosCopycatClient.java | 102 --- .../primitives/impl/PartitionManager.java | 22 +- .../impl/RaftClientCommunicator.java | 112 +++ .../primitives/impl/RaftCommunicator.java | 71 ++ .../primitives/impl/RaftMessageContext.java | 93 +++ .../impl/RaftServerCommunicator.java | 301 ++++++++ .../primitives/impl/StorageNamespaces.java | 216 ++++++ .../primitives/impl/StoragePartition.java | 111 +-- .../impl/StoragePartitionClient.java | 320 ++++---- .../impl/StoragePartitionDetails.java | 25 +- .../impl/StoragePartitionServer.java | 72 +- .../resources/impl/AbstractRaftPrimitive.java | 91 +++ .../impl/AtomixAtomicCounterMap.java | 108 +-- .../impl/AtomixAtomicCounterMapCommands.java | 332 --------- .../impl/AtomixAtomicCounterMapFactory.java | 44 -- .../AtomixAtomicCounterMapOperations.java | 248 +++++++ .../impl/AtomixAtomicCounterMapService.java | 303 ++++++++ .../impl/AtomixAtomicCounterMapState.java | 347 --------- .../resources/impl/AtomixConsistentMap.java | 274 ++++--- .../impl/AtomixConsistentMapCommands.java | 632 ---------------- .../impl/AtomixConsistentMapEvents.java | 47 ++ .../impl/AtomixConsistentMapFactory.java | 45 -- .../impl/AtomixConsistentMapOperations.java | 417 +++++++++++ ...e.java => AtomixConsistentMapService.java} | 605 +++++---------- .../AtomixConsistentMultimapCommands.java | 629 ---------------- .../impl/AtomixConsistentSetMultimap.java | 163 ++-- .../AtomixConsistentSetMultimapEvents.java | 46 ++ .../AtomixConsistentSetMultimapFactory.java | 46 -- ...AtomixConsistentSetMultimapOperations.java | 384 ++++++++++ ...> AtomixConsistentSetMultimapService.java} | 545 ++++++-------- .../impl/AtomixConsistentTreeMap.java | 262 ++++--- .../impl/AtomixConsistentTreeMapCommands.java | 700 ------------------ .../impl/AtomixConsistentTreeMapEvents.java | 45 ++ .../impl/AtomixConsistentTreeMapFactory.java | 44 -- .../AtomixConsistentTreeMapOperations.java | 438 +++++++++++ .../impl/AtomixConsistentTreeMapService.java | 416 +++++++++++ .../impl/AtomixConsistentTreeMapState.java | 575 -------------- .../resources/impl/AtomixCounter.java | 87 ++- .../impl/AtomixCounterOperations.java | 188 +++++ .../resources/impl/AtomixCounterService.java | 153 ++++ .../resources/impl/AtomixDocumentTree.java | 128 ++-- .../impl/AtomixDocumentTreeEvents.java | 45 ++ .../impl/AtomixDocumentTreeFactory.java | 45 -- ...java => AtomixDocumentTreeOperations.java} | 179 ++--- .../impl/AtomixDocumentTreeService.java | 306 ++++++++ .../impl/AtomixDocumentTreeState.java | 342 --------- .../resources/impl/AtomixIdGenerator.java | 22 +- .../resources/impl/AtomixLeaderElector.java | 154 ++-- .../impl/AtomixLeaderElectorCommands.java | 461 ------------ .../impl/AtomixLeaderElectorEvents.java | 42 ++ .../impl/AtomixLeaderElectorFactory.java | 45 -- .../impl/AtomixLeaderElectorOperations.java | 333 +++++++++ ...e.java => AtomixLeaderElectorService.java} | 324 ++++---- .../impl/AtomixSerializerAdapter.java | 39 + .../resources/impl/AtomixWorkQueue.java | 131 ++-- .../impl/AtomixWorkQueueCommands.java | 273 ------- .../resources/impl/AtomixWorkQueueEvents.java | 42 ++ .../impl/AtomixWorkQueueFactory.java | 44 -- .../impl/AtomixWorkQueueOperations.java | 148 ++++ .../impl/AtomixWorkQueueService.java | 243 ++++++ .../resources/impl/AtomixWorkQueueState.java | 305 -------- .../resources/impl/DefaultDocumentTree.java | 9 +- .../impl/DefaultDocumentTreeNode.java | 14 +- .../primitives/impl/CopycatTransportTest.java | 433 ----------- .../AtomixAtomicCounterMapServiceTest.java | 74 ++ .../impl/AtomixAtomicCounterMapTest.java | 25 +- .../impl/AtomixConsistentMapServiceTest.java | 79 ++ .../impl/AtomixConsistentMapTest.java | 59 +- ...tomixConsistentSetMultimapServiceTest.java | 85 +++ .../impl/AtomixConsistentSetMultimapTest.java | 50 +- .../AtomixConsistentTreeMapServiceTest.java | 80 ++ .../impl/AtomixConsistentTreeMapTest.java | 46 +- .../impl/AtomixCounterServiceTest.java | 74 ++ ...ixLongTest.java => AtomixCounterTest.java} | 31 +- .../impl/AtomixDocumentTreeServiceTest.java | 85 +++ .../impl/AtomixDocumentTreeTest.java | 85 +-- .../resources/impl/AtomixIdGeneratorTest.java | 35 +- .../impl/AtomixLeaderElectorServiceTest.java | 113 +++ .../impl/AtomixLeaderElectorTest.java | 111 +-- .../resources/impl/AtomixTestBase.java | 535 ++++++++++--- .../impl/AtomixWorkQueueServiceTest.java | 118 +++ .../resources/impl/AtomixWorkQueueTest.java | 75 +- .../impl/TestClusterCommunicationService.java | 174 +++++ ...estClusterCommunicationServiceFactory.java | 39 + features/features.xml | 2 +- lib/BUCK | 37 +- lib/deps.json | 5 +- 124 files changed, 8011 insertions(+), 10430 deletions(-) delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CatalystSerializers.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransport.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportClient.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportConnection.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportServer.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultCatalystTypeSerializerFactory.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DelegatingCopycatClient.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounter.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounterMap.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicIdGenerator.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicValue.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMap.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMultimap.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentTreeMap.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncDocumentTree.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncLeaderElector.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingDistributedPrimitive.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingWorkQueue.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/OnosCopycatClient.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftClientCommunicator.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftCommunicator.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftMessageContext.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftServerCommunicator.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StorageNamespaces.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AbstractRaftPrimitive.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapCommands.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapFactory.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapOperations.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapService.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapState.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapCommands.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapEvents.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapFactory.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapOperations.java rename core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/{AtomixConsistentMapState.java => AtomixConsistentMapService.java} (54%) delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMultimapCommands.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapEvents.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapFactory.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapOperations.java rename core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/{AtomixConsistentSetMultimapState.java => AtomixConsistentSetMultimapService.java} (53%) delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapCommands.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapEvents.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapFactory.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapOperations.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapService.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapState.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterOperations.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterService.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeEvents.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeFactory.java rename core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/{AtomixDocumentTreeCommands.java => AtomixDocumentTreeOperations.java} (51%) create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeService.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeState.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorCommands.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorEvents.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorFactory.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorOperations.java rename core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/{AtomixLeaderElectorState.java => AtomixLeaderElectorService.java} (63%) create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixSerializerAdapter.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueCommands.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueEvents.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueFactory.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueOperations.java create mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueService.java delete mode 100644 core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueState.java delete mode 100644 core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/CopycatTransportTest.java create mode 100644 core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapServiceTest.java create mode 100644 core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapServiceTest.java create mode 100644 core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapServiceTest.java create mode 100644 core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapServiceTest.java create mode 100644 core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterServiceTest.java rename core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/{AtomixLongTest.java => AtomixCounterTest.java} (69%) create mode 100644 core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeServiceTest.java create mode 100644 core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorServiceTest.java create mode 100644 core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueServiceTest.java create mode 100644 core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationService.java create mode 100644 core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationServiceFactory.java diff --git a/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/DistributedPrimitivesTest.java b/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/DistributedPrimitivesTest.java index f5c9eec532..e481e81948 100644 --- a/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/DistributedPrimitivesTest.java +++ b/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/DistributedPrimitivesTest.java @@ -28,6 +28,7 @@ import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.store.serializers.KryoNamespaces; import org.onosproject.store.service.EventuallyConsistentMap; +import org.onosproject.store.service.LeaderElector; import org.onosproject.store.service.StorageService; import org.onosproject.store.service.WallClockTimestamp; import org.slf4j.Logger; @@ -54,6 +55,7 @@ public class DistributedPrimitivesTest { protected StorageService storageService; private final Map> maps = Maps.newConcurrentMap(); + private final Map electors = Maps.newConcurrentMap(); @Activate protected void activate() { @@ -79,4 +81,17 @@ public class DistributedPrimitivesTest { .withTimestampProvider((k, v) -> new WallClockTimestamp()) .build()); } + + /** + * Returns a leader elector session by name. + * + * @param name the leader elector name + * @return the leader elector + */ + public LeaderElector getLeaderElector(String name) { + return electors.computeIfAbsent(name, n -> storageService.leaderElectorBuilder() + .withName(name) + .build() + .asLeaderElector()); + } } diff --git a/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/LeaderElectorTestCommand.java b/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/LeaderElectorTestCommand.java index ef5e45c728..22849e82d1 100644 --- a/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/LeaderElectorTestCommand.java +++ b/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/LeaderElectorTestCommand.java @@ -21,8 +21,8 @@ import org.onosproject.cli.AbstractShellCommand; import org.onosproject.cluster.ClusterService; import org.onosproject.cluster.Leadership; import org.onosproject.cluster.NodeId; +import org.onosproject.distributedprimitives.DistributedPrimitivesTest; import org.onosproject.store.service.LeaderElector; -import org.onosproject.store.service.StorageService; import com.google.common.base.Joiner; @@ -49,13 +49,10 @@ public class LeaderElectorTestCommand extends AbstractShellCommand { @Override protected void execute() { - StorageService storageService = get(StorageService.class); ClusterService clusterService = get(ClusterService.class); + DistributedPrimitivesTest test = get(DistributedPrimitivesTest.class); + leaderElector = test.getLeaderElector(name); NodeId localNodeId = clusterService.getLocalNode().id(); - leaderElector = storageService.leaderElectorBuilder() - .withName(name) - .build() - .asLeaderElector(); if ("run".equals(operation)) { print(leaderElector.run(topic, localNodeId)); } else if ("withdraw".equals(operation)) { diff --git a/cli/src/main/java/org/onosproject/cli/net/PartitionsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/PartitionsListCommand.java index 28ce781273..30806d542a 100644 --- a/cli/src/main/java/org/onosproject/cli/net/PartitionsListCommand.java +++ b/cli/src/main/java/org/onosproject/cli/net/PartitionsListCommand.java @@ -42,7 +42,7 @@ import com.google.common.collect.Ordering; public class PartitionsListCommand extends AbstractShellCommand { @Option(name = "-c", aliases = "--clients", - description = "Show inforamtion about partition clients", + description = "Show information about partition clients", required = false, multiValued = false) private boolean reportClientInfo = false; @@ -91,7 +91,7 @@ public class PartitionsListCommand extends AbstractShellCommand { } ClusterService clusterService = get(ClusterService.class); print("-------------------------------------------------------------------"); - print(CLIENT_FMT, "Name", "SessionId", "Status", "Servers"); + print(CLIENT_FMT, "Name", "Servers"); print("-------------------------------------------------------------------"); for (PartitionClientInfo info : partitionClientInfo) { @@ -100,11 +100,10 @@ public class PartitionsListCommand extends AbstractShellCommand { ControllerNode server = clusterService.getNode(serverId); String serverString = String.format("%s:%d", server.id(), server.tcpPort()); if (first) { - print(CLIENT_FMT, info.partitionId(), info.sessionId(), - info.status(), serverString); + print(CLIENT_FMT, info.partitionId(), serverString); first = false; } else { - print(CLIENT_FMT, "", "", "", serverString); + print(CLIENT_FMT, "", serverString); } } if (!first) { @@ -164,9 +163,7 @@ public class PartitionsListCommand extends AbstractShellCommand { .forEach(servers::add); // Complete the partition attributes and add it to the array - partition.put("partitionId", info.partitionId().toString()) - .put("sessionId", info.sessionId()) - .put("status", info.status().toString()); + partition.put("partitionId", info.partitionId().toString()); partitions.add(partition); }); diff --git a/core/api/src/main/java/org/onosproject/store/primitives/DistributedPrimitiveBuilder.java b/core/api/src/main/java/org/onosproject/store/primitives/DistributedPrimitiveBuilder.java index f9939da108..20a1c5e444 100644 --- a/core/api/src/main/java/org/onosproject/store/primitives/DistributedPrimitiveBuilder.java +++ b/core/api/src/main/java/org/onosproject/store/primitives/DistributedPrimitiveBuilder.java @@ -15,9 +15,6 @@ */ package org.onosproject.store.primitives; -import java.util.concurrent.Executor; -import java.util.function.Supplier; - import org.onosproject.core.ApplicationId; import org.onosproject.store.service.DistributedPrimitive; import org.onosproject.store.service.Serializer; @@ -34,7 +31,6 @@ public abstract class DistributedPrimitiveBuilder executorSupplier; private boolean partitionsDisabled = false; private boolean meteringDisabled = false; private boolean readOnly = false; @@ -66,31 +62,6 @@ public abstract class DistributedPrimitiveBuilder - * For partitioned primitives, the provided executor will be shared across all partitions. - * - * @param executor the executor to use for asynchronous callbacks - * @return this builder - */ - public B withExecutor(Executor executor) { - return withExecutorSupplier(() -> executor); - } - - /** - * Sets the supplier to be used to create executors. - *

- * When a factory is set, the supplier will be used to create a separate executor for each partition. - * - * @param executorSupplier the executor supplier - * @return this builder - */ - public B withExecutorSupplier(Supplier executorSupplier) { - this.executorSupplier = executorSupplier; - return (B) this; - } - /** * Sets the application id that owns this primitive. * @@ -176,15 +147,6 @@ public abstract class DistributedPrimitiveBuilder executorSupplier() { - return executorSupplier; - } - /** * Returns the application identifier. * diff --git a/core/api/src/main/java/org/onosproject/store/primitives/DistributedPrimitiveCreator.java b/core/api/src/main/java/org/onosproject/store/primitives/DistributedPrimitiveCreator.java index 33c3d333d6..0680bd1788 100644 --- a/core/api/src/main/java/org/onosproject/store/primitives/DistributedPrimitiveCreator.java +++ b/core/api/src/main/java/org/onosproject/store/primitives/DistributedPrimitiveCreator.java @@ -15,6 +15,8 @@ */ package org.onosproject.store.primitives; +import java.util.Set; + import org.onosproject.store.service.AsyncAtomicCounter; import org.onosproject.store.service.AsyncAtomicCounterMap; import org.onosproject.store.service.AsyncAtomicIdGenerator; @@ -28,10 +30,6 @@ import org.onosproject.store.service.AsyncLeaderElector; import org.onosproject.store.service.Serializer; import org.onosproject.store.service.WorkQueue; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.function.Supplier; - /** * Interface for entity that can create instances of different distributed primitives. */ @@ -46,22 +44,7 @@ public interface DistributedPrimitiveCreator { * @param value type * @return map */ - default AsyncConsistentMap newAsyncConsistentMap(String name, Serializer serializer) { - return newAsyncConsistentMap(name, serializer, null); - } - - /** - * Creates a new {@code AsyncConsistentMap}. - * - * @param name map name - * @param serializer serializer to use for serializing/deserializing map entries - * @param executorSupplier a callback that returns an executor to be used for each partition - * @param key type - * @param value type - * @return map - */ - AsyncConsistentMap newAsyncConsistentMap( - String name, Serializer serializer, Supplier executorSupplier); + AsyncConsistentMap newAsyncConsistentMap(String name, Serializer serializer); /** * Creates a new {@code AsyncConsistentTreeMap}. @@ -71,22 +54,7 @@ public interface DistributedPrimitiveCreator { * @param value type * @return distributedTreeMap */ - default AsyncConsistentTreeMap newAsyncConsistentTreeMap( - String name, Serializer serializer) { - return newAsyncConsistentTreeMap(name, serializer, null); - } - - /** - * Creates a new {@code AsyncConsistentTreeMap}. - * - * @param name tree name - * @param serializer serializer to use for serializing/deserializing map entries - * @param executorSupplier a callback that returns an executor to be used for each partition - * @param value type - * @return distributedTreeMap - */ - AsyncConsistentTreeMap newAsyncConsistentTreeMap( - String name, Serializer serializer, Supplier executorSupplier); + AsyncConsistentTreeMap newAsyncConsistentTreeMap(String name, Serializer serializer); /** * Creates a new set backed {@code AsyncConsistentMultimap}. @@ -97,23 +65,7 @@ public interface DistributedPrimitiveCreator { * @param value type * @return set backed distributedMultimap */ - default AsyncConsistentMultimap newAsyncConsistentSetMultimap( - String name, Serializer serializer) { - return newAsyncConsistentSetMultimap(name, serializer, null); - } - - /** - * Creates a new set backed {@code AsyncConsistentMultimap}. - * - * @param name multimap name - * @param serializer serializer to use for serializing/deserializing - * @param executorSupplier a callback that returns an executor to be used for each partition - * @param key type - * @param value type - * @return set backed distributedMultimap - */ - AsyncConsistentMultimap newAsyncConsistentSetMultimap( - String name, Serializer serializer, Supplier executorSupplier); + AsyncConsistentMultimap newAsyncConsistentSetMultimap(String name, Serializer serializer); /** * Creates a new {@code AsyncAtomicCounterMap}. @@ -123,22 +75,7 @@ public interface DistributedPrimitiveCreator { * @param key type * @return atomic counter map */ - default AsyncAtomicCounterMap newAsyncAtomicCounterMap( - String name, Serializer serializer) { - return newAsyncAtomicCounterMap(name, serializer, null); - } - - /** - * Creates a new {@code AsyncAtomicCounterMap}. - * - * @param name counter map name - * @param serializer serializer to use for serializing/deserializing keys - * @param executorSupplier a callback that returns an executor to be used for each partition - * @param key type - * @return atomic counter map - */ - AsyncAtomicCounterMap newAsyncAtomicCounterMap( - String name, Serializer serializer, Supplier executorSupplier); + AsyncAtomicCounterMap newAsyncAtomicCounterMap(String name, Serializer serializer); /** * Creates a new {@code AsyncAtomicCounter}. @@ -146,18 +83,7 @@ public interface DistributedPrimitiveCreator { * @param name counter name * @return counter */ - default AsyncAtomicCounter newAsyncCounter(String name) { - return newAsyncCounter(name, null); - } - - /** - * Creates a new {@code AsyncAtomicCounter}. - * - * @param name counter name - * @param executorSupplier a callback that returns an executor to be used asynchronous callbacks - * @return counter - */ - AsyncAtomicCounter newAsyncCounter(String name, Supplier executorSupplier); + AsyncAtomicCounter newAsyncCounter(String name); /** * Creates a new {@code AsyncAtomixIdGenerator}. @@ -165,18 +91,7 @@ public interface DistributedPrimitiveCreator { * @param name ID generator name * @return ID generator */ - default AsyncAtomicIdGenerator newAsyncIdGenerator(String name) { - return newAsyncIdGenerator(name, null); - } - - /** - * Creates a new {@code AsyncAtomixIdGenerator}. - * - * @param name ID generator name - * @param executorSupplier a callback that returns an executor to be used asynchronous callbacks - * @return ID generator - */ - AsyncAtomicIdGenerator newAsyncIdGenerator(String name, Supplier executorSupplier); + AsyncAtomicIdGenerator newAsyncIdGenerator(String name); /** * Creates a new {@code AsyncAtomicValue}. @@ -186,21 +101,7 @@ public interface DistributedPrimitiveCreator { * @param value type * @return value */ - default AsyncAtomicValue newAsyncAtomicValue(String name, Serializer serializer) { - return newAsyncAtomicValue(name, serializer, null); - } - - /** - * Creates a new {@code AsyncAtomicValue}. - * - * @param name value name - * @param serializer serializer to use for serializing/deserializing value type - * @param executorSupplier a callback that returns an executor to be used asynchronous callbacks - * @param value type - * @return value - */ - AsyncAtomicValue newAsyncAtomicValue( - String name, Serializer serializer, Supplier executorSupplier); + AsyncAtomicValue newAsyncAtomicValue(String name, Serializer serializer); /** * Creates a new {@code AsyncDistributedSet}. @@ -210,21 +111,7 @@ public interface DistributedPrimitiveCreator { * @param set entry type * @return set */ - default AsyncDistributedSet newAsyncDistributedSet(String name, Serializer serializer) { - return newAsyncDistributedSet(name, serializer, null); - } - - /** - * Creates a new {@code AsyncDistributedSet}. - * - * @param name set name - * @param serializer serializer to use for serializing/deserializing set entries - * @param executorSupplier a callback that returns an executor to be used asynchronous callbacks - * @param set entry type - * @return set - */ - AsyncDistributedSet newAsyncDistributedSet( - String name, Serializer serializer, Supplier executorSupplier); + AsyncDistributedSet newAsyncDistributedSet(String name, Serializer serializer); /** * Creates a new {@code AsyncLeaderElector}. @@ -232,18 +119,7 @@ public interface DistributedPrimitiveCreator { * @param name leader elector name * @return leader elector */ - default AsyncLeaderElector newAsyncLeaderElector(String name) { - return newAsyncLeaderElector(name, null); - } - - /** - * Creates a new {@code AsyncLeaderElector}. - * - * @param name leader elector name - * @param executorSupplier a callback that returns an executor to be used asynchronous callbacks - * @return leader elector - */ - AsyncLeaderElector newAsyncLeaderElector(String name, Supplier executorSupplier); + AsyncLeaderElector newAsyncLeaderElector(String name); /** * Creates a new {@code WorkQueue}. @@ -253,20 +129,7 @@ public interface DistributedPrimitiveCreator { * @param serializer serializer * @return work queue */ - default WorkQueue newWorkQueue(String name, Serializer serializer) { - return newWorkQueue(name, serializer, null); - } - - /** - * Creates a new {@code WorkQueue}. - * - * @param work element type - * @param name work queue name - * @param serializer serializer - * @param executorSupplier a callback that returns an executor to be used asynchronous callbacks - * @return work queue - */ - WorkQueue newWorkQueue(String name, Serializer serializer, Supplier executorSupplier); + WorkQueue newWorkQueue(String name, Serializer serializer); /** * Creates a new {@code AsyncDocumentTree}. @@ -276,21 +139,7 @@ public interface DistributedPrimitiveCreator { * @param serializer serializer * @return document tree */ - default AsyncDocumentTree newAsyncDocumentTree(String name, Serializer serializer) { - return newAsyncDocumentTree(name, serializer, null); - } - - /** - * Creates a new {@code AsyncDocumentTree}. - * - * @param document tree node value type - * @param name tree name - * @param serializer serializer - * @param executorSupplier a callback that returns an executor to be used asynchronous callbacks - * @return document tree - */ - AsyncDocumentTree newAsyncDocumentTree( - String name, Serializer serializer, Supplier executorSupplier); + AsyncDocumentTree newAsyncDocumentTree(String name, Serializer serializer); /** * Returns the names of all created {@code AsyncConsistentMap} instances. diff --git a/core/api/src/main/java/org/onosproject/store/service/PartitionClientInfo.java b/core/api/src/main/java/org/onosproject/store/service/PartitionClientInfo.java index 8f4dab3299..ca97060226 100644 --- a/core/api/src/main/java/org/onosproject/store/service/PartitionClientInfo.java +++ b/core/api/src/main/java/org/onosproject/store/service/PartitionClientInfo.java @@ -30,15 +30,11 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class PartitionClientInfo { private final PartitionId partitionId; - private final Status status; private final Collection servers; - private final long sessionId; - public PartitionClientInfo(PartitionId partitionId, Collection servers, long sessionId, Status status) { + public PartitionClientInfo(PartitionId partitionId, Collection servers) { this.partitionId = checkNotNull(partitionId); this.servers = ImmutableList.copyOf(checkNotNull(servers)); - this.sessionId = sessionId; - this.status = checkNotNull(status); } /** @@ -62,16 +58,20 @@ public class PartitionClientInfo { /** * Return the sessionId for the partition client. * @return session id + * @deprecated in Loon release (1.11.0) */ + @Deprecated public long sessionId() { - return sessionId; + return 0; } /** * Returns the current status for the client session. * @return status + * @deprecated in Loon release (1.11.0) */ + @Deprecated public Status status() { - return status; + return null; } } diff --git a/core/store/primitives/BUCK b/core/store/primitives/BUCK index 0758c3ea35..e90ea5dad8 100644 --- a/core/store/primitives/BUCK +++ b/core/store/primitives/BUCK @@ -9,13 +9,11 @@ COMPILE_DEPS = [ TEST_DEPS = [ '//lib:TEST', '//core/api:onos-api-tests', - '//lib:netty-transport', - '//lib:catalyst-transport', '//lib:netty-handler', '//lib:netty-buffer', '//lib:netty-codec', '//lib:netty-resolver', - + '//lib:commons-math3', ] osgi_jar_with_tests ( diff --git a/core/store/primitives/pom.xml b/core/store/primitives/pom.xml index c46a7ca361..ee89881906 100644 --- a/core/store/primitives/pom.xml +++ b/core/store/primitives/pom.xml @@ -70,19 +70,9 @@ io.atomix atomix - 1.0.8 + 2.0.0-alpha1 - - io.atomix.catalyst - catalyst-netty - 1.2.1 - - - io.atomix.catalyst - catalyst-transport - 1.2.1 - org.onosproject onlab-junit diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CatalystSerializers.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CatalystSerializers.java deleted file mode 100644 index 2c4ee731d5..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CatalystSerializers.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import com.google.common.collect.HashMultiset; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; -import io.atomix.catalyst.serializer.Serializer; -import io.atomix.catalyst.serializer.TypeSerializerFactory; -import io.atomix.manager.util.ResourceManagerTypeResolver; -import io.atomix.variables.internal.LongCommands; -import org.onlab.util.Match; -import org.onosproject.cluster.Leader; -import org.onosproject.cluster.Leadership; -import org.onosproject.cluster.NodeId; -import org.onosproject.event.Change; -import org.onosproject.store.primitives.MapUpdate; -import org.onosproject.store.primitives.TransactionId; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapFactory; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapFactory; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapFactory; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeFactory; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorFactory; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueFactory; -import org.onosproject.store.primitives.resources.impl.CommitResult; -import org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult; -import org.onosproject.store.primitives.resources.impl.MapEntryUpdateResult; -import org.onosproject.store.primitives.resources.impl.PrepareResult; -import org.onosproject.store.primitives.resources.impl.RollbackResult; -import org.onosproject.store.serializers.KryoNamespaces; -import org.onosproject.store.service.DocumentPath; -import org.onosproject.store.service.DocumentTreeEvent; -import org.onosproject.store.service.MapEvent; -import org.onosproject.store.service.TransactionLog; -import org.onosproject.store.service.MultimapEvent; -import org.onosproject.store.service.Task; -import org.onosproject.store.service.Versioned; -import org.onosproject.store.service.WorkQueueStats; - -import java.util.Arrays; -import java.util.Optional; - -/** - * Serializer utility for Atomix Catalyst. - */ -public final class CatalystSerializers { - - private CatalystSerializers() { - } - - public static Serializer getSerializer() { - Serializer serializer = new Serializer(); - TypeSerializerFactory factory = - new DefaultCatalystTypeSerializerFactory( - org.onosproject.store.service.Serializer.using(Arrays.asList((KryoNamespaces.API)), - MapEntryUpdateResult.class, - MapEntryUpdateResult.Status.class, - Transaction.State.class, - PrepareResult.class, - CommitResult.class, - DocumentPath.class, - DocumentTreeUpdateResult.class, - DocumentTreeUpdateResult.Status.class, - DocumentTreeEvent.class, - DocumentTreeEvent.Type.class, - RollbackResult.class)); - // ONOS classes - serializer.register(Change.class, factory); - serializer.register(Leader.class, factory); - serializer.register(Leadership.class, factory); - serializer.register(NodeId.class, factory); - serializer.register(Match.class, factory); - serializer.register(MapEntryUpdateResult.class, factory); - serializer.register(MapEntryUpdateResult.Status.class, factory); - serializer.register(Transaction.State.class, factory); - serializer.register(PrepareResult.class, factory); - serializer.register(CommitResult.class, factory); - serializer.register(RollbackResult.class, factory); - serializer.register(TransactionId.class, factory); - serializer.register(MapUpdate.class, factory); - serializer.register(MapUpdate.Type.class, factory); - serializer.register(TransactionLog.class, factory); - serializer.register(Versioned.class, factory); - serializer.register(MapEvent.class, factory); - serializer.register(MultimapEvent.class, factory); - serializer.register(MultimapEvent.Type.class, factory); - serializer.register(Task.class, factory); - serializer.register(WorkQueueStats.class, factory); - serializer.register(DocumentPath.class, factory); - serializer.register(DocumentTreeUpdateResult.class, factory); - serializer.register(DocumentTreeUpdateResult.Status.class, factory); - serializer.register(DocumentTreeEvent.class, factory); - serializer.register(Maps.immutableEntry("a", "b").getClass(), factory); - serializer.register(ImmutableList.of().getClass(), factory); - serializer.register(ImmutableList.of("a").getClass(), factory); - serializer.register(Arrays.asList().getClass(), factory); - serializer.register(HashMultiset.class, factory); - serializer.register(Optional.class, factory); - - serializer.resolve(new LongCommands.TypeResolver()); - serializer.resolve(new AtomixConsistentMapCommands.TypeResolver()); - serializer.resolve(new AtomixLeaderElectorCommands.TypeResolver()); - serializer.resolve(new AtomixWorkQueueCommands.TypeResolver()); - serializer.resolve(new AtomixDocumentTreeCommands.TypeResolver()); - serializer.resolve(new ResourceManagerTypeResolver()); - serializer.resolve(new AtomixConsistentTreeMapCommands.TypeResolver()); - serializer.resolve(new AtomixConsistentMultimapCommands.TypeResolver()); - - serializer.registerClassLoader(AtomixConsistentMapFactory.class) - .registerClassLoader(AtomixLeaderElectorFactory.class) - .registerClassLoader(AtomixWorkQueueFactory.class) - .registerClassLoader(AtomixDocumentTreeFactory.class) - .registerClassLoader(AtomixConsistentTreeMapFactory.class) - .registerClassLoader(AtomixConsistentSetMultimapFactory.class); - - return serializer; - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransport.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransport.java deleted file mode 100644 index fc94dd6635..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransport.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import com.google.common.base.Throwables; -import com.google.common.collect.Maps; -import io.atomix.catalyst.transport.Address; -import io.atomix.catalyst.transport.Client; -import io.atomix.catalyst.transport.Server; -import io.atomix.catalyst.transport.Transport; -import org.onlab.packet.IpAddress; -import org.onosproject.cluster.PartitionId; -import org.onosproject.store.cluster.messaging.Endpoint; -import org.onosproject.store.cluster.messaging.MessagingService; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Map; - -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Copycat transport implementation built on {@link MessagingService}. - */ -public class CopycatTransport implements Transport { - private final PartitionId partitionId; - private final MessagingService messagingService; - private static final Map EP_LOOKUP_CACHE = Maps.newConcurrentMap(); - - static final byte MESSAGE = 0x01; - static final byte CONNECT = 0x02; - static final byte CLOSE = 0x03; - - static final byte SUCCESS = 0x01; - static final byte FAILURE = 0x02; - - public CopycatTransport(PartitionId partitionId, MessagingService messagingService) { - this.partitionId = checkNotNull(partitionId, "partitionId cannot be null"); - this.messagingService = checkNotNull(messagingService, "messagingService cannot be null"); - } - - @Override - public Client client() { - return new CopycatTransportClient(partitionId, messagingService); - } - - @Override - public Server server() { - return new CopycatTransportServer(partitionId, messagingService); - } - - @Override - public String toString() { - return toStringHelper(this).toString(); - } - - /** - * Maps {@link Address address} to {@link Endpoint endpoint}. - * @param address address - * @return end point - */ - static Endpoint toEndpoint(Address address) { - return EP_LOOKUP_CACHE.computeIfAbsent(address, a -> { - try { - return new Endpoint(IpAddress.valueOf(InetAddress.getByName(a.host())), a.port()); - } catch (UnknownHostException e) { - Throwables.propagate(e); - return null; - } - }); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportClient.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportClient.java deleted file mode 100644 index afa98ccd54..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportClient.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import com.google.common.base.Throwables; -import com.google.common.collect.Sets; -import io.atomix.catalyst.concurrent.ThreadContext; -import io.atomix.catalyst.transport.Address; -import io.atomix.catalyst.transport.Client; -import io.atomix.catalyst.transport.Connection; -import io.atomix.catalyst.transport.TransportException; -import org.onosproject.cluster.PartitionId; -import org.onosproject.store.cluster.messaging.Endpoint; -import org.onosproject.store.cluster.messaging.MessagingException; -import org.onosproject.store.cluster.messaging.MessagingService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.ConnectException; -import java.nio.ByteBuffer; -import java.util.Set; -import java.util.concurrent.CompletableFuture; - -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.onosproject.store.primitives.impl.CopycatTransport.CONNECT; -import static org.onosproject.store.primitives.impl.CopycatTransport.SUCCESS; - -/** - * Copycat transport client implementation. - */ -public class CopycatTransportClient implements Client { - private final Logger log = LoggerFactory.getLogger(getClass()); - private final PartitionId partitionId; - private final String serverSubject; - private final MessagingService messagingService; - private final Set connections = Sets.newConcurrentHashSet(); - - public CopycatTransportClient(PartitionId partitionId, MessagingService messagingService) { - this.partitionId = checkNotNull(partitionId, "partitionId cannot be null"); - this.serverSubject = String.format("onos-copycat-%s", partitionId); - this.messagingService = checkNotNull(messagingService, "messagingService cannot be null"); - } - - @Override - public CompletableFuture connect(Address address) { - CompletableFuture future = new CompletableFuture<>(); - ThreadContext context = ThreadContext.currentContextOrThrow(); - Endpoint endpoint = CopycatTransport.toEndpoint(address); - - log.debug("Connecting to {}", address); - - ByteBuffer requestBuffer = ByteBuffer.allocate(1); - requestBuffer.put(CONNECT); - - // Send a connect request to the server to get a unique connection ID. - messagingService.sendAndReceive(endpoint, serverSubject, requestBuffer.array(), context.executor()) - .whenComplete((payload, error) -> { - Throwable wrappedError = error; - if (error != null) { - Throwable rootCause = Throwables.getRootCause(error); - if (MessagingException.class.isAssignableFrom(rootCause.getClass())) { - wrappedError = new TransportException(error); - } - // TODO ONOS-6788 we might consider demoting this warning during startup when there is - // a race between the server registering handlers and the client sending messages - log.warn("Connection to {} failed! Reason: {}", address, wrappedError); - future.completeExceptionally(wrappedError); - } else { - // If the connection is successful, the server will send back a - // connection ID indicating where to send messages for the connection. - ByteBuffer responseBuffer = ByteBuffer.wrap(payload); - if (responseBuffer.get() == SUCCESS) { - long connectionId = responseBuffer.getLong(); - CopycatTransportConnection connection = new CopycatTransportConnection( - connectionId, - CopycatTransportConnection.Mode.CLIENT, - partitionId, - endpoint, - messagingService, - context); - connection.onClose(connections::remove); - connections.add(connection); - future.complete(connection); - log.debug("Created connection {}-{} to {}", partitionId, connectionId, address); - } else { - log.warn("Connection to {} failed!"); - future.completeExceptionally(new ConnectException()); - } - } - }); - return future; - } - - @Override - public CompletableFuture close() { - return CompletableFuture.allOf(connections.stream().map(Connection::close).toArray(CompletableFuture[]::new)); - } - - @Override - public String toString() { - return toStringHelper(this) - .add("partitionId", partitionId) - .toString(); - } -} - diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportConnection.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportConnection.java deleted file mode 100644 index a3a8539f00..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportConnection.java +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Consumer; -import java.util.function.Function; - -import com.google.common.base.Throwables; -import io.atomix.catalyst.concurrent.Listener; -import io.atomix.catalyst.concurrent.Listeners; -import io.atomix.catalyst.concurrent.ThreadContext; -import io.atomix.catalyst.serializer.SerializationException; -import io.atomix.catalyst.transport.Connection; -import io.atomix.catalyst.transport.TransportException; -import io.atomix.catalyst.util.reference.ReferenceCounted; -import org.apache.commons.io.IOUtils; -import org.onlab.util.Tools; -import org.onosproject.cluster.PartitionId; -import org.onosproject.store.cluster.messaging.Endpoint; -import org.onosproject.store.cluster.messaging.MessagingException; -import org.onosproject.store.cluster.messaging.MessagingService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.onosproject.store.primitives.impl.CopycatTransport.CLOSE; -import static org.onosproject.store.primitives.impl.CopycatTransport.FAILURE; -import static org.onosproject.store.primitives.impl.CopycatTransport.MESSAGE; -import static org.onosproject.store.primitives.impl.CopycatTransport.SUCCESS; - -/** - * Base Copycat Transport connection. - */ -public class CopycatTransportConnection implements Connection { - private static final int MAX_MESSAGE_SIZE = 1024 * 1024; - - private final Logger log = LoggerFactory.getLogger(getClass()); - private final long connectionId; - private final String localSubject; - private final String remoteSubject; - private final PartitionId partitionId; - private final Endpoint endpoint; - private final MessagingService messagingService; - private final ThreadContext context; - private final Map handlers = new ConcurrentHashMap<>(); - private final Listeners exceptionListeners = new Listeners<>(); - private final Listeners closeListeners = new Listeners<>(); - - CopycatTransportConnection( - long connectionId, - Mode mode, - PartitionId partitionId, - Endpoint endpoint, - MessagingService messagingService, - ThreadContext context) { - this.connectionId = connectionId; - this.partitionId = checkNotNull(partitionId, "partitionId cannot be null"); - this.localSubject = mode.getLocalSubject(partitionId, connectionId); - this.remoteSubject = mode.getRemoteSubject(partitionId, connectionId); - this.endpoint = checkNotNull(endpoint, "endpoint cannot be null"); - this.messagingService = checkNotNull(messagingService, "messagingService cannot be null"); - this.context = checkNotNull(context, "context cannot be null"); - messagingService.registerHandler(localSubject, this::handle); - } - - @Override - public CompletableFuture send(Object message) { - ThreadContext context = ThreadContext.currentContextOrThrow(); - CompletableFuture future = new CompletableFuture<>(); - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - DataOutputStream dos = new DataOutputStream(baos); - dos.writeByte(MESSAGE); - context.serializer().writeObject(message, baos); - if (message instanceof ReferenceCounted) { - ((ReferenceCounted) message).release(); - } - - byte[] bytes = baos.toByteArray(); - if (bytes.length > MAX_MESSAGE_SIZE) { - throw new IllegalArgumentException(message + " exceeds maximum message size " + MAX_MESSAGE_SIZE); - } - messagingService.sendAsync(endpoint, remoteSubject, bytes) - .whenComplete((r, e) -> { - if (e != null) { - context.executor().execute(() -> future.completeExceptionally(e)); - } else { - context.executor().execute(() -> future.complete(null)); - } - }); - } catch (SerializationException | IOException e) { - future.completeExceptionally(e); - } - return future; - } - - @Override - public CompletableFuture sendAndReceive(T message) { - ThreadContext context = ThreadContext.currentContextOrThrow(); - CompletableFuture future = new CompletableFuture<>(); - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - DataOutputStream dos = new DataOutputStream(baos); - dos.writeByte(MESSAGE); - context.serializer().writeObject(message, baos); - if (message instanceof ReferenceCounted) { - ((ReferenceCounted) message).release(); - } - - byte[] bytes = baos.toByteArray(); - if (bytes.length > MAX_MESSAGE_SIZE) { - throw new IllegalArgumentException(message + " exceeds maximum message size " + MAX_MESSAGE_SIZE); - } - messagingService.sendAndReceive(endpoint, - remoteSubject, - bytes, - context.executor()) - .whenComplete((response, error) -> handleResponse(response, error, future)); - } catch (SerializationException | IOException e) { - future.completeExceptionally(e); - } - return future; - } - - /** - * Handles a response received from the other side of the connection. - */ - private void handleResponse( - byte[] response, - Throwable error, - CompletableFuture future) { - if (error != null) { - Throwable rootCause = Throwables.getRootCause(error); - if (rootCause instanceof MessagingException.NoRemoteHandler) { - future.completeExceptionally(new TransportException(error)); - close(rootCause); - } else if (rootCause instanceof SocketException) { - future.completeExceptionally(new TransportException(error)); - } else { - future.completeExceptionally(error); - } - return; - } - - checkNotNull(response); - InputStream input = new ByteArrayInputStream(response); - try { - byte status = (byte) input.read(); - if (status == FAILURE) { - Throwable t = context.serializer().readObject(input); - future.completeExceptionally(t); - } else { - try { - future.complete(context.serializer().readObject(input)); - } catch (SerializationException e) { - future.completeExceptionally(e); - } - } - } catch (IOException e) { - future.completeExceptionally(e); - } - } - - /** - * Handles a message sent to the connection. - */ - private CompletableFuture handle(Endpoint sender, byte[] payload) { - try (DataInputStream input = new DataInputStream(new ByteArrayInputStream(payload))) { - byte type = input.readByte(); - switch (type) { - case MESSAGE: - return handleMessage(IOUtils.toByteArray(input)); - case CLOSE: - return handleClose(); - default: - throw new IllegalStateException("Invalid message type"); - } - } catch (IOException e) { - Throwables.propagate(e); - return null; - } - } - - /** - * Handles a message from the other side of the connection. - */ - @SuppressWarnings("unchecked") - private CompletableFuture handleMessage(byte[] message) { - try { - Object request = context.serializer().readObject(new ByteArrayInputStream(message)); - InternalHandler handler = handlers.get(request.getClass()); - if (handler == null) { - log.warn("No handler registered on connection {}-{} for type {}", - partitionId, connectionId, request.getClass()); - return Tools.exceptionalFuture(new IllegalStateException( - "No handler registered for " + request.getClass())); - } - - return handler.handle(request).handle((result, error) -> { - try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - baos.write(error != null ? FAILURE : SUCCESS); - context.serializer().writeObject(error != null ? error : result, baos); - byte[] bytes = baos.toByteArray(); - if (bytes.length > MAX_MESSAGE_SIZE) { - throw new IllegalArgumentException("response exceeds maximum message size " + MAX_MESSAGE_SIZE); - } - return bytes; - } catch (IOException e) { - Throwables.propagate(e); - return null; - } - }); - } catch (Exception e) { - return Tools.exceptionalFuture(e); - } - } - - /** - * Handles a close request from the other side of the connection. - */ - private CompletableFuture handleClose() { - CompletableFuture future = new CompletableFuture<>(); - context.executor().execute(() -> { - close(null); - ByteBuffer responseBuffer = ByteBuffer.allocate(1); - responseBuffer.put(SUCCESS); - future.complete(responseBuffer.array()); - }); - return future; - } - - @Override - public Connection handler(Class type, Consumer handler) { - return handler(type, r -> { - handler.accept(r); - return null; - }); - } - - @Override - public Connection handler(Class type, Function> handler) { - if (log.isTraceEnabled()) { - log.trace("Registered handler on connection {}-{}: {}", partitionId, connectionId, type); - } - handlers.put(type, new InternalHandler(handler, ThreadContext.currentContextOrThrow())); - return this; - } - - @Override - public Listener onException(Consumer consumer) { - return exceptionListeners.add(consumer); - } - - @Override - public Listener onClose(Consumer consumer) { - return closeListeners.add(consumer); - } - - @Override - public CompletableFuture close() { - log.debug("Closing connection {}-{}", partitionId, connectionId); - - ByteBuffer requestBuffer = ByteBuffer.allocate(1); - requestBuffer.put(CLOSE); - - ThreadContext context = ThreadContext.currentContextOrThrow(); - CompletableFuture future = new CompletableFuture<>(); - messagingService.sendAndReceive(endpoint, remoteSubject, requestBuffer.array(), context.executor()) - .whenComplete((payload, error) -> { - close(error); - Throwable wrappedError = error; - if (error != null) { - Throwable rootCause = Throwables.getRootCause(error); - if (rootCause instanceof MessagingException.NoRemoteHandler) { - wrappedError = new TransportException(error); - } - future.completeExceptionally(wrappedError); - } else { - ByteBuffer responseBuffer = ByteBuffer.wrap(payload); - if (responseBuffer.get() == SUCCESS) { - future.complete(null); - } else { - future.completeExceptionally(new TransportException("Failed to close connection")); - } - } - }); - return future; - } - - /** - * Cleans up the connection, unregistering handlers registered on the MessagingService. - */ - private void close(Throwable error) { - log.debug("Connection {}-{} closed", partitionId, connectionId); - messagingService.unregisterHandler(localSubject); - if (error != null) { - exceptionListeners.accept(error); - } - closeListeners.accept(this); - } - - /** - * Connection mode used to indicate whether this side of the connection is - * a client or server. - */ - enum Mode { - - /** - * Represents the client side of a bi-directional connection. - */ - CLIENT { - @Override - String getLocalSubject(PartitionId partitionId, long connectionId) { - return String.format("onos-copycat-%s-%d-client", partitionId, connectionId); - } - - @Override - String getRemoteSubject(PartitionId partitionId, long connectionId) { - return String.format("onos-copycat-%s-%d-server", partitionId, connectionId); - } - }, - - /** - * Represents the server side of a bi-directional connection. - */ - SERVER { - @Override - String getLocalSubject(PartitionId partitionId, long connectionId) { - return String.format("onos-copycat-%s-%d-server", partitionId, connectionId); - } - - @Override - String getRemoteSubject(PartitionId partitionId, long connectionId) { - return String.format("onos-copycat-%s-%d-client", partitionId, connectionId); - } - }; - - /** - * Returns the local messaging service subject for the connection in this mode. - * Subjects generated by the connection mode are guaranteed to be globally unique. - * - * @param partitionId the partition ID to which the connection belongs. - * @param connectionId the connection ID. - * @return the globally unique local subject for the connection. - */ - abstract String getLocalSubject(PartitionId partitionId, long connectionId); - - /** - * Returns the remote messaging service subject for the connection in this mode. - * Subjects generated by the connection mode are guaranteed to be globally unique. - * - * @param partitionId the partition ID to which the connection belongs. - * @param connectionId the connection ID. - * @return the globally unique remote subject for the connection. - */ - abstract String getRemoteSubject(PartitionId partitionId, long connectionId); - } - - /** - * Internal container for a handler/context pair. - */ - private static class InternalHandler { - private final Function handler; - private final ThreadContext context; - - InternalHandler(Function handler, ThreadContext context) { - this.handler = handler; - this.context = context; - } - - @SuppressWarnings("unchecked") - CompletableFuture handle(Object message) { - CompletableFuture future = new CompletableFuture<>(); - context.executor().execute(() -> { - CompletableFuture responseFuture = (CompletableFuture) handler.apply(message); - if (responseFuture != null) { - responseFuture.whenComplete((r, e) -> { - if (e != null) { - future.completeExceptionally((Throwable) e); - } else { - future.complete(r); - } - }); - } - }); - return future; - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportServer.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportServer.java deleted file mode 100644 index 8de05a36f3..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/CopycatTransportServer.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import com.google.common.collect.Sets; -import io.atomix.catalyst.concurrent.ThreadContext; -import io.atomix.catalyst.transport.Address; -import io.atomix.catalyst.transport.Connection; -import io.atomix.catalyst.transport.Server; -import org.apache.commons.lang3.RandomUtils; -import org.onosproject.cluster.PartitionId; -import org.onosproject.store.cluster.messaging.MessagingService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.ByteBuffer; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; - -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.onosproject.store.primitives.impl.CopycatTransport.CONNECT; -import static org.onosproject.store.primitives.impl.CopycatTransport.FAILURE; -import static org.onosproject.store.primitives.impl.CopycatTransport.SUCCESS; - -/** - * Copycat transport server implementation. - */ -public class CopycatTransportServer implements Server { - private final Logger log = LoggerFactory.getLogger(getClass()); - private final PartitionId partitionId; - private final String serverSubject; - private final MessagingService messagingService; - private final Set connections = Sets.newConcurrentHashSet(); - - public CopycatTransportServer(PartitionId partitionId, MessagingService messagingService) { - this.partitionId = checkNotNull(partitionId, "partitionId cannot be null"); - this.serverSubject = String.format("onos-copycat-%s", partitionId); - this.messagingService = checkNotNull(messagingService, "messagingService cannot be null"); - } - - @Override - public CompletableFuture listen(Address address, Consumer consumer) { - ThreadContext context = ThreadContext.currentContextOrThrow(); - messagingService.registerHandler(serverSubject, (sender, payload) -> { - - // Only connect messages can be sent to the server. Once a connect message - // is received, the connection will register a separate handler for messaging. - ByteBuffer requestBuffer = ByteBuffer.wrap(payload); - if (requestBuffer.get() != CONNECT) { - ByteBuffer responseBuffer = ByteBuffer.allocate(1); - responseBuffer.put(FAILURE); - return CompletableFuture.completedFuture(responseBuffer.array()); - } - - // Create the connection and ensure state is cleaned up when the connection is closed. - long connectionId = RandomUtils.nextLong(); - CopycatTransportConnection connection = new CopycatTransportConnection( - connectionId, - CopycatTransportConnection.Mode.SERVER, - partitionId, - sender, - messagingService, - context); - connection.onClose(connections::remove); - connections.add(connection); - - CompletableFuture future = new CompletableFuture<>(); - - // We need to ensure the connection event is called on the Copycat thread - // and that the future is not completed until the Copycat server has been - // able to register message handlers, otherwise some messages can be received - // prior to any handlers being registered. - context.executor().execute(() -> { - log.debug("Created connection {}-{}", partitionId, connectionId); - consumer.accept(connection); - - ByteBuffer responseBuffer = ByteBuffer.allocate(9); - responseBuffer.put(SUCCESS); - responseBuffer.putLong(connectionId); - future.complete(responseBuffer.array()); - }); - return future; - }); - return CompletableFuture.completedFuture(null); - } - - @Override - public CompletableFuture close() { - return CompletableFuture.allOf(connections.stream().map(Connection::close).toArray(CompletableFuture[]::new)); - } - - @Override - public String toString() { - return toStringHelper(this) - .add("partitionId", partitionId) - .toString(); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterBuilder.java index a189eb20c3..45cf193f6c 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterBuilder.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterBuilder.java @@ -32,6 +32,6 @@ public class DefaultAtomicCounterBuilder extends AtomicCounterBuilder { @Override public AsyncAtomicCounter build() { - return primitiveCreator.newAsyncCounter(name(), executorSupplier()); + return primitiveCreator.newAsyncCounter(name()); } } diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterMapBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterMapBuilder.java index c92ef22aa2..309220aa23 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterMapBuilder.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicCounterMapBuilder.java @@ -33,7 +33,7 @@ public class DefaultAtomicCounterMapBuilder extends AtomicCounterMapBuilder buildAsyncMap() { - return primitiveCreator.newAsyncAtomicCounterMap(name(), serializer(), executorSupplier()); + return primitiveCreator.newAsyncAtomicCounterMap(name(), serializer()); } @Override diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicIdGeneratorBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicIdGeneratorBuilder.java index ac294f45d7..c6742cb869 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicIdGeneratorBuilder.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicIdGeneratorBuilder.java @@ -32,6 +32,6 @@ public class DefaultAtomicIdGeneratorBuilder extends AtomicIdGeneratorBuilder { @Override public AsyncAtomicIdGenerator build() { - return primitiveCreator.newAsyncIdGenerator(name(), executorSupplier()); + return primitiveCreator.newAsyncIdGenerator(name()); } } diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicValueBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicValueBuilder.java index a63fe4be93..b17983c09b 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicValueBuilder.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultAtomicValueBuilder.java @@ -15,7 +15,6 @@ */ package org.onosproject.store.primitives.impl; -import java.util.concurrent.Executor; import java.util.function.Supplier; import org.onosproject.store.service.AsyncAtomicValue; @@ -37,12 +36,6 @@ public class DefaultAtomicValueBuilder extends AtomicValueBuilder { mapBuilder = mapBuilderSupplier.get(); } - @Override - public AtomicValueBuilder withExecutorSupplier(Supplier executorSupplier) { - mapBuilder.withExecutorSupplier(executorSupplier); - return this; - } - @Override public AsyncAtomicValue build() { return new DefaultAsyncAtomicValue<>(checkNotNull(name()), diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultCatalystTypeSerializerFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultCatalystTypeSerializerFactory.java deleted file mode 100644 index b0d6841bc0..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultCatalystTypeSerializerFactory.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import static org.slf4j.LoggerFactory.getLogger; - -import org.onosproject.store.service.Serializer; -import org.slf4j.Logger; - -import com.google.common.base.Throwables; - -import io.atomix.catalyst.buffer.BufferInput; -import io.atomix.catalyst.buffer.BufferOutput; -import io.atomix.catalyst.serializer.TypeSerializer; -import io.atomix.catalyst.serializer.TypeSerializerFactory; - -/** - * {@link TypeSerializerFactory} for providing {@link TypeSerializer}s based on - * {@code org.onosproject.store.service.Serializer}. - */ -public class DefaultCatalystTypeSerializerFactory implements TypeSerializerFactory { - - private final Logger log = getLogger(getClass()); - private final TypeSerializer typeSerializer; - - public DefaultCatalystTypeSerializerFactory(Serializer serializer) { - typeSerializer = new InternalSerializer<>(serializer); - } - - @Override - public TypeSerializer createSerializer(Class clazz) { - return typeSerializer; - } - - private class InternalSerializer implements TypeSerializer { - - private final Serializer serializer; - - InternalSerializer(Serializer serializer) { - this.serializer = serializer; - } - - @Override - public void write(T object, BufferOutput buffer, io.atomix.catalyst.serializer.Serializer serializer) { - try { - byte[] payload = this.serializer.encode(object); - buffer.writeInt(payload.length); - buffer.write(payload); - } catch (Exception e) { - log.warn("Failed to serialize {}", object, e); - throw Throwables.propagate(e); - } - } - - @Override - public T read(Class type, BufferInput buffer, io.atomix.catalyst.serializer.Serializer serializer) { - int size = buffer.readInt(); - try { - byte[] payload = new byte[size]; - buffer.read(payload); - return this.serializer.decode(payload); - } catch (Exception e) { - log.warn("Failed to deserialize as type {}. Payload size: {}", type, size, e); - throw Throwables.propagate(e); - } - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMapBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMapBuilder.java index 820174daf2..b5284de8f4 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMapBuilder.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMapBuilder.java @@ -41,7 +41,7 @@ public class DefaultConsistentMapBuilder extends ConsistentMapBuilder buildAsyncMap() { - AsyncConsistentMap map = primitiveCreator.newAsyncConsistentMap(name(), serializer(), executorSupplier()); + AsyncConsistentMap map = primitiveCreator.newAsyncConsistentMap(name(), serializer()); map = relaxedReadConsistency() ? DistributedPrimitives.newCachingMap(map) : map; map = readOnly() ? DistributedPrimitives.newUnmodifiableMap(map) : map; return meteringEnabled() ? DistributedPrimitives.newMeteredMap(map) : map; diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMultimapBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMultimapBuilder.java index ba7d673395..4b23253cf0 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMultimapBuilder.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentMultimapBuilder.java @@ -36,7 +36,7 @@ public class DefaultConsistentMultimapBuilder @Override public AsyncConsistentMultimap buildMultimap() { - return primitiveCreator.newAsyncConsistentSetMultimap(name(), serializer(), executorSupplier()); + return primitiveCreator.newAsyncConsistentSetMultimap(name(), serializer()); } @Override diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentTreeMapBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentTreeMapBuilder.java index 5e2a8b43c5..2aa906a35c 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentTreeMapBuilder.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultConsistentTreeMapBuilder.java @@ -35,7 +35,7 @@ public class DefaultConsistentTreeMapBuilder extends ConsistentTreeMapBuilder @Override public AsyncConsistentTreeMap buildTreeMap() { - return primitiveCreator.newAsyncConsistentTreeMap(name(), serializer(), executorSupplier()); + return primitiveCreator.newAsyncConsistentTreeMap(name(), serializer()); } @Override diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDistributedSetBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDistributedSetBuilder.java index 5e95180910..c17f91d1bf 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDistributedSetBuilder.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDistributedSetBuilder.java @@ -15,7 +15,6 @@ */ package org.onosproject.store.primitives.impl; -import java.util.concurrent.Executor; import java.util.function.Supplier; import org.onosproject.core.ApplicationId; @@ -53,12 +52,6 @@ public class DefaultDistributedSetBuilder extends DistributedSetBuilder { return this; } - @Override - public DistributedSetBuilder withExecutorSupplier(Supplier executorSupplier) { - mapBuilder.withExecutorSupplier(executorSupplier); - return this; - } - @Override public DistributedSetBuilder withPurgeOnUninstall() { mapBuilder.withPurgeOnUninstall(); diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDocumentTreeBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDocumentTreeBuilder.java index 65c0504564..8d21e60136 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDocumentTreeBuilder.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultDocumentTreeBuilder.java @@ -47,6 +47,6 @@ public class DefaultDocumentTreeBuilder extends DocumentTreeBuilder { @Deprecated @Override public AsyncDocumentTree build() { - return primitiveCreator.newAsyncDocumentTree(name(), serializer(), executorSupplier()); + return primitiveCreator.newAsyncDocumentTree(name(), serializer()); } } \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultLeaderElectorBuilder.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultLeaderElectorBuilder.java index 6f8f55d97a..69788f9a06 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultLeaderElectorBuilder.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DefaultLeaderElectorBuilder.java @@ -32,6 +32,6 @@ public class DefaultLeaderElectorBuilder extends LeaderElectorBuilder { @Override public AsyncLeaderElector build() { - return primitiveCreator.newAsyncLeaderElector(name(), executorSupplier()); + return primitiveCreator.newAsyncLeaderElector(name()); } } diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DelegatingCopycatClient.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DelegatingCopycatClient.java deleted file mode 100644 index 8955a6dbb5..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/DelegatingCopycatClient.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import io.atomix.catalyst.concurrent.Listener; -import io.atomix.catalyst.concurrent.ThreadContext; -import io.atomix.catalyst.serializer.Serializer; -import io.atomix.catalyst.transport.Address; -import io.atomix.catalyst.transport.Transport; -import io.atomix.copycat.Command; -import io.atomix.copycat.Query; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.copycat.session.Session; - -import java.util.Collection; -import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; - -/** - * {@code CopycatClient} that merely delegates control to - * another CopycatClient. - */ -public class DelegatingCopycatClient implements CopycatClient { - - protected final CopycatClient client; - - DelegatingCopycatClient(CopycatClient client) { - this.client = client; - } - - @Override - public State state() { - return client.state(); - } - - @Override - public Listener onStateChange(Consumer callback) { - return client.onStateChange(callback); - } - - @Override - public ThreadContext context() { - return client.context(); - } - - @Override - public Transport transport() { - return client.transport(); - } - - @Override - public Serializer serializer() { - return client.serializer(); - } - - @Override - public Session session() { - return client.session(); - } - - @Override - public CompletableFuture submit(Command command) { - return client.submit(command); - } - - @Override - public CompletableFuture submit(Query query) { - return client.submit(query); - } - - @Override - public Listener onEvent(String event, Runnable callback) { - return client.onEvent(event, callback); - } - - @Override - public Listener onEvent(String event, Consumer callback) { - return client.onEvent(event, callback); - } - - @Override - public CompletableFuture connect(Collection
members) { - return client.connect(members); - } - - @Override - public CompletableFuture recover() { - return client.recover(); - } - - @Override - public CompletableFuture close() { - return client.close(); - } -} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounter.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounter.java deleted file mode 100644 index 504fa75a71..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; - -import org.onosproject.store.service.AsyncAtomicCounter; - -/** - * {@link AsyncAtomicCounter} that executes asynchronous callbacks on a user provided - * {@link Executor}. - */ -public class ExecutingAsyncAtomicCounter extends ExecutingDistributedPrimitive implements AsyncAtomicCounter { - private final AsyncAtomicCounter delegateCounter; - - public ExecutingAsyncAtomicCounter( - AsyncAtomicCounter delegateCounter, Executor orderedExecutor, Executor threadPoolExecutor) { - super(delegateCounter, orderedExecutor, threadPoolExecutor); - this.delegateCounter = delegateCounter; - } - - @Override - public CompletableFuture incrementAndGet() { - return asyncFuture(delegateCounter.incrementAndGet()); - } - - @Override - public CompletableFuture getAndIncrement() { - return asyncFuture(delegateCounter.getAndIncrement()); - } - - @Override - public CompletableFuture getAndAdd(long delta) { - return asyncFuture(delegateCounter.getAndAdd(delta)); - } - - @Override - public CompletableFuture addAndGet(long delta) { - return asyncFuture(delegateCounter.addAndGet(delta)); - } - - @Override - public CompletableFuture get() { - return asyncFuture(delegateCounter.get()); - } - - @Override - public CompletableFuture set(long value) { - return asyncFuture(delegateCounter.set(value)); - } - - @Override - public CompletableFuture compareAndSet(long expectedValue, long updateValue) { - return asyncFuture(delegateCounter.compareAndSet(expectedValue, updateValue)); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounterMap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounterMap.java deleted file mode 100644 index a17a2f02c2..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicCounterMap.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; - -import org.onosproject.store.service.AsyncAtomicCounterMap; - -/** - * {@link org.onosproject.store.service.AsyncAtomicCounterMap} that executes asynchronous callbacks on a user provided - * {@link Executor}. - */ -public class ExecutingAsyncAtomicCounterMap - extends ExecutingDistributedPrimitive implements AsyncAtomicCounterMap { - private final AsyncAtomicCounterMap delegateMap; - - public ExecutingAsyncAtomicCounterMap( - AsyncAtomicCounterMap delegateMap, Executor orderedExecutor, Executor threadPoolExecutor) { - super(delegateMap, orderedExecutor, threadPoolExecutor); - this.delegateMap = delegateMap; - } - - @Override - public CompletableFuture incrementAndGet(K key) { - return asyncFuture(delegateMap.incrementAndGet(key)); - } - - @Override - public CompletableFuture decrementAndGet(K key) { - return asyncFuture(delegateMap.decrementAndGet(key)); - } - - @Override - public CompletableFuture getAndIncrement(K key) { - return asyncFuture(delegateMap.getAndIncrement(key)); - } - - @Override - public CompletableFuture getAndDecrement(K key) { - return asyncFuture(delegateMap.getAndDecrement(key)); - } - - @Override - public CompletableFuture addAndGet(K key, long delta) { - return asyncFuture(delegateMap.addAndGet(key, delta)); - } - - @Override - public CompletableFuture getAndAdd(K key, long delta) { - return asyncFuture(delegateMap.getAndAdd(key, delta)); - } - - @Override - public CompletableFuture get(K key) { - return asyncFuture(delegateMap.get(key)); - } - - @Override - public CompletableFuture put(K key, long newValue) { - return asyncFuture(delegateMap.put(key, newValue)); - } - - @Override - public CompletableFuture putIfAbsent(K key, long newValue) { - return asyncFuture(delegateMap.putIfAbsent(key, newValue)); - } - - @Override - public CompletableFuture replace(K key, long expectedOldValue, long newValue) { - return asyncFuture(delegateMap.replace(key, expectedOldValue, newValue)); - } - - @Override - public CompletableFuture remove(K key) { - return asyncFuture(delegateMap.remove(key)); - } - - @Override - public CompletableFuture remove(K key, long value) { - return asyncFuture(delegateMap.remove(key, value)); - } - - @Override - public CompletableFuture size() { - return asyncFuture(delegateMap.size()); - } - - @Override - public CompletableFuture isEmpty() { - return asyncFuture(delegateMap.isEmpty()); - } - - @Override - public CompletableFuture clear() { - return asyncFuture(delegateMap.clear()); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicIdGenerator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicIdGenerator.java deleted file mode 100644 index baf2b8aa50..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicIdGenerator.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; - -import org.onosproject.store.service.AsyncAtomicIdGenerator; - -/** - * {@link AsyncAtomicIdGenerator} that executes asynchronous callbacks on a user provided - * {@link Executor}. - */ -public class ExecutingAsyncAtomicIdGenerator extends ExecutingDistributedPrimitive implements AsyncAtomicIdGenerator { - private final AsyncAtomicIdGenerator delegateIdGenerator; - - public ExecutingAsyncAtomicIdGenerator( - AsyncAtomicIdGenerator delegateIdGenerator, Executor orderedExecutor, Executor threadPoolExecutor) { - super(delegateIdGenerator, orderedExecutor, threadPoolExecutor); - this.delegateIdGenerator = delegateIdGenerator; - } - - @Override - public CompletableFuture nextId() { - return asyncFuture(delegateIdGenerator.nextId()); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicValue.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicValue.java deleted file mode 100644 index c8bba5239a..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncAtomicValue.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; - -import com.google.common.collect.Maps; -import org.onosproject.store.service.AsyncAtomicValue; -import org.onosproject.store.service.AtomicValueEventListener; - -/** - * {@link AsyncAtomicValue} that executes asynchronous callbacks on a user provided - * {@link Executor}. - */ -public class ExecutingAsyncAtomicValue extends ExecutingDistributedPrimitive implements AsyncAtomicValue { - private final AsyncAtomicValue delegateValue; - private final Executor orderedExecutor; - private final Map, AtomicValueEventListener> listenerMap = Maps.newConcurrentMap(); - - public ExecutingAsyncAtomicValue( - AsyncAtomicValue delegateValue, Executor orderedExecutor, Executor threadPoolExecutor) { - super(delegateValue, orderedExecutor, threadPoolExecutor); - this.delegateValue = delegateValue; - this.orderedExecutor = orderedExecutor; - } - - @Override - public CompletableFuture compareAndSet(V expect, V update) { - return asyncFuture(delegateValue.compareAndSet(expect, update)); - } - - @Override - public CompletableFuture get() { - return asyncFuture(delegateValue.get()); - } - - @Override - public CompletableFuture getAndSet(V value) { - return asyncFuture(delegateValue.getAndSet(value)); - } - - @Override - public CompletableFuture set(V value) { - return asyncFuture(delegateValue.set(value)); - } - - @Override - public CompletableFuture addListener(AtomicValueEventListener listener) { - AtomicValueEventListener wrappedListener = e -> orderedExecutor.execute(() -> listener.event(e)); - listenerMap.put(listener, wrappedListener); - return asyncFuture(delegateValue.addListener(wrappedListener)); - } - - @Override - public CompletableFuture removeListener(AtomicValueEventListener listener) { - AtomicValueEventListener wrappedListener = listenerMap.remove(listener); - if (wrappedListener != null) { - return asyncFuture(delegateValue.removeListener(wrappedListener)); - } - return CompletableFuture.completedFuture(null); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMap.java deleted file mode 100644 index d955121a41..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMap.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import java.util.Collection; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.BiFunction; -import java.util.function.Predicate; - -import org.onosproject.store.primitives.MapUpdate; -import org.onosproject.store.primitives.TransactionId; -import org.onosproject.store.service.AsyncConsistentMap; -import org.onosproject.store.service.MapEventListener; -import org.onosproject.store.service.TransactionLog; -import org.onosproject.store.service.Version; -import org.onosproject.store.service.Versioned; - -/** - * An {@link org.onosproject.store.service.AsyncConsistentMap} that completes asynchronous calls on a provided - * {@link Executor}. - */ -public class ExecutingAsyncConsistentMap - extends ExecutingDistributedPrimitive implements AsyncConsistentMap { - private final AsyncConsistentMap delegateMap; - - public ExecutingAsyncConsistentMap( - AsyncConsistentMap delegateMap, Executor orderedExecutor, Executor threadPoolExecutor) { - super(delegateMap, orderedExecutor, threadPoolExecutor); - this.delegateMap = delegateMap; - } - - @Override - public CompletableFuture size() { - return asyncFuture(delegateMap.size()); - } - - @Override - public CompletableFuture containsKey(K key) { - return asyncFuture(delegateMap.containsKey(key)); - } - - @Override - public CompletableFuture containsValue(V value) { - return asyncFuture(delegateMap.containsValue(value)); - } - - @Override - public CompletableFuture> get(K key) { - return asyncFuture(delegateMap.get(key)); - } - - @Override - public CompletableFuture> getOrDefault(K key, V defaultValue) { - return asyncFuture(delegateMap.getOrDefault(key, defaultValue)); - } - - @Override - public CompletableFuture> computeIf( - K key, Predicate condition, BiFunction remappingFunction) { - return asyncFuture(delegateMap.computeIf(key, condition, remappingFunction)); - } - - @Override - public CompletableFuture> put(K key, V value) { - return asyncFuture(delegateMap.put(key, value)); - } - - @Override - public CompletableFuture> putAndGet(K key, V value) { - return asyncFuture(delegateMap.putAndGet(key, value)); - } - - @Override - public CompletableFuture> remove(K key) { - return asyncFuture(delegateMap.remove(key)); - } - - @Override - public CompletableFuture clear() { - return asyncFuture(delegateMap.clear()); - } - - @Override - public CompletableFuture> keySet() { - return asyncFuture(delegateMap.keySet()); - } - - @Override - public CompletableFuture>> values() { - return asyncFuture(delegateMap.values()); - } - - @Override - public CompletableFuture>>> entrySet() { - return asyncFuture(delegateMap.entrySet()); - } - - @Override - public CompletableFuture> putIfAbsent(K key, V value) { - return asyncFuture(delegateMap.putIfAbsent(key, value)); - } - - @Override - public CompletableFuture remove(K key, V value) { - return asyncFuture(delegateMap.remove(key, value)); - } - - @Override - public CompletableFuture remove(K key, long version) { - return asyncFuture(delegateMap.remove(key, version)); - } - - @Override - public CompletableFuture> replace(K key, V value) { - return asyncFuture(delegateMap.replace(key, value)); - } - - @Override - public CompletableFuture replace(K key, V oldValue, V newValue) { - return asyncFuture(delegateMap.replace(key, oldValue, newValue)); - } - - @Override - public CompletableFuture replace(K key, long oldVersion, V newValue) { - return asyncFuture(delegateMap.replace(key, oldVersion, newValue)); - } - - @Override - public CompletableFuture begin(TransactionId transactionId) { - return asyncFuture(delegateMap.begin(transactionId)); - } - - @Override - public CompletableFuture prepare(TransactionLog> transactionLog) { - return asyncFuture(delegateMap.prepare(transactionLog)); - } - - @Override - public CompletableFuture commit(TransactionId transactionId) { - return asyncFuture(delegateMap.commit(transactionId)); - } - - @Override - public CompletableFuture rollback(TransactionId transactionId) { - return asyncFuture(delegateMap.rollback(transactionId)); - } - - @Override - public CompletableFuture prepareAndCommit(TransactionLog> transactionLog) { - return asyncFuture(delegateMap.prepareAndCommit(transactionLog)); - } - - @Override - public CompletableFuture addListener(MapEventListener listener) { - return addListener(listener); - } - - @Override - public CompletableFuture addListener(MapEventListener listener, Executor executor) { - return asyncFuture(delegateMap.addListener(listener, executor)); - } - - @Override - public CompletableFuture removeListener(MapEventListener listener) { - return asyncFuture(delegateMap.removeListener(listener)); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMultimap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMultimap.java deleted file mode 100644 index 20455869f4..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentMultimap.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import com.google.common.collect.Multiset; -import org.onosproject.store.service.AsyncConsistentMultimap; -import org.onosproject.store.service.MultimapEventListener; -import org.onosproject.store.service.Versioned; - -import java.util.Collection; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; - -/** - * {@link org.onosproject.store.service.AsyncConsistentMultimap} that executes asynchronous callbacks on a provided - * {@link Executor}. - */ -public class ExecutingAsyncConsistentMultimap - extends ExecutingDistributedPrimitive implements AsyncConsistentMultimap { - private final AsyncConsistentMultimap delegateMap; - - public ExecutingAsyncConsistentMultimap( - AsyncConsistentMultimap delegateMap, Executor orderedExecutor, Executor threadPoolExecutor) { - super(delegateMap, orderedExecutor, threadPoolExecutor); - this.delegateMap = delegateMap; - } - - @Override - public CompletableFuture size() { - return asyncFuture(delegateMap.size()); - } - - @Override - public CompletableFuture isEmpty() { - return asyncFuture(delegateMap.isEmpty()); - } - - @Override - public CompletableFuture containsKey(K key) { - return asyncFuture(delegateMap.containsKey(key)); - } - - @Override - public CompletableFuture containsValue(V value) { - return asyncFuture(delegateMap.containsValue(value)); - } - - @Override - public CompletableFuture containsEntry(K key, V value) { - return asyncFuture(delegateMap.containsEntry(key, value)); - } - - @Override - public CompletableFuture put(K key, V value) { - return asyncFuture(delegateMap.put(key, value)); - } - - @Override - public CompletableFuture remove(K key, V value) { - return asyncFuture(delegateMap.remove(key, value)); - } - - @Override - public CompletableFuture removeAll(K key, Collection values) { - return asyncFuture(delegateMap.removeAll(key, values)); - } - - @Override - public CompletableFuture>> removeAll(K key) { - return asyncFuture(delegateMap.removeAll(key)); - } - - @Override - public CompletableFuture putAll(K key, Collection values) { - return asyncFuture(delegateMap.putAll(key, values)); - } - - @Override - public CompletableFuture>> replaceValues(K key, Collection values) { - return asyncFuture(delegateMap.replaceValues(key, values)); - } - - @Override - public CompletableFuture clear() { - return asyncFuture(delegateMap.clear()); - } - - @Override - public CompletableFuture>> get(K key) { - return asyncFuture(delegateMap.get(key)); - } - - @Override - public CompletableFuture> keySet() { - return asyncFuture(delegateMap.keySet()); - } - - @Override - public CompletableFuture> keys() { - return asyncFuture(delegateMap.keys()); - } - - @Override - public CompletableFuture> values() { - return asyncFuture(delegateMap.values()); - } - - @Override - public CompletableFuture>> entries() { - return asyncFuture(delegateMap.entries()); - } - - @Override - public CompletableFuture addListener(MultimapEventListener listener, Executor executor) { - return asyncFuture(delegateMap.addListener(listener, executor)); - } - - @Override - public CompletableFuture removeListener(MultimapEventListener listener) { - return asyncFuture(delegateMap.removeListener(listener)); - } - - @Override - public CompletableFuture>> asMap() { - return asyncFuture(delegateMap.asMap()); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentTreeMap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentTreeMap.java deleted file mode 100644 index 3a4fe85e54..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncConsistentTreeMap.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import java.util.Collection; -import java.util.Map; -import java.util.NavigableMap; -import java.util.NavigableSet; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.BiFunction; -import java.util.function.Predicate; - -import org.onosproject.store.primitives.MapUpdate; -import org.onosproject.store.primitives.TransactionId; -import org.onosproject.store.service.AsyncConsistentTreeMap; -import org.onosproject.store.service.MapEventListener; -import org.onosproject.store.service.TransactionLog; -import org.onosproject.store.service.Version; -import org.onosproject.store.service.Versioned; - -/** - * {@link org.onosproject.store.service.AsyncConsistentTreeMap} that executes asynchronous callbacks on a provided - * {@link Executor}. - */ -public class ExecutingAsyncConsistentTreeMap - extends ExecutingDistributedPrimitive implements AsyncConsistentTreeMap { - private final AsyncConsistentTreeMap delegateMap; - - public ExecutingAsyncConsistentTreeMap( - AsyncConsistentTreeMap delegateMap, Executor orderedExecutor, Executor threadPoolExecutor) { - super(delegateMap, orderedExecutor, threadPoolExecutor); - this.delegateMap = delegateMap; - } - - @Override - public CompletableFuture firstKey() { - return asyncFuture(delegateMap.firstKey()); - } - - @Override - public CompletableFuture lastKey() { - return asyncFuture(delegateMap.lastKey()); - } - - @Override - public CompletableFuture>> ceilingEntry(String key) { - return asyncFuture(delegateMap.ceilingEntry(key)); - } - - @Override - public CompletableFuture>> floorEntry(String key) { - return asyncFuture(delegateMap.floorEntry(key)); - } - - @Override - public CompletableFuture>> higherEntry(String key) { - return asyncFuture(delegateMap.higherEntry(key)); - } - - @Override - public CompletableFuture>> lowerEntry(String key) { - return asyncFuture(delegateMap.lowerEntry(key)); - } - - @Override - public CompletableFuture>> firstEntry() { - return asyncFuture(delegateMap.firstEntry()); - } - - @Override - public CompletableFuture size() { - return asyncFuture(delegateMap.size()); - } - - @Override - public CompletableFuture>> lastEntry() { - return asyncFuture(delegateMap.lastEntry()); - } - - @Override - public CompletableFuture>> pollFirstEntry() { - return asyncFuture(delegateMap.pollFirstEntry()); - } - - @Override - public CompletableFuture containsKey(String key) { - return asyncFuture(delegateMap.containsKey(key)); - } - - @Override - public CompletableFuture>> pollLastEntry() { - return asyncFuture(delegateMap.pollLastEntry()); - } - - @Override - public CompletableFuture lowerKey(String key) { - return asyncFuture(delegateMap.lowerKey(key)); - } - - @Override - public CompletableFuture containsValue(V value) { - return asyncFuture(delegateMap.containsValue(value)); - } - - @Override - public CompletableFuture floorKey(String key) { - return asyncFuture(delegateMap.floorKey(key)); - } - - @Override - public CompletableFuture ceilingKey(String key) { - return asyncFuture(delegateMap.ceilingKey(key)); - } - - @Override - public CompletableFuture> get(String key) { - return asyncFuture(delegateMap.get(key)); - } - - @Override - public CompletableFuture> getOrDefault(String key, V defaultValue) { - return asyncFuture(delegateMap.getOrDefault(key, defaultValue)); - } - - @Override - public CompletableFuture higherKey(String key) { - return asyncFuture(delegateMap.higherKey(key)); - } - - @Override - public CompletableFuture> navigableKeySet() { - return asyncFuture(delegateMap.navigableKeySet()); - } - - @Override - public CompletableFuture> subMap( - String upperKey, String lowerKey, boolean inclusiveUpper, boolean inclusiveLower) { - return asyncFuture(delegateMap.subMap(upperKey, lowerKey, inclusiveUpper, inclusiveLower)); - } - - @Override - public CompletableFuture> computeIf( - String key, Predicate condition, - BiFunction remappingFunction) { - return asyncFuture(delegateMap.computeIf(key, condition, remappingFunction)); - } - - @Override - public CompletableFuture> put(String key, V value) { - return asyncFuture(delegateMap.put(key, value)); - } - - @Override - public CompletableFuture> putAndGet(String key, V value) { - return asyncFuture(delegateMap.putAndGet(key, value)); - } - - @Override - public CompletableFuture> remove(String key) { - return asyncFuture(delegateMap.remove(key)); - } - - @Override - public CompletableFuture clear() { - return asyncFuture(delegateMap.clear()); - } - - @Override - public CompletableFuture> keySet() { - return asyncFuture(delegateMap.keySet()); - } - - @Override - public CompletableFuture>> values() { - return asyncFuture(delegateMap.values()); - } - - @Override - public CompletableFuture>>> entrySet() { - return asyncFuture(delegateMap.entrySet()); - } - - @Override - public CompletableFuture> putIfAbsent(String key, V value) { - return asyncFuture(delegateMap.putIfAbsent(key, value)); - } - - @Override - public CompletableFuture remove(String key, V value) { - return asyncFuture(delegateMap.remove(key, value)); - } - - @Override - public CompletableFuture remove(String key, long version) { - return asyncFuture(delegateMap.remove(key, version)); - } - - @Override - public CompletableFuture> replace(String key, V value) { - return asyncFuture(delegateMap.replace(key, value)); - } - - @Override - public CompletableFuture replace(String key, V oldValue, V newValue) { - return asyncFuture(delegateMap.replace(key, oldValue, newValue)); - } - - @Override - public CompletableFuture replace(String key, long oldVersion, V newValue) { - return asyncFuture(delegateMap.replace(key, oldVersion, newValue)); - } - - @Override - public CompletableFuture begin(TransactionId transactionId) { - return asyncFuture(delegateMap.begin(transactionId)); - } - - @Override - public CompletableFuture prepare(TransactionLog> transactionLog) { - return asyncFuture(delegateMap.prepare(transactionLog)); - } - - @Override - public CompletableFuture commit(TransactionId transactionId) { - return asyncFuture(delegateMap.commit(transactionId)); - } - - @Override - public CompletableFuture rollback(TransactionId transactionId) { - return asyncFuture(delegateMap.rollback(transactionId)); - } - - @Override - public CompletableFuture prepareAndCommit(TransactionLog> transactionLog) { - return asyncFuture(delegateMap.prepareAndCommit(transactionLog)); - } - - @Override - public CompletableFuture addListener(MapEventListener listener) { - return addListener(listener); - } - - @Override - public CompletableFuture addListener(MapEventListener listener, Executor executor) { - return asyncFuture(delegateMap.addListener(listener, executor)); - } - - @Override - public CompletableFuture removeListener(MapEventListener listener) { - return asyncFuture(delegateMap.removeListener(listener)); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncDocumentTree.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncDocumentTree.java deleted file mode 100644 index f6fc3d106b..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncDocumentTree.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; - -import com.google.common.collect.Maps; -import org.onosproject.store.service.AsyncDocumentTree; -import org.onosproject.store.service.DocumentPath; -import org.onosproject.store.service.DocumentTreeListener; -import org.onosproject.store.service.Versioned; - -/** - * {@link AsyncDocumentTree} that executes asynchronous callbacks on a user provided - * {@link Executor}. - */ -public class ExecutingAsyncDocumentTree extends ExecutingDistributedPrimitive implements AsyncDocumentTree { - private final AsyncDocumentTree delegateTree; - private final Executor orderedExecutor; - private final Map, DocumentTreeListener> listenerMap = Maps.newConcurrentMap(); - - public ExecutingAsyncDocumentTree( - AsyncDocumentTree delegateTree, Executor orderedExecutor, Executor threadPoolExecutor) { - super(delegateTree, orderedExecutor, threadPoolExecutor); - this.delegateTree = delegateTree; - this.orderedExecutor = orderedExecutor; - } - - @Override - public DocumentPath root() { - return delegateTree.root(); - } - - @Override - public CompletableFuture>> getChildren(DocumentPath path) { - return asyncFuture(delegateTree.getChildren(path)); - } - - @Override - public CompletableFuture> get(DocumentPath path) { - return asyncFuture(delegateTree.get(path)); - } - - @Override - public CompletableFuture> set(DocumentPath path, V value) { - return asyncFuture(delegateTree.set(path, value)); - } - - @Override - public CompletableFuture create(DocumentPath path, V value) { - return asyncFuture(delegateTree.create(path, value)); - } - - @Override - public CompletableFuture createRecursive(DocumentPath path, V value) { - return asyncFuture(delegateTree.createRecursive(path, value)); - } - - @Override - public CompletableFuture replace(DocumentPath path, V newValue, long version) { - return asyncFuture(delegateTree.replace(path, newValue, version)); - } - - @Override - public CompletableFuture replace(DocumentPath path, V newValue, V currentValue) { - return asyncFuture(delegateTree.replace(path, newValue, currentValue)); - } - - @Override - public CompletableFuture> removeNode(DocumentPath path) { - return asyncFuture(delegateTree.removeNode(path)); - } - - @Override - public CompletableFuture addListener(DocumentPath path, DocumentTreeListener listener) { - DocumentTreeListener wrappedListener = e -> orderedExecutor.execute(() -> listener.event(e)); - listenerMap.put(listener, wrappedListener); - return asyncFuture(delegateTree.addListener(path, wrappedListener)); - } - - @Override - public CompletableFuture removeListener(DocumentTreeListener listener) { - DocumentTreeListener wrappedListener = listenerMap.remove(listener); - if (wrappedListener != null) { - return asyncFuture(delegateTree.removeListener(wrappedListener)); - } - return CompletableFuture.completedFuture(null); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncLeaderElector.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncLeaderElector.java deleted file mode 100644 index ba7cb812dc..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingAsyncLeaderElector.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -import com.google.common.collect.Maps; -import org.onosproject.cluster.Leadership; -import org.onosproject.cluster.NodeId; -import org.onosproject.event.Change; -import org.onosproject.store.service.AsyncLeaderElector; - -/** - * {@link AsyncLeaderElector} that executes asynchronous callbacks on a user provided - * {@link Executor}. - */ -public class ExecutingAsyncLeaderElector extends ExecutingDistributedPrimitive implements AsyncLeaderElector { - private final AsyncLeaderElector delegateElector; - private final Executor orderedExecutor; - private final Map>, Consumer>> listenerMap = Maps.newConcurrentMap(); - - public ExecutingAsyncLeaderElector( - AsyncLeaderElector delegateElector, Executor orderedExecutor, Executor threadPoolExecutor) { - super(delegateElector, orderedExecutor, threadPoolExecutor); - this.delegateElector = delegateElector; - this.orderedExecutor = orderedExecutor; - } - - @Override - public CompletableFuture run(String topic, NodeId nodeId) { - return asyncFuture(delegateElector.run(topic, nodeId)); - } - - @Override - public CompletableFuture withdraw(String topic) { - return asyncFuture(delegateElector.withdraw(topic)); - } - - @Override - public CompletableFuture anoint(String topic, NodeId nodeId) { - return asyncFuture(delegateElector.anoint(topic, nodeId)); - } - - @Override - public CompletableFuture evict(NodeId nodeId) { - return asyncFuture(delegateElector.evict(nodeId)); - } - - @Override - public CompletableFuture promote(String topic, NodeId nodeId) { - return asyncFuture(delegateElector.promote(topic, nodeId)); - } - - @Override - public CompletableFuture getLeadership(String topic) { - return asyncFuture(delegateElector.getLeadership(topic)); - } - - @Override - public CompletableFuture> getLeaderships() { - return asyncFuture(delegateElector.getLeaderships()); - } - - @Override - public CompletableFuture addChangeListener(Consumer> listener) { - Consumer> wrappedListener = e -> orderedExecutor.execute(() -> listener.accept(e)); - listenerMap.put(listener, wrappedListener); - return asyncFuture(delegateElector.addChangeListener(wrappedListener)); - } - - @Override - public CompletableFuture removeChangeListener(Consumer> listener) { - Consumer> wrappedListener = listenerMap.remove(listener); - if (wrappedListener != null) { - return asyncFuture(delegateElector.removeChangeListener(wrappedListener)); - } - return CompletableFuture.completedFuture(null); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingDistributedPrimitive.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingDistributedPrimitive.java deleted file mode 100644 index 836a6824cb..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingDistributedPrimitive.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -import com.google.common.collect.Maps; -import org.onlab.util.Tools; -import org.onosproject.store.service.DistributedPrimitive; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Base class for primitives that delegate asynchronous callbacks to a user provided {@link Executor}. - */ -public abstract class ExecutingDistributedPrimitive - extends DelegatingDistributedPrimitive { - private final DistributedPrimitive primitive; - private final Executor orderedExecutor; - private final Executor threadPoolExecutor; - private final Map, Consumer> listenerMap = Maps.newConcurrentMap(); - - protected ExecutingDistributedPrimitive( - DistributedPrimitive primitive, Executor orderedExecutor, Executor threadPoolExecutor) { - super(primitive); - this.primitive = primitive; - this.orderedExecutor = checkNotNull(orderedExecutor); - this.threadPoolExecutor = checkNotNull(threadPoolExecutor); - } - - /** - * Creates a future to be completed asynchronously on the provided ordered and thread pool executors. - * - * @param future the future to be completed asynchronously - * @param future result type - * @return a new {@link CompletableFuture} to be completed asynchronously using the primitive thread model - */ - protected CompletableFuture asyncFuture(CompletableFuture future) { - return Tools.orderedFuture(future, orderedExecutor, threadPoolExecutor); - } - - @Override - public CompletableFuture destroy() { - return asyncFuture(primitive.destroy()); - } - - @Override - public void addStatusChangeListener(Consumer listener) { - Consumer wrappedListener = - status -> orderedExecutor.execute(() -> listener.accept(status)); - listenerMap.put(listener, wrappedListener); - primitive.addStatusChangeListener(wrappedListener); - } - - @Override - public void removeStatusChangeListener(Consumer listener) { - Consumer wrappedListener = listenerMap.remove(listener); - if (wrappedListener != null) { - primitive.removeStatusChangeListener(wrappedListener); - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingWorkQueue.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingWorkQueue.java deleted file mode 100644 index e6290b89b6..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/ExecutingWorkQueue.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import java.util.Collection; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -import org.onosproject.store.service.AsyncAtomicValue; -import org.onosproject.store.service.Task; -import org.onosproject.store.service.WorkQueue; -import org.onosproject.store.service.WorkQueueStats; - -/** - * {@link AsyncAtomicValue} that executes asynchronous callbacks on a user provided - * {@link Executor}. - */ -public class ExecutingWorkQueue extends ExecutingDistributedPrimitive implements WorkQueue { - private final WorkQueue delegateQueue; - - public ExecutingWorkQueue(WorkQueue delegateQueue, Executor orderedExecutor, Executor threadPoolExecutor) { - super(delegateQueue, orderedExecutor, threadPoolExecutor); - this.delegateQueue = delegateQueue; - } - - @Override - public CompletableFuture addMultiple(Collection items) { - return asyncFuture(delegateQueue.addMultiple(items)); - } - - @Override - public CompletableFuture>> take(int maxItems) { - return asyncFuture(delegateQueue.take(maxItems)); - } - - @Override - public CompletableFuture complete(Collection taskIds) { - return asyncFuture(delegateQueue.complete(taskIds)); - } - - @Override - public CompletableFuture registerTaskProcessor( - Consumer taskProcessor, int parallelism, Executor executor) { - return asyncFuture(delegateQueue.registerTaskProcessor(taskProcessor, parallelism, executor)); - } - - @Override - public CompletableFuture stopProcessing() { - return asyncFuture(delegateQueue.stopProcessing()); - } - - @Override - public CompletableFuture stats() { - return asyncFuture(delegateQueue.stats()); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/FederatedDistributedPrimitiveCreator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/FederatedDistributedPrimitiveCreator.java index 556484421b..ea8d075194 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/FederatedDistributedPrimitiveCreator.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/FederatedDistributedPrimitiveCreator.java @@ -40,8 +40,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; -import java.util.concurrent.Executor; -import java.util.function.Supplier; import static com.google.common.base.Preconditions.checkNotNull; @@ -60,13 +58,12 @@ public class FederatedDistributedPrimitiveCreator implements DistributedPrimitiv } @Override - public AsyncConsistentMap newAsyncConsistentMap( - String name, Serializer serializer, Supplier executorSupplier) { + public AsyncConsistentMap newAsyncConsistentMap(String name, Serializer serializer) { checkNotNull(name); checkNotNull(serializer); Map> maps = Maps.transformValues(members, - partition -> partition.newAsyncConsistentMap(name, serializer, executorSupplier)); + partition -> partition.newAsyncConsistentMap(name, serializer)); Hasher hasher = key -> { int hashCode = Hashing.sha256().hashBytes(serializer.encode(key)).asInt(); return sortedMemberPartitionIds.get(Math.abs(hashCode) % members.size()); @@ -75,51 +72,46 @@ public class FederatedDistributedPrimitiveCreator implements DistributedPrimitiv } @Override - public AsyncConsistentTreeMap newAsyncConsistentTreeMap( - String name, Serializer serializer, Supplier executorSupplier) { - return getCreator(name).newAsyncConsistentTreeMap(name, serializer, executorSupplier); + public AsyncConsistentTreeMap newAsyncConsistentTreeMap(String name, Serializer serializer) { + return getCreator(name).newAsyncConsistentTreeMap(name, serializer); } @Override - public AsyncConsistentMultimap newAsyncConsistentSetMultimap( - String name, Serializer serializer, Supplier executorSupplier) { - return getCreator(name).newAsyncConsistentSetMultimap(name, serializer, executorSupplier); + public AsyncConsistentMultimap newAsyncConsistentSetMultimap(String name, Serializer serializer) { + return getCreator(name).newAsyncConsistentSetMultimap(name, serializer); } @Override - public AsyncDistributedSet newAsyncDistributedSet( - String name, Serializer serializer, Supplier executorSupplier) { - return DistributedPrimitives.newSetFromMap(newAsyncConsistentMap(name, serializer, executorSupplier)); + public AsyncDistributedSet newAsyncDistributedSet(String name, Serializer serializer) { + return DistributedPrimitives.newSetFromMap(newAsyncConsistentMap(name, serializer)); } @Override - public AsyncAtomicCounterMap newAsyncAtomicCounterMap( - String name, Serializer serializer, Supplier executorSupplier) { - return getCreator(name).newAsyncAtomicCounterMap(name, serializer, executorSupplier); + public AsyncAtomicCounterMap newAsyncAtomicCounterMap(String name, Serializer serializer) { + return getCreator(name).newAsyncAtomicCounterMap(name, serializer); } @Override - public AsyncAtomicCounter newAsyncCounter(String name, Supplier executorSupplier) { - return getCreator(name).newAsyncCounter(name, executorSupplier); + public AsyncAtomicCounter newAsyncCounter(String name) { + return getCreator(name).newAsyncCounter(name); } @Override - public AsyncAtomicIdGenerator newAsyncIdGenerator(String name, Supplier executorSupplier) { - return getCreator(name).newAsyncIdGenerator(name, executorSupplier); + public AsyncAtomicIdGenerator newAsyncIdGenerator(String name) { + return getCreator(name).newAsyncIdGenerator(name); } @Override - public AsyncAtomicValue newAsyncAtomicValue( - String name, Serializer serializer, Supplier executorSupplier) { - return getCreator(name).newAsyncAtomicValue(name, serializer, executorSupplier); + public AsyncAtomicValue newAsyncAtomicValue(String name, Serializer serializer) { + return getCreator(name).newAsyncAtomicValue(name, serializer); } @Override - public AsyncLeaderElector newAsyncLeaderElector(String name, Supplier executorSupplier) { + public AsyncLeaderElector newAsyncLeaderElector(String name) { checkNotNull(name); Map leaderElectors = Maps.transformValues(members, - partition -> partition.newAsyncLeaderElector(name, executorSupplier)); + partition -> partition.newAsyncLeaderElector(name)); Hasher hasher = topic -> { int hashCode = Hashing.sha256().hashString(topic, Charsets.UTF_8).asInt(); return sortedMemberPartitionIds.get(Math.abs(hashCode) % members.size()); @@ -128,14 +120,13 @@ public class FederatedDistributedPrimitiveCreator implements DistributedPrimitiv } @Override - public WorkQueue newWorkQueue(String name, Serializer serializer, Supplier executorSupplier) { - return getCreator(name).newWorkQueue(name, serializer, executorSupplier); + public WorkQueue newWorkQueue(String name, Serializer serializer) { + return getCreator(name).newWorkQueue(name, serializer); } @Override - public AsyncDocumentTree newAsyncDocumentTree( - String name, Serializer serializer, Supplier executorSupplier) { - return getCreator(name).newAsyncDocumentTree(name, serializer, executorSupplier); + public AsyncDocumentTree newAsyncDocumentTree(String name, Serializer serializer) { + return getCreator(name).newAsyncDocumentTree(name, serializer); } @Override diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/OnosCopycatClient.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/OnosCopycatClient.java deleted file mode 100644 index d541bb1b6c..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/OnosCopycatClient.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; -import static org.onlab.util.Tools.groupedThreads; -import static org.onlab.util.Tools.maxPriority; -import static org.slf4j.LoggerFactory.getLogger; - -import java.net.ConnectException; -import java.nio.channels.ClosedChannelException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Predicate; - -import org.onlab.util.Tools; -import org.onosproject.store.service.StorageException; -import org.slf4j.Logger; - -import com.google.common.base.Throwables; - -import io.atomix.catalyst.transport.TransportException; -import io.atomix.copycat.Query; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.copycat.error.QueryException; -import io.atomix.copycat.error.UnknownSessionException; -import io.atomix.copycat.session.ClosedSessionException; - -/** - * Custom {@code CopycatClient} for injecting additional logic that runs before/after operation submission. - */ -public class OnosCopycatClient extends DelegatingCopycatClient { - - private final int maxRetries; - private final long delayBetweenRetriesMillis; - private final ScheduledExecutorService executor; - private final Logger log = getLogger(getClass()); - - private final Predicate retryableCheck = e -> e instanceof ConnectException - || e instanceof TimeoutException - || e instanceof TransportException - || e instanceof ClosedChannelException - || e instanceof QueryException - || e instanceof UnknownSessionException - || e instanceof ClosedSessionException - || e instanceof StorageException.Unavailable; - - OnosCopycatClient(CopycatClient client, int maxRetries, long delayBetweenRetriesMillis) { - super(client); - this.maxRetries = maxRetries; - this.delayBetweenRetriesMillis = delayBetweenRetriesMillis; - this.executor = newSingleThreadScheduledExecutor(maxPriority(groupedThreads("OnosCopycat", "client", log))); - } - - @Override - public CompletableFuture close() { - executor.shutdown(); - return super.close(); - } - - @Override - public CompletableFuture submit(Query query) { - if (state() == State.CLOSED) { - return Tools.exceptionalFuture(new StorageException.Unavailable()); - } - CompletableFuture future = new CompletableFuture<>(); - executor.execute(() -> submit(query, 1, future)); - return future; - } - - private void submit(Query query, int attemptIndex, CompletableFuture future) { - client.submit(query).whenComplete((r, e) -> { - if (e != null) { - if (attemptIndex < maxRetries + 1 && retryableCheck.test(Throwables.getRootCause(e))) { - log.debug("Retry attempt ({} of {}). Failure due to {}", - attemptIndex, maxRetries, Throwables.getRootCause(e).getClass()); - executor.schedule(() -> - submit(query, attemptIndex + 1, future), delayBetweenRetriesMillis, TimeUnit.MILLISECONDS); - } else { - future.completeExceptionally(e); - } - } else { - future.complete(r); - } - }); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/PartitionManager.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/PartitionManager.java index bacda00d78..2201bb58f1 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/PartitionManager.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/PartitionManager.java @@ -16,19 +16,16 @@ package org.onosproject.store.primitives.impl; -import static org.onlab.util.Tools.groupedThreads; -import static org.slf4j.LoggerFactory.getLogger; - import java.io.File; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -46,7 +43,7 @@ import org.onosproject.cluster.NodeId; import org.onosproject.cluster.PartitionDiff; import org.onosproject.cluster.PartitionId; import org.onosproject.event.AbstractListenerManager; -import org.onosproject.store.cluster.messaging.MessagingService; +import org.onosproject.store.cluster.messaging.ClusterCommunicationService; import org.onosproject.store.primitives.DistributedPrimitiveCreator; import org.onosproject.store.primitives.PartitionAdminService; import org.onosproject.store.primitives.PartitionEvent; @@ -56,11 +53,9 @@ import org.onosproject.store.service.PartitionClientInfo; import org.onosproject.store.service.PartitionInfo; import org.slf4j.Logger; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; - import static org.onosproject.security.AppGuard.checkPermission; import static org.onosproject.security.AppPermission.Type.PARTITION_READ; +import static org.slf4j.LoggerFactory.getLogger; /** * Implementation of {@code PartitionService} and {@code PartitionAdminService}. @@ -73,7 +68,7 @@ public class PartitionManager extends AbstractListenerManager partitions = Maps.newConcurrentMap(); private final AtomicReference currentClusterMetadata = new AtomicReference<>(); private final InternalClusterMetadataListener metadataListener = new InternalClusterMetadataListener(); - private final ExecutorService sharedPrimitiveExecutor = Executors.newFixedThreadPool( - Runtime.getRuntime().availableProcessors(), - groupedThreads("onos/primitives", "primitive-events", log)); @Activate public void activate() { @@ -96,10 +88,8 @@ public class PartitionManager extends AbstractListenerManager partitions.put(partition.getId(), new StoragePartition(partition, - messagingService, + clusterCommunicator, clusterService, - CatalystSerializers.getSerializer(), - sharedPrimitiveExecutor, new File(System.getProperty("karaf.data") + "/partitions/" + partition.getId())))); CompletableFuture openFuture = CompletableFuture.allOf(partitions.values() diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftClientCommunicator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftClientCommunicator.java new file mode 100644 index 0000000000..099e17af84 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftClientCommunicator.java @@ -0,0 +1,112 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.impl; + +import java.util.Collection; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import io.atomix.protocols.raft.cluster.MemberId; +import io.atomix.protocols.raft.protocol.CloseSessionRequest; +import io.atomix.protocols.raft.protocol.CloseSessionResponse; +import io.atomix.protocols.raft.protocol.CommandRequest; +import io.atomix.protocols.raft.protocol.CommandResponse; +import io.atomix.protocols.raft.protocol.KeepAliveRequest; +import io.atomix.protocols.raft.protocol.KeepAliveResponse; +import io.atomix.protocols.raft.protocol.MetadataRequest; +import io.atomix.protocols.raft.protocol.MetadataResponse; +import io.atomix.protocols.raft.protocol.OpenSessionRequest; +import io.atomix.protocols.raft.protocol.OpenSessionResponse; +import io.atomix.protocols.raft.protocol.PublishRequest; +import io.atomix.protocols.raft.protocol.QueryRequest; +import io.atomix.protocols.raft.protocol.QueryResponse; +import io.atomix.protocols.raft.protocol.RaftClientProtocol; +import io.atomix.protocols.raft.protocol.ResetRequest; +import io.atomix.protocols.raft.session.SessionId; +import org.onosproject.cluster.NodeId; +import org.onosproject.cluster.PartitionId; +import org.onosproject.store.cluster.messaging.ClusterCommunicationService; +import org.onosproject.store.service.Serializer; + +/** + * Raft client protocol that uses a cluster communicator. + */ +public class RaftClientCommunicator extends RaftCommunicator implements RaftClientProtocol { + + public RaftClientCommunicator( + PartitionId partitionId, + Serializer serializer, + ClusterCommunicationService clusterCommunicator) { + super(new RaftMessageContext(String.format("partition-%d", partitionId.id())), serializer, clusterCommunicator); + } + + @Override + public CompletableFuture openSession(MemberId memberId, OpenSessionRequest request) { + return sendAndReceive(context.openSessionSubject, request, memberId); + } + + @Override + public CompletableFuture closeSession(MemberId memberId, CloseSessionRequest request) { + return sendAndReceive(context.closeSessionSubject, request, memberId); + } + + @Override + public CompletableFuture keepAlive(MemberId memberId, KeepAliveRequest request) { + return sendAndReceive(context.keepAliveSubject, request, memberId); + } + + @Override + public CompletableFuture query(MemberId memberId, QueryRequest request) { + return sendAndReceive(context.querySubject, request, memberId); + } + + @Override + public CompletableFuture command(MemberId memberId, CommandRequest request) { + return sendAndReceive(context.commandSubject, request, memberId); + } + + @Override + public CompletableFuture metadata(MemberId memberId, MetadataRequest request) { + return sendAndReceive(context.metadataSubject, request, memberId); + } + + @Override + public void reset(Collection members, ResetRequest request) { + Set nodes = members.stream().map(m -> NodeId.nodeId(m.id())).collect(Collectors.toSet()); + clusterCommunicator.multicast( + request, + context.resetSubject(request.session()), + serializer::encode, + nodes); + } + + @Override + public void registerPublishListener(SessionId sessionId, Consumer listener, Executor executor) { + clusterCommunicator.addSubscriber( + context.publishSubject(sessionId.id()), + serializer::decode, + listener, + executor); + } + + @Override + public void unregisterPublishListener(SessionId sessionId) { + clusterCommunicator.removeSubscriber(context.publishSubject(sessionId.id())); + } +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftCommunicator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftCommunicator.java new file mode 100644 index 0000000000..b708a343de --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftCommunicator.java @@ -0,0 +1,71 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.impl; + +import java.net.ConnectException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import io.atomix.protocols.raft.RaftException; +import io.atomix.protocols.raft.cluster.MemberId; +import org.onosproject.cluster.NodeId; +import org.onosproject.store.cluster.messaging.ClusterCommunicationService; +import org.onosproject.store.cluster.messaging.MessageSubject; +import org.onosproject.store.cluster.messaging.MessagingException; +import org.onosproject.store.service.Serializer; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Abstract base class for Raft protocol client/server. + */ +public abstract class RaftCommunicator { + protected final RaftMessageContext context; + protected final Serializer serializer; + protected final ClusterCommunicationService clusterCommunicator; + + public RaftCommunicator( + RaftMessageContext context, + Serializer serializer, + ClusterCommunicationService clusterCommunicator) { + this.context = checkNotNull(context, "context cannot be null"); + this.serializer = checkNotNull(serializer, "serializer cannot be null"); + this.clusterCommunicator = checkNotNull(clusterCommunicator, "clusterCommunicator cannot be null"); + } + + protected CompletableFuture sendAndReceive(MessageSubject subject, T request, MemberId memberId) { + CompletableFuture future = new CompletableFuture<>(); + clusterCommunicator.sendAndReceive( + request, subject, serializer::encode, serializer::decode, NodeId.nodeId(memberId.id())) + .whenComplete((result, error) -> { + if (error == null) { + future.complete(result); + } else { + if (error instanceof CompletionException) { + error = error.getCause(); + } + if (error instanceof MessagingException.NoRemoteHandler) { + error = new ConnectException(error.getMessage()); + } else if (error instanceof MessagingException.RemoteHandlerFailure + || error instanceof MessagingException.ProtocolException) { + error = new RaftException.ProtocolException(error.getMessage()); + } + future.completeExceptionally(error); + } + }); + return future; + } +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftMessageContext.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftMessageContext.java new file mode 100644 index 0000000000..2deb86a51c --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftMessageContext.java @@ -0,0 +1,93 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.impl; + +import org.onosproject.store.cluster.messaging.MessageSubject; + +/** + * Protocol message context. + */ +final class RaftMessageContext { + private final String prefix; + final MessageSubject openSessionSubject; + final MessageSubject closeSessionSubject; + final MessageSubject keepAliveSubject; + final MessageSubject querySubject; + final MessageSubject commandSubject; + final MessageSubject metadataSubject; + final MessageSubject joinSubject; + final MessageSubject leaveSubject; + final MessageSubject configureSubject; + final MessageSubject reconfigureSubject; + final MessageSubject installSubject; + final MessageSubject pollSubject; + final MessageSubject voteSubject; + final MessageSubject appendSubject; + + RaftMessageContext(String prefix) { + this.prefix = prefix; + this.openSessionSubject = getSubject(prefix, "open"); + this.closeSessionSubject = getSubject(prefix, "close"); + this.keepAliveSubject = getSubject(prefix, "keep-alive"); + this.querySubject = getSubject(prefix, "query"); + this.commandSubject = getSubject(prefix, "command"); + this.metadataSubject = getSubject(prefix, "metadata"); + this.joinSubject = getSubject(prefix, "join"); + this.leaveSubject = getSubject(prefix, "leave"); + this.configureSubject = getSubject(prefix, "configure"); + this.reconfigureSubject = getSubject(prefix, "reconfigure"); + this.installSubject = getSubject(prefix, "install"); + this.pollSubject = getSubject(prefix, "poll"); + this.voteSubject = getSubject(prefix, "vote"); + this.appendSubject = getSubject(prefix, "append"); + } + + private static MessageSubject getSubject(String prefix, String type) { + if (prefix == null) { + return new MessageSubject(type); + } else { + return new MessageSubject(String.format("%s-%s", prefix, type)); + } + } + + /** + * Returns the publish subject for the given session. + * + * @param sessionId the session for which to return the publish subject + * @return the publish subject for the given session + */ + MessageSubject publishSubject(long sessionId) { + if (prefix == null) { + return new MessageSubject(String.format("publish-%d", sessionId)); + } else { + return new MessageSubject(String.format("%s-publish-%d", prefix, sessionId)); + } + } + + /** + * Returns the reset subject for the given session. + * + * @param sessionId the session for which to return the reset subject + * @return the reset subject for the given session + */ + MessageSubject resetSubject(long sessionId) { + if (prefix == null) { + return new MessageSubject(String.format("reset-%d", sessionId)); + } else { + return new MessageSubject(String.format("%s-reset-%d", prefix, sessionId)); + } + } +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftServerCommunicator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftServerCommunicator.java new file mode 100644 index 0000000000..b31810f57d --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/RaftServerCommunicator.java @@ -0,0 +1,301 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.impl; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Consumer; +import java.util.function.Function; + +import io.atomix.protocols.raft.cluster.MemberId; +import io.atomix.protocols.raft.protocol.AppendRequest; +import io.atomix.protocols.raft.protocol.AppendResponse; +import io.atomix.protocols.raft.protocol.CloseSessionRequest; +import io.atomix.protocols.raft.protocol.CloseSessionResponse; +import io.atomix.protocols.raft.protocol.CommandRequest; +import io.atomix.protocols.raft.protocol.CommandResponse; +import io.atomix.protocols.raft.protocol.ConfigureRequest; +import io.atomix.protocols.raft.protocol.ConfigureResponse; +import io.atomix.protocols.raft.protocol.InstallRequest; +import io.atomix.protocols.raft.protocol.InstallResponse; +import io.atomix.protocols.raft.protocol.JoinRequest; +import io.atomix.protocols.raft.protocol.JoinResponse; +import io.atomix.protocols.raft.protocol.KeepAliveRequest; +import io.atomix.protocols.raft.protocol.KeepAliveResponse; +import io.atomix.protocols.raft.protocol.LeaveRequest; +import io.atomix.protocols.raft.protocol.LeaveResponse; +import io.atomix.protocols.raft.protocol.MetadataRequest; +import io.atomix.protocols.raft.protocol.MetadataResponse; +import io.atomix.protocols.raft.protocol.OpenSessionRequest; +import io.atomix.protocols.raft.protocol.OpenSessionResponse; +import io.atomix.protocols.raft.protocol.PollRequest; +import io.atomix.protocols.raft.protocol.PollResponse; +import io.atomix.protocols.raft.protocol.PublishRequest; +import io.atomix.protocols.raft.protocol.QueryRequest; +import io.atomix.protocols.raft.protocol.QueryResponse; +import io.atomix.protocols.raft.protocol.RaftServerProtocol; +import io.atomix.protocols.raft.protocol.ReconfigureRequest; +import io.atomix.protocols.raft.protocol.ReconfigureResponse; +import io.atomix.protocols.raft.protocol.ResetRequest; +import io.atomix.protocols.raft.protocol.VoteRequest; +import io.atomix.protocols.raft.protocol.VoteResponse; +import io.atomix.protocols.raft.session.SessionId; +import org.onosproject.cluster.NodeId; +import org.onosproject.cluster.PartitionId; +import org.onosproject.store.cluster.messaging.ClusterCommunicationService; +import org.onosproject.store.service.Serializer; + +/** + * Raft server protocol that uses a {@link ClusterCommunicationService}. + */ +public class RaftServerCommunicator extends RaftCommunicator implements RaftServerProtocol { + + public RaftServerCommunicator( + PartitionId partitionId, + Serializer serializer, + ClusterCommunicationService clusterCommunicator) { + super(new RaftMessageContext(String.format("partition-%d", partitionId.id())), serializer, clusterCommunicator); + } + + @Override + public CompletableFuture openSession(MemberId memberId, OpenSessionRequest request) { + return sendAndReceive(context.openSessionSubject, request, memberId); + } + + @Override + public CompletableFuture closeSession(MemberId memberId, CloseSessionRequest request) { + return sendAndReceive(context.closeSessionSubject, request, memberId); + } + + @Override + public CompletableFuture keepAlive(MemberId memberId, KeepAliveRequest request) { + return sendAndReceive(context.keepAliveSubject, request, memberId); + } + + @Override + public CompletableFuture query(MemberId memberId, QueryRequest request) { + return sendAndReceive(context.querySubject, request, memberId); + } + + @Override + public CompletableFuture command(MemberId memberId, CommandRequest request) { + return sendAndReceive(context.commandSubject, request, memberId); + } + + @Override + public CompletableFuture metadata(MemberId memberId, MetadataRequest request) { + return sendAndReceive(context.metadataSubject, request, memberId); + } + + @Override + public CompletableFuture join(MemberId memberId, JoinRequest request) { + return sendAndReceive(context.joinSubject, request, memberId); + } + + @Override + public CompletableFuture leave(MemberId memberId, LeaveRequest request) { + return sendAndReceive(context.leaveSubject, request, memberId); + } + + @Override + public CompletableFuture configure(MemberId memberId, ConfigureRequest request) { + return sendAndReceive(context.configureSubject, request, memberId); + } + + @Override + public CompletableFuture reconfigure(MemberId memberId, ReconfigureRequest request) { + return sendAndReceive(context.reconfigureSubject, request, memberId); + } + + @Override + public CompletableFuture install(MemberId memberId, InstallRequest request) { + return sendAndReceive(context.installSubject, request, memberId); + } + + @Override + public CompletableFuture poll(MemberId memberId, PollRequest request) { + return sendAndReceive(context.pollSubject, request, memberId); + } + + @Override + public CompletableFuture vote(MemberId memberId, VoteRequest request) { + return sendAndReceive(context.voteSubject, request, memberId); + } + + @Override + public CompletableFuture append(MemberId memberId, AppendRequest request) { + return sendAndReceive(context.appendSubject, request, memberId); + } + + @Override + public void publish(MemberId memberId, PublishRequest request) { + clusterCommunicator.unicast(request, + context.publishSubject(request.session()), serializer::encode, NodeId.nodeId(memberId.id())); + } + + @Override + public void registerOpenSessionHandler( + Function> handler) { + clusterCommunicator.addSubscriber(context.openSessionSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterOpenSessionHandler() { + clusterCommunicator.removeSubscriber(context.openSessionSubject); + } + + @Override + public void registerCloseSessionHandler( + Function> handler) { + clusterCommunicator.addSubscriber(context.closeSessionSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterCloseSessionHandler() { + clusterCommunicator.removeSubscriber(context.closeSessionSubject); + } + + @Override + public void registerKeepAliveHandler(Function> handler) { + clusterCommunicator.addSubscriber(context.keepAliveSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterKeepAliveHandler() { + clusterCommunicator.removeSubscriber(context.keepAliveSubject); + } + + @Override + public void registerQueryHandler(Function> handler) { + clusterCommunicator.addSubscriber(context.querySubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterQueryHandler() { + clusterCommunicator.removeSubscriber(context.querySubject); + } + + @Override + public void registerCommandHandler(Function> handler) { + clusterCommunicator.addSubscriber(context.commandSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterCommandHandler() { + clusterCommunicator.removeSubscriber(context.commandSubject); + } + + @Override + public void registerMetadataHandler(Function> handler) { + clusterCommunicator.addSubscriber(context.metadataSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterMetadataHandler() { + clusterCommunicator.removeSubscriber(context.metadataSubject); + } + + @Override + public void registerJoinHandler(Function> handler) { + clusterCommunicator.addSubscriber(context.joinSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterJoinHandler() { + clusterCommunicator.removeSubscriber(context.joinSubject); + } + + @Override + public void registerLeaveHandler(Function> handler) { + clusterCommunicator.addSubscriber(context.leaveSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterLeaveHandler() { + clusterCommunicator.removeSubscriber(context.leaveSubject); + } + + @Override + public void registerConfigureHandler(Function> handler) { + clusterCommunicator.addSubscriber(context.configureSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterConfigureHandler() { + clusterCommunicator.removeSubscriber(context.configureSubject); + } + + @Override + public void registerReconfigureHandler( + Function> handler) { + clusterCommunicator.addSubscriber(context.reconfigureSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterReconfigureHandler() { + clusterCommunicator.removeSubscriber(context.reconfigureSubject); + } + + @Override + public void registerInstallHandler(Function> handler) { + clusterCommunicator.addSubscriber(context.installSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterInstallHandler() { + clusterCommunicator.removeSubscriber(context.installSubject); + } + + @Override + public void registerPollHandler(Function> handler) { + clusterCommunicator.addSubscriber(context.pollSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterPollHandler() { + clusterCommunicator.removeSubscriber(context.pollSubject); + } + + @Override + public void registerVoteHandler(Function> handler) { + clusterCommunicator.addSubscriber(context.voteSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterVoteHandler() { + clusterCommunicator.removeSubscriber(context.voteSubject); + } + + @Override + public void registerAppendHandler(Function> handler) { + clusterCommunicator.addSubscriber(context.appendSubject, serializer::decode, handler, serializer::encode); + } + + @Override + public void unregisterAppendHandler() { + clusterCommunicator.removeSubscriber(context.appendSubject); + } + + @Override + public void registerResetListener(SessionId sessionId, Consumer listener, Executor executor) { + clusterCommunicator.addSubscriber(context.resetSubject(sessionId.id()), serializer::decode, listener, executor); + } + + @Override + public void unregisterResetListener(SessionId sessionId) { + clusterCommunicator.removeSubscriber(context.resetSubject(sessionId.id())); + } +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StorageNamespaces.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StorageNamespaces.java new file mode 100644 index 0000000000..2db6affc5e --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StorageNamespaces.java @@ -0,0 +1,216 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.impl; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; + +import io.atomix.protocols.raft.RaftError; +import io.atomix.protocols.raft.ReadConsistency; +import io.atomix.protocols.raft.cluster.MemberId; +import io.atomix.protocols.raft.cluster.RaftMember; +import io.atomix.protocols.raft.cluster.impl.DefaultRaftMember; +import io.atomix.protocols.raft.event.RaftEvent; +import io.atomix.protocols.raft.event.impl.DefaultEventType; +import io.atomix.protocols.raft.operation.OperationType; +import io.atomix.protocols.raft.operation.RaftOperation; +import io.atomix.protocols.raft.operation.impl.DefaultOperationId; +import io.atomix.protocols.raft.protocol.AppendRequest; +import io.atomix.protocols.raft.protocol.AppendResponse; +import io.atomix.protocols.raft.protocol.CloseSessionRequest; +import io.atomix.protocols.raft.protocol.CloseSessionResponse; +import io.atomix.protocols.raft.protocol.CommandRequest; +import io.atomix.protocols.raft.protocol.CommandResponse; +import io.atomix.protocols.raft.protocol.ConfigureRequest; +import io.atomix.protocols.raft.protocol.ConfigureResponse; +import io.atomix.protocols.raft.protocol.InstallRequest; +import io.atomix.protocols.raft.protocol.InstallResponse; +import io.atomix.protocols.raft.protocol.JoinRequest; +import io.atomix.protocols.raft.protocol.JoinResponse; +import io.atomix.protocols.raft.protocol.KeepAliveRequest; +import io.atomix.protocols.raft.protocol.KeepAliveResponse; +import io.atomix.protocols.raft.protocol.LeaveRequest; +import io.atomix.protocols.raft.protocol.LeaveResponse; +import io.atomix.protocols.raft.protocol.MetadataRequest; +import io.atomix.protocols.raft.protocol.MetadataResponse; +import io.atomix.protocols.raft.protocol.OpenSessionRequest; +import io.atomix.protocols.raft.protocol.OpenSessionResponse; +import io.atomix.protocols.raft.protocol.PollRequest; +import io.atomix.protocols.raft.protocol.PollResponse; +import io.atomix.protocols.raft.protocol.PublishRequest; +import io.atomix.protocols.raft.protocol.QueryRequest; +import io.atomix.protocols.raft.protocol.QueryResponse; +import io.atomix.protocols.raft.protocol.RaftResponse; +import io.atomix.protocols.raft.protocol.ReconfigureRequest; +import io.atomix.protocols.raft.protocol.ReconfigureResponse; +import io.atomix.protocols.raft.protocol.ResetRequest; +import io.atomix.protocols.raft.protocol.VoteRequest; +import io.atomix.protocols.raft.protocol.VoteResponse; +import io.atomix.protocols.raft.session.RaftSessionMetadata; +import io.atomix.protocols.raft.session.SessionId; +import io.atomix.protocols.raft.storage.log.entry.CloseSessionEntry; +import io.atomix.protocols.raft.storage.log.entry.CommandEntry; +import io.atomix.protocols.raft.storage.log.entry.ConfigurationEntry; +import io.atomix.protocols.raft.storage.log.entry.InitializeEntry; +import io.atomix.protocols.raft.storage.log.entry.KeepAliveEntry; +import io.atomix.protocols.raft.storage.log.entry.MetadataEntry; +import io.atomix.protocols.raft.storage.log.entry.OpenSessionEntry; +import io.atomix.protocols.raft.storage.log.entry.QueryEntry; +import io.atomix.protocols.raft.storage.system.Configuration; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapEvents; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapEvents; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapEvents; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations; +import org.onosproject.store.primitives.resources.impl.AtomixCounterOperations; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeEvents; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorEvents; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations; +import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueEvents; +import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations; +import org.onosproject.store.serializers.KryoNamespaces; + +/** + * Storage serializer namespaces. + */ +public final class StorageNamespaces { + + /** + * Raft protocol namespace. + */ + public static final KryoNamespace RAFT_PROTOCOL = KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID) + .register(OpenSessionRequest.class) + .register(OpenSessionResponse.class) + .register(CloseSessionRequest.class) + .register(CloseSessionResponse.class) + .register(KeepAliveRequest.class) + .register(KeepAliveResponse.class) + .register(QueryRequest.class) + .register(QueryResponse.class) + .register(CommandRequest.class) + .register(CommandResponse.class) + .register(MetadataRequest.class) + .register(MetadataResponse.class) + .register(JoinRequest.class) + .register(JoinResponse.class) + .register(LeaveRequest.class) + .register(LeaveResponse.class) + .register(ConfigureRequest.class) + .register(ConfigureResponse.class) + .register(ReconfigureRequest.class) + .register(ReconfigureResponse.class) + .register(InstallRequest.class) + .register(InstallResponse.class) + .register(PollRequest.class) + .register(PollResponse.class) + .register(VoteRequest.class) + .register(VoteResponse.class) + .register(AppendRequest.class) + .register(AppendResponse.class) + .register(PublishRequest.class) + .register(ResetRequest.class) + .register(RaftResponse.Status.class) + .register(RaftError.class) + .register(RaftError.Type.class) + .register(ReadConsistency.class) + .register(RaftSessionMetadata.class) + .register(CloseSessionEntry.class) + .register(CommandEntry.class) + .register(ConfigurationEntry.class) + .register(InitializeEntry.class) + .register(KeepAliveEntry.class) + .register(MetadataEntry.class) + .register(OpenSessionEntry.class) + .register(QueryEntry.class) + .register(RaftOperation.class) + .register(RaftEvent.class) + .register(DefaultEventType.class) + .register(DefaultOperationId.class) + .register(OperationType.class) + .register(ReadConsistency.class) + .register(ArrayList.class) + .register(LinkedList.class) + .register(Collections.emptyList().getClass()) + .register(HashSet.class) + .register(DefaultRaftMember.class) + .register(MemberId.class) + .register(SessionId.class) + .register(RaftMember.Type.class) + .register(RaftMember.Status.class) + .register(Instant.class) + .register(Configuration.class) + .register(AtomixAtomicCounterMapOperations.class) + .register(AtomixConsistentMapEvents.class) + .register(AtomixConsistentMapOperations.class) + .register(AtomixConsistentSetMultimapOperations.class) + .register(AtomixConsistentSetMultimapEvents.class) + .register(AtomixConsistentTreeMapEvents.class) + .register(AtomixConsistentTreeMapOperations.class) + .register(AtomixCounterOperations.class) + .register(AtomixDocumentTreeEvents.class) + .register(AtomixDocumentTreeOperations.class) + .register(AtomixLeaderElectorEvents.class) + .register(AtomixLeaderElectorOperations.class) + .register(AtomixWorkQueueEvents.class) + .register(AtomixWorkQueueOperations.class) + .build("RaftProtocol"); + + /** + * Raft storage namespace. + */ + public static final KryoNamespace RAFT_STORAGE = KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 100) + .register(CloseSessionEntry.class) + .register(CommandEntry.class) + .register(ConfigurationEntry.class) + .register(InitializeEntry.class) + .register(KeepAliveEntry.class) + .register(MetadataEntry.class) + .register(OpenSessionEntry.class) + .register(QueryEntry.class) + .register(RaftOperation.class) + .register(ReadConsistency.class) + .register(ArrayList.class) + .register(HashSet.class) + .register(DefaultRaftMember.class) + .register(MemberId.class) + .register(RaftMember.Type.class) + .register(RaftMember.Status.class) + .register(Instant.class) + .register(Configuration.class) + .register(AtomixAtomicCounterMapOperations.class) + .register(AtomixConsistentMapOperations.class) + .register(AtomixConsistentSetMultimapOperations.class) + .register(AtomixConsistentTreeMapOperations.class) + .register(AtomixCounterOperations.class) + .register(AtomixDocumentTreeOperations.class) + .register(AtomixLeaderElectorOperations.class) + .register(AtomixWorkQueueOperations.class) + .build("RaftStorage"); + + private StorageNamespaces() { + } +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartition.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartition.java index e9261c8183..051c95a3c3 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartition.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartition.java @@ -15,31 +15,36 @@ */ package org.onosproject.store.primitives.impl; -import io.atomix.catalyst.serializer.Serializer; -import io.atomix.catalyst.transport.Address; -import io.atomix.resource.ResourceType; - import java.io.File; import java.util.Collection; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; import java.util.stream.Collectors; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableMap; +import io.atomix.protocols.raft.cluster.MemberId; +import io.atomix.protocols.raft.service.RaftService; import org.onosproject.cluster.ClusterService; -import org.onosproject.cluster.ControllerNode; import org.onosproject.cluster.NodeId; import org.onosproject.cluster.Partition; import org.onosproject.cluster.PartitionId; -import org.onosproject.store.cluster.messaging.MessagingService; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMap; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElector; +import org.onosproject.store.cluster.messaging.ClusterCommunicationService; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapService; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapService; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapService; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapService; +import org.onosproject.store.primitives.resources.impl.AtomixCounterService; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeService; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorService; +import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueService; +import org.onosproject.store.service.DistributedPrimitive; import org.onosproject.store.service.PartitionInfo; - -import com.google.common.collect.Collections2; -import com.google.common.collect.ImmutableSet; +import org.onosproject.store.service.Serializer; /** * Storage partition. @@ -47,32 +52,32 @@ import com.google.common.collect.ImmutableSet; public class StoragePartition implements Managed { private final AtomicBoolean isOpened = new AtomicBoolean(false); - private final Serializer serializer; - private final Executor sharedExecutor; - private final MessagingService messagingService; - private final ClusterService clusterService; + private final ClusterCommunicationService clusterCommunicator; private final File logFolder; private Partition partition; private NodeId localNodeId; private StoragePartitionServer server; private StoragePartitionClient client; - public static final Collection RESOURCE_TYPES = ImmutableSet.of( - new ResourceType(AtomixLeaderElector.class), - new ResourceType(AtomixConsistentMap.class)); + public static final Map> RAFT_SERVICES = + ImmutableMap.>builder() + .put(DistributedPrimitive.Type.CONSISTENT_MAP.name(), AtomixConsistentMapService::new) + .put(DistributedPrimitive.Type.CONSISTENT_TREEMAP.name(), AtomixConsistentTreeMapService::new) + .put(DistributedPrimitive.Type.CONSISTENT_MULTIMAP.name(), AtomixConsistentSetMultimapService::new) + .put(DistributedPrimitive.Type.COUNTER_MAP.name(), AtomixAtomicCounterMapService::new) + .put(DistributedPrimitive.Type.COUNTER.name(), AtomixCounterService::new) + .put(DistributedPrimitive.Type.LEADER_ELECTOR.name(), AtomixLeaderElectorService::new) + .put(DistributedPrimitive.Type.WORK_QUEUE.name(), AtomixWorkQueueService::new) + .put(DistributedPrimitive.Type.DOCUMENT_TREE.name(), AtomixDocumentTreeService::new) + .build(); public StoragePartition(Partition partition, - MessagingService messagingService, + ClusterCommunicationService clusterCommunicator, ClusterService clusterService, - Serializer serializer, - Executor sharedExecutor, File logFolder) { this.partition = partition; - this.messagingService = messagingService; - this.clusterService = clusterService; + this.clusterCommunicator = clusterCommunicator; this.localNodeId = clusterService.getLocalNode().id(); - this.serializer = serializer; - this.sharedExecutor = sharedExecutor; this.logFolder = logFolder; } @@ -87,10 +92,14 @@ public class StoragePartition implements Managed { @Override public CompletableFuture open() { if (partition.getMembers().contains(localNodeId)) { - openServer(); + return openServer() + .thenCompose(v -> openClient()) + .thenAccept(v -> isOpened.set(true)) + .thenApply(v -> null); } - return openClient().thenAccept(v -> isOpened.set(true)) - .thenApply(v -> null); + return openClient() + .thenAccept(v -> isOpened.set(true)) + .thenApply(v -> null); } @Override @@ -117,11 +126,11 @@ public class StoragePartition implements Managed { } /** - * Returns the {@link Address addresses} of partition members. - * @return partition member addresses + * Returns the {@link MemberId identifiers} of partition members. + * @return partition member identifiers */ - public Collection
getMemberAddresses() { - return Collections2.transform(partition.getMembers(), this::toAddress); + public Collection getMemberIds() { + return Collections2.transform(partition.getMembers(), n -> MemberId.from(n.id())); } /** @@ -132,10 +141,12 @@ public class StoragePartition implements Managed { if (!partition.getMembers().contains(localNodeId) || server != null) { return CompletableFuture.completedFuture(null); } - StoragePartitionServer server = new StoragePartitionServer(toAddress(localNodeId), - this, - serializer, - () -> new CopycatTransport(partition.getId(), messagingService), + StoragePartitionServer server = new StoragePartitionServer(this, + MemberId.from(localNodeId.id()), + () -> new RaftServerCommunicator( + partition.getId(), + Serializer.using(StorageNamespaces.RAFT_PROTOCOL), + clusterCommunicator), logFolder); return server.open().thenRun(() -> this.server = server); } @@ -149,19 +160,24 @@ public class StoragePartition implements Managed { .stream() .filter(nodeId -> !nodeId.equals(localNodeId)) .collect(Collectors.toSet()); - StoragePartitionServer server = new StoragePartitionServer(toAddress(localNodeId), - this, - serializer, - () -> new CopycatTransport(partition.getId(), messagingService), + StoragePartitionServer server = new StoragePartitionServer(this, + MemberId.from(localNodeId.id()), + () -> new RaftServerCommunicator( + partition.getId(), + Serializer.using(StorageNamespaces.RAFT_PROTOCOL), + clusterCommunicator), logFolder); - return server.join(Collections2.transform(otherMembers, this::toAddress)).thenRun(() -> this.server = server); + return server.join(Collections2.transform(otherMembers, n -> MemberId.from(n.id()))) + .thenRun(() -> this.server = server); } private CompletableFuture openClient() { client = new StoragePartitionClient(this, - serializer, - new CopycatTransport(partition.getId(), messagingService), - sharedExecutor); + MemberId.from(localNodeId.id()), + new RaftClientCommunicator( + partition.getId(), + Serializer.using(StorageNamespaces.RAFT_PROTOCOL), + clusterCommunicator)); return client.open().thenApply(v -> client); } @@ -185,11 +201,6 @@ public class StoragePartition implements Managed { return CompletableFuture.completedFuture(null); } - private Address toAddress(NodeId nodeId) { - ControllerNode node = clusterService.getNode(nodeId); - return new Address(node.ip().toString(), node.tcpPort()); - } - /** * Returns the partition information if this partition is locally managed i.e. * this node is a active member of the partition. diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java index d97868cb1a..b55efc4e22 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionClient.java @@ -15,30 +15,19 @@ */ package org.onosproject.store.primitives.impl; -import java.util.Collection; +import java.time.Duration; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; +import java.util.stream.Collectors; import com.google.common.base.Suppliers; -import io.atomix.AtomixClient; -import io.atomix.catalyst.transport.Transport; -import io.atomix.copycat.client.ConnectionStrategies; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.copycat.client.CopycatClient.State; -import io.atomix.copycat.client.RecoveryStrategies; -import io.atomix.copycat.client.ServerSelectionStrategies; -import io.atomix.manager.ResourceClient; -import io.atomix.manager.ResourceManagerException; -import io.atomix.manager.util.ResourceManagerTypeResolver; -import io.atomix.resource.ResourceRegistry; -import io.atomix.resource.ResourceType; -import io.atomix.variables.DistributedLong; +import io.atomix.protocols.raft.RaftClient; +import io.atomix.protocols.raft.ReadConsistency; +import io.atomix.protocols.raft.cluster.MemberId; +import io.atomix.protocols.raft.protocol.RaftClientProtocol; +import io.atomix.protocols.raft.proxy.CommunicationStrategy; +import io.atomix.protocols.raft.session.RaftSessionMetadata; import org.onlab.util.HexString; -import org.onlab.util.OrderedExecutor; import org.onosproject.store.primitives.DistributedPrimitiveCreator; import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMap; import org.onosproject.store.primitives.resources.impl.AtomixConsistentMap; @@ -60,7 +49,7 @@ import org.onosproject.store.service.AsyncConsistentTreeMap; import org.onosproject.store.service.AsyncDistributedSet; import org.onosproject.store.service.AsyncDocumentTree; import org.onosproject.store.service.AsyncLeaderElector; -import org.onosproject.store.service.DistributedPrimitive.Status; +import org.onosproject.store.service.DistributedPrimitive; import org.onosproject.store.service.PartitionClientInfo; import org.onosproject.store.service.Serializer; import org.onosproject.store.service.WorkQueue; @@ -76,55 +65,33 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana private final Logger log = getLogger(getClass()); private final StoragePartition partition; - private final Transport transport; - private final io.atomix.catalyst.serializer.Serializer serializer; - private final Executor sharedExecutor; - private AtomixClient client; - private ResourceClient resourceClient; + private final MemberId localMemberId; + private final RaftClientProtocol protocol; + private RaftClient client; private static final String ATOMIC_VALUES_CONSISTENT_MAP_NAME = "onos-atomic-values"; private final com.google.common.base.Supplier> onosAtomicValuesMap = Suppliers.memoize(() -> newAsyncConsistentMap(ATOMIC_VALUES_CONSISTENT_MAP_NAME, Serializer.using(KryoNamespaces.BASIC))); - Function mapper = state -> { - switch (state) { - case CONNECTED: - return Status.ACTIVE; - case SUSPENDED: - return Status.SUSPENDED; - case CLOSED: - return Status.INACTIVE; - default: - throw new IllegalStateException("Unknown state " + state); - } - }; - public StoragePartitionClient(StoragePartition partition, - io.atomix.catalyst.serializer.Serializer serializer, - Transport transport, - Executor sharedExecutor) { + public StoragePartitionClient(StoragePartition partition, MemberId localMemberId, RaftClientProtocol protocol) { this.partition = partition; - this.serializer = serializer; - this.transport = transport; - this.sharedExecutor = sharedExecutor; + this.localMemberId = localMemberId; + this.protocol = protocol; } @Override public CompletableFuture open() { synchronized (StoragePartitionClient.this) { - resourceClient = newResourceClient(transport, - serializer.clone(), - StoragePartition.RESOURCE_TYPES); - resourceClient.client().onStateChange(state -> log.debug("Partition {} client state" - + " changed to {}", partition.getId(), state)); - client = new AtomixClient(resourceClient); + client = newRaftClient(protocol); } - return client.connect(partition.getMemberAddresses()).whenComplete((r, e) -> { + return client.connect(partition.getMemberIds()).whenComplete((r, e) -> { if (e == null) { log.info("Successfully started client for partition {}", partition.getId()); } else { log.info("Failed to start client for partition {}", partition.getId(), e); } }).thenApply(v -> null); + } @Override @@ -132,25 +99,19 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana return client != null ? client.close() : CompletableFuture.completedFuture(null); } - /** - * Returns the executor provided by the given supplier or a serial executor if the supplier is {@code null}. - * - * @param executorSupplier the user-provided executor supplier - * @return the executor - */ - private Executor defaultExecutor(Supplier executorSupplier) { - return executorSupplier != null ? executorSupplier.get() : new OrderedExecutor(sharedExecutor); - } - @Override - public AsyncConsistentMap newAsyncConsistentMap( - String name, Serializer serializer, Supplier executorSupplier) { - AtomixConsistentMap atomixConsistentMap = client.getResource(name, AtomixConsistentMap.class).join(); - Consumer statusListener = state -> { - atomixConsistentMap.statusChangeListeners() - .forEach(listener -> listener.accept(mapper.apply(state))); - }; - resourceClient.client().onStateChange(statusListener); + public AsyncConsistentMap newAsyncConsistentMap(String name, Serializer serializer) { + AtomixConsistentMap atomixConsistentMap = + new AtomixConsistentMap(client.newProxyBuilder() + .withName(name) + .withServiceType(DistributedPrimitive.Type.CONSISTENT_MAP.name()) + .withReadConsistency(ReadConsistency.SEQUENTIAL) + .withCommunicationStrategy(CommunicationStrategy.ANY) + .withTimeout(Duration.ofSeconds(30)) + .withMaxRetries(5) + .build() + .open() + .join()); AsyncConsistentMap rawMap = new DelegatingAsyncConsistentMap(atomixConsistentMap) { @@ -162,24 +123,27 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana // We have to ensure serialization is done on the Copycat threads since Kryo is not thread safe. AsyncConsistentMap transcodedMap = DistributedPrimitives.newTranscodingMap(rawMap, - key -> HexString.toHexString(serializer.encode(key)), - string -> serializer.decode(HexString.fromHexString(string)), - value -> value == null ? null : serializer.encode(value), - bytes -> serializer.decode(bytes)); + key -> HexString.toHexString(serializer.encode(key)), + string -> serializer.decode(HexString.fromHexString(string)), + value -> value == null ? null : serializer.encode(value), + bytes -> serializer.decode(bytes)); - return new ExecutingAsyncConsistentMap<>(transcodedMap, defaultExecutor(executorSupplier), sharedExecutor); + return transcodedMap; } @Override - public AsyncConsistentTreeMap newAsyncConsistentTreeMap( - String name, Serializer serializer, Supplier executorSupplier) { + public AsyncConsistentTreeMap newAsyncConsistentTreeMap(String name, Serializer serializer) { AtomixConsistentTreeMap atomixConsistentTreeMap = - client.getResource(name, AtomixConsistentTreeMap.class).join(); - Consumer statusListener = state -> { - atomixConsistentTreeMap.statusChangeListeners() - .forEach(listener -> listener.accept(mapper.apply(state))); - }; - resourceClient.client().onStateChange(statusListener); + new AtomixConsistentTreeMap(client.newProxyBuilder() + .withName(name) + .withServiceType(DistributedPrimitive.Type.CONSISTENT_TREEMAP.name()) + .withReadConsistency(ReadConsistency.SEQUENTIAL) + .withCommunicationStrategy(CommunicationStrategy.ANY) + .withTimeout(Duration.ofSeconds(30)) + .withMaxRetries(5) + .build() + .open() + .join()); AsyncConsistentTreeMap rawMap = new DelegatingAsyncConsistentTreeMap(atomixConsistentTreeMap) { @@ -191,24 +155,26 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana AsyncConsistentTreeMap transcodedMap = DistributedPrimitives.newTranscodingTreeMap( - rawMap, - value -> value == null ? null : serializer.encode(value), - bytes -> serializer.decode(bytes)); + rawMap, + value -> value == null ? null : serializer.encode(value), + bytes -> serializer.decode(bytes)); - return new ExecutingAsyncConsistentTreeMap<>(transcodedMap, defaultExecutor(executorSupplier), sharedExecutor); + return transcodedMap; } @Override - public AsyncConsistentMultimap newAsyncConsistentSetMultimap( - String name, Serializer serializer, Supplier executorSupplier) { + public AsyncConsistentMultimap newAsyncConsistentSetMultimap(String name, Serializer serializer) { AtomixConsistentSetMultimap atomixConsistentSetMultimap = - client.getResource(name, AtomixConsistentSetMultimap.class) - .join(); - Consumer statusListener = state -> { - atomixConsistentSetMultimap.statusChangeListeners() - .forEach(listener -> listener.accept(mapper.apply(state))); - }; - resourceClient.client().onStateChange(statusListener); + new AtomixConsistentSetMultimap(client.newProxyBuilder() + .withName(name) + .withServiceType(DistributedPrimitive.Type.CONSISTENT_MULTIMAP.name()) + .withReadConsistency(ReadConsistency.SEQUENTIAL) + .withCommunicationStrategy(CommunicationStrategy.ANY) + .withTimeout(Duration.ofSeconds(30)) + .withMaxRetries(5) + .build() + .open() + .join()); AsyncConsistentMultimap rawMap = new DelegatingAsyncConsistentMultimap( @@ -227,97 +193,136 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana value -> serializer.encode(value), bytes -> serializer.decode(bytes)); - return new ExecutingAsyncConsistentMultimap<>(transcodedMap, defaultExecutor(executorSupplier), sharedExecutor); + return transcodedMap; } @Override - public AsyncDistributedSet newAsyncDistributedSet( - String name, Serializer serializer, Supplier executorSupplier) { - return DistributedPrimitives.newSetFromMap(newAsyncConsistentMap(name, serializer, executorSupplier)); + public AsyncDistributedSet newAsyncDistributedSet(String name, Serializer serializer) { + return DistributedPrimitives.newSetFromMap(newAsyncConsistentMap(name, serializer)); } @Override - public AsyncAtomicCounterMap newAsyncAtomicCounterMap( - String name, Serializer serializer, Supplier executorSupplier) { - AtomixAtomicCounterMap atomixAtomicCounterMap = - client.getResource(name, AtomixAtomicCounterMap.class) - .join(); + public AsyncAtomicCounterMap newAsyncAtomicCounterMap(String name, Serializer serializer) { + AtomixAtomicCounterMap atomixAtomicCounterMap = new AtomixAtomicCounterMap(client.newProxyBuilder() + .withName(name) + .withServiceType(DistributedPrimitive.Type.COUNTER_MAP.name()) + .withReadConsistency(ReadConsistency.LINEARIZABLE_LEASE) + .withCommunicationStrategy(CommunicationStrategy.LEADER) + .withTimeout(Duration.ofSeconds(30)) + .withMaxRetries(5) + .build() + .open() + .join()); AsyncAtomicCounterMap transcodedMap = - DistributedPrimitives.newTranscodingAtomicCounterMap( - atomixAtomicCounterMap, + DistributedPrimitives.newTranscodingAtomicCounterMap( + atomixAtomicCounterMap, key -> HexString.toHexString(serializer.encode(key)), string -> serializer.decode(HexString.fromHexString(string))); - return new ExecutingAsyncAtomicCounterMap<>(transcodedMap, defaultExecutor(executorSupplier), sharedExecutor); + return transcodedMap; } @Override - public AsyncAtomicCounter newAsyncCounter(String name, Supplier executorSupplier) { - DistributedLong distributedLong = client.getLong(name).join(); - AsyncAtomicCounter asyncCounter = new AtomixCounter(name, distributedLong); - return new ExecutingAsyncAtomicCounter(asyncCounter, defaultExecutor(executorSupplier), sharedExecutor); + public AsyncAtomicCounter newAsyncCounter(String name) { + return new AtomixCounter(client.newProxyBuilder() + .withName(name) + .withServiceType(DistributedPrimitive.Type.COUNTER.name()) + .withReadConsistency(ReadConsistency.LINEARIZABLE_LEASE) + .withCommunicationStrategy(CommunicationStrategy.LEADER) + .withTimeout(Duration.ofSeconds(30)) + .withMaxRetries(5) + .build() + .open() + .join()); } @Override - public AsyncAtomicIdGenerator newAsyncIdGenerator(String name, Supplier executorSupplier) { - DistributedLong distributedLong = client.getLong(name).join(); - AsyncAtomicIdGenerator asyncIdGenerator = new AtomixIdGenerator(name, distributedLong); - return new ExecutingAsyncAtomicIdGenerator(asyncIdGenerator, defaultExecutor(executorSupplier), sharedExecutor); + public AsyncAtomicIdGenerator newAsyncIdGenerator(String name) { + return new AtomixIdGenerator(newAsyncCounter(name)); } @Override - public AsyncAtomicValue newAsyncAtomicValue( - String name, Serializer serializer, Supplier executorSupplier) { - AsyncAtomicValue asyncValue = new DefaultAsyncAtomicValue<>(name, serializer, onosAtomicValuesMap.get()); - return new ExecutingAsyncAtomicValue<>(asyncValue, defaultExecutor(executorSupplier), sharedExecutor); + public AsyncAtomicValue newAsyncAtomicValue(String name, Serializer serializer) { + return new DefaultAsyncAtomicValue<>(name, serializer, onosAtomicValuesMap.get()); } @Override - public WorkQueue newWorkQueue(String name, Serializer serializer, Supplier executorSupplier) { - AtomixWorkQueue atomixWorkQueue = client.getResource(name, AtomixWorkQueue.class).join(); - WorkQueue workQueue = new DefaultDistributedWorkQueue<>(atomixWorkQueue, serializer); - return new ExecutingWorkQueue<>(workQueue, defaultExecutor(executorSupplier), sharedExecutor); + public WorkQueue newWorkQueue(String name, Serializer serializer) { + AtomixWorkQueue atomixWorkQueue = new AtomixWorkQueue(client.newProxyBuilder() + .withName(name) + .withServiceType(DistributedPrimitive.Type.WORK_QUEUE.name()) + .withReadConsistency(ReadConsistency.LINEARIZABLE_LEASE) + .withCommunicationStrategy(CommunicationStrategy.LEADER) + .withTimeout(Duration.ofSeconds(5)) + .withMaxRetries(5) + .build() + .open() + .join()); + return new DefaultDistributedWorkQueue<>(atomixWorkQueue, serializer); } @Override - public AsyncDocumentTree newAsyncDocumentTree( - String name, Serializer serializer, Supplier executorSupplier) { - AtomixDocumentTree atomixDocumentTree = client.getResource(name, AtomixDocumentTree.class).join(); - AsyncDocumentTree asyncDocumentTree = new DefaultDistributedDocumentTree<>( - name, atomixDocumentTree, serializer); - return new ExecutingAsyncDocumentTree<>(asyncDocumentTree, defaultExecutor(executorSupplier), sharedExecutor); + public AsyncDocumentTree newAsyncDocumentTree(String name, Serializer serializer) { + AtomixDocumentTree atomixDocumentTree = new AtomixDocumentTree(client.newProxyBuilder() + .withName(name) + .withServiceType(DistributedPrimitive.Type.DOCUMENT_TREE.name()) + .withReadConsistency(ReadConsistency.SEQUENTIAL) + .withCommunicationStrategy(CommunicationStrategy.ANY) + .withTimeout(Duration.ofSeconds(30)) + .withMaxRetries(5) + .build() + .open() + .join()); + return new DefaultDistributedDocumentTree<>(name, atomixDocumentTree, serializer); } @Override - public AsyncLeaderElector newAsyncLeaderElector(String name, Supplier executorSupplier) { - AtomixLeaderElector leaderElector = client.getResource(name, AtomixLeaderElector.class) - .thenCompose(AtomixLeaderElector::setupCache) - .join(); - Consumer statusListener = state -> leaderElector.statusChangeListeners() - .forEach(listener -> listener.accept(mapper.apply(state))); - resourceClient.client().onStateChange(statusListener); - return new ExecutingAsyncLeaderElector(leaderElector, defaultExecutor(executorSupplier), sharedExecutor); + public AsyncLeaderElector newAsyncLeaderElector(String name) { + AtomixLeaderElector leaderElector = new AtomixLeaderElector(client.newProxyBuilder() + .withName(name) + .withServiceType(DistributedPrimitive.Type.LEADER_ELECTOR.name()) + .withReadConsistency(ReadConsistency.LINEARIZABLE) + .withCommunicationStrategy(CommunicationStrategy.LEADER) + .withTimeout(Duration.ofSeconds(5)) + .withMaxRetries(5) + .build() + .open() + .join()); + leaderElector.setupCache().join(); + return leaderElector; } @Override public Set getAsyncConsistentMapNames() { - return client.keys(AtomixConsistentMap.class).join(); + return client.metadata().getSessions(DistributedPrimitive.Type.CONSISTENT_MAP.name()) + .join() + .stream() + .map(RaftSessionMetadata::serviceName) + .collect(Collectors.toSet()); } @Override public Set getAsyncAtomicCounterNames() { - return client.keys(DistributedLong.class).join(); + return client.metadata().getSessions(DistributedPrimitive.Type.COUNTER.name()) + .join() + .stream() + .map(RaftSessionMetadata::serviceName) + .collect(Collectors.toSet()); } @Override public Set getWorkQueueNames() { - return client.keys(AtomixWorkQueue.class).join(); + return client.metadata().getSessions(DistributedPrimitive.Type.WORK_QUEUE.name()) + .join() + .stream() + .map(RaftSessionMetadata::serviceName) + .collect(Collectors.toSet()); } @Override public boolean isOpen() { - return resourceClient.client().state() != State.CLOSED; + return client != null; } /** @@ -325,35 +330,14 @@ public class StoragePartitionClient implements DistributedPrimitiveCreator, Mana * @return partition client information */ public PartitionClientInfo clientInfo() { - return new PartitionClientInfo(partition.getId(), - partition.getMembers(), - resourceClient.client().session().id(), - mapper.apply(resourceClient.client().state())); + return new PartitionClientInfo(partition.getId(), partition.getMembers()); } - private ResourceClient newResourceClient(Transport transport, - io.atomix.catalyst.serializer.Serializer serializer, - Collection resourceTypes) { - ResourceRegistry registry = new ResourceRegistry(); - resourceTypes.forEach(registry::register); - CopycatClient copycatClient = CopycatClient.builder() - .withServerSelectionStrategy(ServerSelectionStrategies.ANY) - .withConnectionStrategy(ConnectionStrategies.FIBONACCI_BACKOFF) - .withRecoveryStrategy(RecoveryStrategies.RECOVER) - .withTransport(transport) - .withSerializer(serializer) + private RaftClient newRaftClient(RaftClientProtocol protocol) { + return RaftClient.newBuilder() + .withClientId("partition-" + partition.getId()) + .withMemberId(MemberId.from(localMemberId.id())) + .withProtocol(protocol) .build(); - copycatClient.serializer().resolve(new ResourceManagerTypeResolver()); - for (ResourceType type : registry.types()) { - try { - type.factory() - .newInstance() - .createSerializableTypeResolver() - .resolve(copycatClient.serializer().registry()); - } catch (InstantiationException | IllegalAccessException e) { - throw new ResourceManagerException(e); - } - } - return new ResourceClient(new OnosCopycatClient(copycatClient, 5, 100)); } } diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionDetails.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionDetails.java index 9305a31575..9ae34d92ea 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionDetails.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionDetails.java @@ -15,13 +15,12 @@ */ package org.onosproject.store.primitives.impl; -import io.atomix.copycat.server.cluster.Member; - import java.util.Collection; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import io.atomix.protocols.raft.cluster.RaftMember; import org.onosproject.cluster.PartitionId; import org.onosproject.store.service.PartitionInfo; @@ -34,15 +33,15 @@ import com.google.common.collect.ImmutableSet; public class StoragePartitionDetails { private final PartitionId partitionId; - private final Set activeMembers; - private final Set configuredMembers; - private final Member leader; + private final Set activeMembers; + private final Set configuredMembers; + private final RaftMember leader; private final long leaderTerm; public StoragePartitionDetails(PartitionId partitionId, - Collection activeMembers, - Collection configuredMembers, - Member leader, + Collection activeMembers, + Collection configuredMembers, + RaftMember leader, long leaderTerm) { this.partitionId = partitionId; this.activeMembers = ImmutableSet.copyOf(activeMembers); @@ -55,7 +54,7 @@ public class StoragePartitionDetails { * Returns the set of active members. * @return active members */ - public Set activeMembers() { + public Set activeMembers() { return activeMembers; } @@ -63,7 +62,7 @@ public class StoragePartitionDetails { * Returns the set of configured members. * @return configured members */ - public Set configuredMembers() { + public Set configuredMembers() { return configuredMembers; } @@ -71,7 +70,7 @@ public class StoragePartitionDetails { * Returns the partition leader. * @return leader */ - public Member leader() { + public RaftMember leader() { return leader; } @@ -98,8 +97,8 @@ public class StoragePartitionDetails { * @return partition info */ public PartitionInfo toPartitionInfo() { - Function memberToString = - m -> m == null ? "none" : String.format("%s:%d", m.address().host(), m.address().port()); + Function memberToString = + m -> m == null ? "none" : m.memberId().toString(); return new PartitionInfo(partitionId.toString(), leaderTerm, activeMembers.stream().map(memberToString).collect(Collectors.toList()), diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionServer.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionServer.java index f0c060934e..c1ea37cb29 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionServer.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/impl/StoragePartitionServer.java @@ -15,24 +15,23 @@ */ package org.onosproject.store.primitives.impl; -import static org.slf4j.LoggerFactory.getLogger; -import io.atomix.catalyst.serializer.Serializer; -import io.atomix.catalyst.transport.Address; -import io.atomix.catalyst.transport.Transport; -import io.atomix.copycat.server.CopycatServer; -import io.atomix.copycat.server.storage.Storage; -import io.atomix.copycat.server.storage.StorageLevel; -import io.atomix.manager.internal.ResourceManagerState; -import io.atomix.manager.util.ResourceManagerTypeResolver; - import java.io.File; import java.util.Collection; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; +import io.atomix.protocols.raft.RaftServer; +import io.atomix.protocols.raft.cluster.MemberId; +import io.atomix.protocols.raft.protocol.RaftServerProtocol; +import io.atomix.protocols.raft.storage.RaftStorage; +import io.atomix.storage.StorageLevel; +import org.onosproject.store.primitives.resources.impl.AtomixSerializerAdapter; import org.onosproject.store.service.PartitionInfo; +import org.onosproject.store.service.Serializer; import org.slf4j.Logger; +import static org.slf4j.LoggerFactory.getLogger; + /** * {@link StoragePartition} server. */ @@ -41,36 +40,34 @@ public class StoragePartitionServer implements Managed { private final Logger log = getLogger(getClass()); private static final int MAX_ENTRIES_PER_LOG_SEGMENT = 32768; + private final MemberId localMemberId; private final StoragePartition partition; - private final Address localAddress; - private final Supplier transport; - private final Serializer serializer; + private final Supplier protocol; private final File dataFolder; - private CopycatServer server; + private RaftServer server; - public StoragePartitionServer(Address localAddress, + public StoragePartitionServer( StoragePartition partition, - Serializer serializer, - Supplier transport, + MemberId localMemberId, + Supplier protocol, File dataFolder) { this.partition = partition; - this.localAddress = localAddress; - this.serializer = serializer; - this.transport = transport; + this.localMemberId = localMemberId; + this.protocol = protocol; this.dataFolder = dataFolder; } @Override public CompletableFuture open() { - CompletableFuture serverOpenFuture; - if (partition.getMemberAddresses().contains(localAddress)) { + CompletableFuture serverOpenFuture; + if (partition.getMemberIds().contains(localMemberId)) { if (server != null && server.isRunning()) { return CompletableFuture.completedFuture(null); } synchronized (this) { server = buildServer(); } - serverOpenFuture = server.bootstrap(partition.getMemberAddresses()); + serverOpenFuture = server.bootstrap(partition.getMemberIds()); } else { serverOpenFuture = CompletableFuture.completedFuture(null); } @@ -96,24 +93,21 @@ public class StoragePartitionServer implements Managed { return server.leave(); } - private CopycatServer buildServer() { - CopycatServer server = CopycatServer.builder(localAddress) + private RaftServer buildServer() { + RaftServer.Builder builder = RaftServer.newBuilder(localMemberId) .withName("partition-" + partition.getId()) - .withSerializer(serializer.clone()) - .withTransport(transport.get()) - .withStateMachine(ResourceManagerState::new) - .withStorage(Storage.builder() + .withProtocol(protocol.get()) + .withStorage(RaftStorage.newBuilder() .withStorageLevel(StorageLevel.DISK) - .withCompactionThreads(1) + .withSerializer(new AtomixSerializerAdapter(Serializer.using(StorageNamespaces.RAFT_STORAGE))) .withDirectory(dataFolder) .withMaxEntriesPerSegment(MAX_ENTRIES_PER_LOG_SEGMENT) - .build()) - .build(); - server.serializer().resolve(new ResourceManagerTypeResolver()); - return server; + .build()); + StoragePartition.RAFT_SERVICES.forEach(builder::addService); + return builder.build(); } - public CompletableFuture join(Collection
otherMembers) { + public CompletableFuture join(Collection otherMembers) { server = buildServer(); return server.join(otherMembers).whenComplete((r, e) -> { if (e == null) { @@ -135,9 +129,9 @@ public class StoragePartitionServer implements Managed { */ public PartitionInfo info() { return new StoragePartitionDetails(partition.getId(), - server.cluster().members(), - server.cluster().members(), - server.cluster().leader(), - server.cluster().term()).toPartitionInfo(); + server.cluster().getMembers(), + server.cluster().getMembers(), + server.cluster().getLeader(), + server.cluster().getTerm()).toPartitionInfo(); } } diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AbstractRaftPrimitive.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AbstractRaftPrimitive.java new file mode 100644 index 0000000000..d0237882e2 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AbstractRaftPrimitive.java @@ -0,0 +1,91 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import java.util.Collection; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import io.atomix.protocols.raft.proxy.RaftProxy; +import org.onosproject.store.service.DistributedPrimitive; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Abstract base class for primitives that interact with Raft replicated state machines via proxy. + */ +public abstract class AbstractRaftPrimitive implements DistributedPrimitive { + private final Function mapper = state -> { + switch (state) { + case CONNECTED: + return Status.ACTIVE; + case SUSPENDED: + return Status.SUSPENDED; + case CLOSED: + return Status.INACTIVE; + default: + throw new IllegalStateException("Unknown state " + state); + } + }; + + protected final RaftProxy proxy; + private final Set> statusChangeListeners = Sets.newCopyOnWriteArraySet(); + + public AbstractRaftPrimitive(RaftProxy proxy) { + this.proxy = checkNotNull(proxy, "proxy cannot be null"); + proxy.addStateChangeListener(this::onStateChange); + } + + @Override + public String name() { + return proxy.name(); + } + + /** + * Handles a Raft session state change. + * + * @param state the updated Raft session state + */ + private void onStateChange(RaftProxy.State state) { + statusChangeListeners.forEach(listener -> listener.accept(mapper.apply(state))); + } + + @Override + public void addStatusChangeListener(Consumer listener) { + statusChangeListeners.add(listener); + } + + @Override + public void removeStatusChangeListener(Consumer listener) { + statusChangeListeners.remove(listener); + } + + @Override + public Collection> statusChangeListeners() { + return ImmutableSet.copyOf(statusChangeListeners); + } + + @Override + public String toString() { + return toStringHelper(this) + .add("proxy", proxy) + .toString(); + } +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMap.java index ad1de2ee53..c84e03a357 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMap.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMap.java @@ -15,117 +15,131 @@ */ package org.onosproject.store.primitives.resources.impl; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.AbstractResource; -import io.atomix.resource.ResourceTypeInfo; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.AddAndGet; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Clear; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.DecrementAndGet; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Get; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.GetAndAdd; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.GetAndDecrement; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.GetAndIncrement; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.IncrementAndGet; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.IsEmpty; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Put; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.PutIfAbsent; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Remove; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.RemoveValue; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Replace; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Size; -import org.onosproject.store.service.AsyncAtomicCounterMap; - -import java.util.Properties; import java.util.concurrent.CompletableFuture; +import io.atomix.protocols.raft.proxy.RaftProxy; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.AddAndGet; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.DecrementAndGet; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Get; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GetAndAdd; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GetAndDecrement; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GetAndIncrement; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.IncrementAndGet; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Put; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PutIfAbsent; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Remove; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.RemoveValue; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Replace; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.AsyncAtomicCounterMap; +import org.onosproject.store.service.Serializer; + +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.ADD_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.CLEAR; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.DECREMENT_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET_AND_ADD; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET_AND_DECREMENT; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET_AND_INCREMENT; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.INCREMENT_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.IS_EMPTY; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PUT; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PUT_IF_ABSENT; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.REMOVE; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.REMOVE_VALUE; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.REPLACE; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.SIZE; + /** * {@code AsyncAtomicCounterMap} implementation backed by Atomix. */ -@ResourceTypeInfo(id = -157, factory = AtomixAtomicCounterMapFactory.class) -public class AtomixAtomicCounterMap extends AbstractResource - implements AsyncAtomicCounterMap { +public class AtomixAtomicCounterMap extends AbstractRaftPrimitive implements AsyncAtomicCounterMap { + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixAtomicCounterMapOperations.NAMESPACE) + .build()); - public AtomixAtomicCounterMap(CopycatClient client, Properties options) { - super(client, options); - } - - @Override - public String name() { - return null; + public AtomixAtomicCounterMap(RaftProxy proxy) { + super(proxy); } @Override public CompletableFuture incrementAndGet(String key) { - return client.submit(new IncrementAndGet(key)); + return proxy.invoke(INCREMENT_AND_GET, SERIALIZER::encode, new IncrementAndGet(key), SERIALIZER::decode); } @Override public CompletableFuture decrementAndGet(String key) { - return client.submit(new DecrementAndGet(key)); + return proxy.invoke(DECREMENT_AND_GET, SERIALIZER::encode, new DecrementAndGet(key), SERIALIZER::decode); } @Override public CompletableFuture getAndIncrement(String key) { - return client.submit(new GetAndIncrement(key)); + return proxy.invoke(GET_AND_INCREMENT, SERIALIZER::encode, new GetAndIncrement(key), SERIALIZER::decode); } @Override public CompletableFuture getAndDecrement(String key) { - return client.submit(new GetAndDecrement(key)); + return proxy.invoke(GET_AND_DECREMENT, SERIALIZER::encode, new GetAndDecrement(key), SERIALIZER::decode); } @Override public CompletableFuture addAndGet(String key, long delta) { - return client.submit(new AddAndGet(key, delta)); + return proxy.invoke(ADD_AND_GET, SERIALIZER::encode, new AddAndGet(key, delta), SERIALIZER::decode); } @Override public CompletableFuture getAndAdd(String key, long delta) { - return client.submit(new GetAndAdd(key, delta)); + return proxy.invoke(GET_AND_ADD, SERIALIZER::encode, new GetAndAdd(key, delta), SERIALIZER::decode); } @Override public CompletableFuture get(String key) { - return client.submit(new Get(key)); + return proxy.invoke(GET, SERIALIZER::encode, new Get(key), SERIALIZER::decode); } @Override public CompletableFuture put(String key, long newValue) { - return client.submit(new Put(key, newValue)); + return proxy.invoke(PUT, SERIALIZER::encode, new Put(key, newValue), SERIALIZER::decode); } @Override public CompletableFuture putIfAbsent(String key, long newValue) { - return client.submit(new PutIfAbsent(key, newValue)); + return proxy.invoke(PUT_IF_ABSENT, SERIALIZER::encode, new PutIfAbsent(key, newValue), SERIALIZER::decode); } @Override public CompletableFuture replace(String key, long expectedOldValue, long newValue) { - return client.submit(new Replace(key, expectedOldValue, newValue)); + return proxy.invoke( + REPLACE, + SERIALIZER::encode, + new Replace(key, expectedOldValue, newValue), + SERIALIZER::decode); } @Override public CompletableFuture remove(String key) { - return client.submit(new Remove(key)); + return proxy.invoke(REMOVE, SERIALIZER::encode, new Remove(key), SERIALIZER::decode); } @Override public CompletableFuture remove(String key, long value) { - return client.submit(new RemoveValue(key, value)); + return proxy.invoke(REMOVE_VALUE, SERIALIZER::encode, new RemoveValue(key, value), SERIALIZER::decode); } @Override public CompletableFuture size() { - return client.submit(new Size()); + return proxy.invoke(SIZE, SERIALIZER::decode); } @Override public CompletableFuture isEmpty() { - return client.submit(new IsEmpty()); + return proxy.invoke(IS_EMPTY, SERIALIZER::decode); } @Override public CompletableFuture clear() { - return client.submit(new Clear()); + return proxy.invoke(CLEAR); } -} +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapCommands.java deleted file mode 100644 index 3a7f696756..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapCommands.java +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.resources.impl; - -import io.atomix.catalyst.buffer.BufferInput; -import io.atomix.catalyst.buffer.BufferOutput; -import io.atomix.catalyst.serializer.CatalystSerializable; -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.catalyst.serializer.Serializer; -import io.atomix.catalyst.serializer.SerializerRegistry; -import io.atomix.copycat.Command; -import io.atomix.copycat.Query; - -/** - * Atomic counter map commands. - */ -public final class AtomixAtomicCounterMapCommands { - private AtomixAtomicCounterMapCommands() { - } - - public abstract static class AtomicCounterMapCommand implements Command, CatalystSerializable { - @Override - public CompactionMode compaction() { - return CompactionMode.SNAPSHOT; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - } - - public abstract static class AtomicCounterMapQuery implements Query, CatalystSerializable { - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - } - - public abstract static class KeyCommand extends AtomicCounterMapCommand { - private String key; - - public KeyCommand() { - } - - public KeyCommand(String key) { - this.key = key; - } - - public String key() { - return key; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - buffer.writeString(key); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - key = buffer.readString(); - } - } - - public abstract static class KeyQuery extends AtomicCounterMapQuery { - private String key; - - public KeyQuery() { - } - - public KeyQuery(String key) { - this.key = key; - } - - public String key() { - return key; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - buffer.writeString(key); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - key = buffer.readString(); - } - } - - public static class KeyValueCommand extends KeyCommand { - private long value; - - public KeyValueCommand() { - } - - public KeyValueCommand(String key, long value) { - super(key); - this.value = value; - } - - public long value() { - return value; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - buffer.writeLong(value); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - value = buffer.readLong(); - } - } - - public static class Get extends KeyQuery { - public Get() { - } - - public Get(String key) { - super(key); - } - } - - public static class Put extends KeyValueCommand { - public Put() { - } - - public Put(String key, long value) { - super(key, value); - } - } - - public static class PutIfAbsent extends KeyValueCommand { - public PutIfAbsent() { - } - - public PutIfAbsent(String key, long value) { - super(key, value); - } - } - - public static class Replace extends KeyCommand { - private long replace; - private long value; - - public Replace() { - } - - public Replace(String key, long replace, long value) { - super(key); - this.replace = replace; - this.value = value; - } - - public long replace() { - return replace; - } - - public long value() { - return value; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - buffer.writeLong(replace); - buffer.writeLong(value); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - replace = buffer.readLong(); - value = buffer.readLong(); - } - } - - public static class Remove extends KeyCommand { - public Remove() { - } - - public Remove(String key) { - super(key); - } - } - - public static class RemoveValue extends KeyValueCommand { - public RemoveValue() { - } - - public RemoveValue(String key, long value) { - super(key, value); - } - } - - public static class IncrementAndGet extends KeyCommand { - public IncrementAndGet() { - } - - public IncrementAndGet(String key) { - super(key); - } - } - - public static class DecrementAndGet extends KeyCommand { - public DecrementAndGet(String key) { - super(key); - } - - public DecrementAndGet() { - } - } - - public static class GetAndIncrement extends KeyCommand { - public GetAndIncrement() { - } - - public GetAndIncrement(String key) { - super(key); - } - } - - public static class GetAndDecrement extends KeyCommand { - public GetAndDecrement() { - } - - public GetAndDecrement(String key) { - super(key); - } - } - - public abstract static class DeltaCommand extends KeyCommand { - private long delta; - - public DeltaCommand() { - } - - public DeltaCommand(String key, long delta) { - super(key); - this.delta = delta; - } - - public long delta() { - return delta; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - buffer.writeLong(delta); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - delta = buffer.readLong(); - } - } - - public static class AddAndGet extends DeltaCommand { - public AddAndGet() { - } - - public AddAndGet(String key, long delta) { - super(key, delta); - } - } - - public static class GetAndAdd extends DeltaCommand { - public GetAndAdd() { - } - - public GetAndAdd(String key, long delta) { - super(key, delta); - } - } - - public static class Size extends AtomicCounterMapQuery { - } - - public static class IsEmpty extends AtomicCounterMapQuery { - } - - public static class Clear extends AtomicCounterMapCommand { - } - - /** - * Counter map command type resolver. - */ - public static class TypeResolver implements SerializableTypeResolver { - @Override - public void resolve(SerializerRegistry registry) { - registry.register(Get.class, -790); - registry.register(Put.class, -791); - registry.register(PutIfAbsent.class, -792); - registry.register(Replace.class, -793); - registry.register(Remove.class, -794); - registry.register(RemoveValue.class, -795); - registry.register(IncrementAndGet.class, -796); - registry.register(DecrementAndGet.class, -797); - registry.register(GetAndIncrement.class, -798); - registry.register(GetAndDecrement.class, -799); - registry.register(AddAndGet.class, -800); - registry.register(GetAndAdd.class, -801); - registry.register(Size.class, -801); - registry.register(IsEmpty.class, -801); - registry.register(Clear.class, -801); - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapFactory.java deleted file mode 100644 index 4caf68e3f9..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapFactory.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.resources.impl; - -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.ResourceFactory; -import io.atomix.resource.ResourceStateMachine; - -import java.util.Properties; - -/** - * Atomic counter map factory. - */ -public class AtomixAtomicCounterMapFactory implements ResourceFactory { - - @Override - public SerializableTypeResolver createSerializableTypeResolver() { - return new AtomixAtomicCounterMapCommands.TypeResolver(); - } - - @Override - public ResourceStateMachine createStateMachine(Properties config) { - return new AtomixAtomicCounterMapState(config); - } - - @Override - public AtomixAtomicCounterMap createInstance(CopycatClient client, Properties options) { - return new AtomixAtomicCounterMap(client, options); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapOperations.java new file mode 100644 index 0000000000..df3508f822 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapOperations.java @@ -0,0 +1,248 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.operation.OperationId; +import io.atomix.protocols.raft.operation.OperationType; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.serializers.KryoNamespaces; + +/** + * Atomic counter map commands. + */ +public enum AtomixAtomicCounterMapOperations implements OperationId { + PUT("put", OperationType.COMMAND), + PUT_IF_ABSENT("putIfAbsent", OperationType.COMMAND), + GET("get", OperationType.QUERY), + REPLACE("replace", OperationType.COMMAND), + REMOVE("remove", OperationType.COMMAND), + REMOVE_VALUE("removeValue", OperationType.COMMAND), + GET_AND_INCREMENT("getAndIncrement", OperationType.COMMAND), + GET_AND_DECREMENT("getAndDecrement", OperationType.COMMAND), + INCREMENT_AND_GET("incrementAndGet", OperationType.COMMAND), + DECREMENT_AND_GET("decrementAndGet", OperationType.COMMAND), + ADD_AND_GET("addAndGet", OperationType.COMMAND), + GET_AND_ADD("getAndAdd", OperationType.COMMAND), + SIZE("size", OperationType.QUERY), + IS_EMPTY("isEmpty", OperationType.QUERY), + CLEAR("clear", OperationType.COMMAND); + + private final String id; + private final OperationType type; + + AtomixAtomicCounterMapOperations(String id, OperationType type) { + this.id = id; + this.type = type; + } + + @Override + public String id() { + return id; + } + + @Override + public OperationType type() { + return type; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID) + .register(IncrementAndGet.class) + .register(DecrementAndGet.class) + .register(GetAndIncrement.class) + .register(GetAndDecrement.class) + .register(AddAndGet.class) + .register(GetAndAdd.class) + .register(Get.class) + .register(Put.class) + .register(PutIfAbsent.class) + .register(Replace.class) + .register(Remove.class) + .register(RemoveValue.class) + .build("AtomixAtomicCounterMapOperations"); + + public abstract static class AtomicCounterMapOperation { + } + + public abstract static class KeyOperation extends AtomicCounterMapOperation { + private String key; + + public KeyOperation() { + } + + public KeyOperation(String key) { + this.key = key; + } + + public String key() { + return key; + } + } + + public static class KeyValueOperation extends KeyOperation { + private long value; + + public KeyValueOperation() { + } + + public KeyValueOperation(String key, long value) { + super(key); + this.value = value; + } + + public long value() { + return value; + } + } + + public static class Get extends KeyOperation { + public Get() { + } + + public Get(String key) { + super(key); + } + } + + public static class Put extends KeyValueOperation { + public Put() { + } + + public Put(String key, long value) { + super(key, value); + } + } + + public static class PutIfAbsent extends KeyValueOperation { + public PutIfAbsent() { + } + + public PutIfAbsent(String key, long value) { + super(key, value); + } + } + + public static class Replace extends KeyOperation { + private long replace; + private long value; + + public Replace() { + } + + public Replace(String key, long replace, long value) { + super(key); + this.replace = replace; + this.value = value; + } + + public long replace() { + return replace; + } + + public long value() { + return value; + } + } + + public static class Remove extends KeyOperation { + public Remove() { + } + + public Remove(String key) { + super(key); + } + } + + public static class RemoveValue extends KeyValueOperation { + public RemoveValue() { + } + + public RemoveValue(String key, long value) { + super(key, value); + } + } + + public static class IncrementAndGet extends KeyOperation { + public IncrementAndGet() { + } + + public IncrementAndGet(String key) { + super(key); + } + } + + public static class DecrementAndGet extends KeyOperation { + public DecrementAndGet(String key) { + super(key); + } + + public DecrementAndGet() { + } + } + + public static class GetAndIncrement extends KeyOperation { + public GetAndIncrement() { + } + + public GetAndIncrement(String key) { + super(key); + } + } + + public static class GetAndDecrement extends KeyOperation { + public GetAndDecrement() { + } + + public GetAndDecrement(String key) { + super(key); + } + } + + public abstract static class DeltaOperation extends KeyOperation { + private long delta; + + public DeltaOperation() { + } + + public DeltaOperation(String key, long delta) { + super(key); + this.delta = delta; + } + + public long delta() { + return delta; + } + } + + public static class AddAndGet extends DeltaOperation { + public AddAndGet() { + } + + public AddAndGet(String key, long delta) { + super(key, delta); + } + } + + public static class GetAndAdd extends DeltaOperation { + public GetAndAdd() { + } + + public GetAndAdd(String key, long delta) { + super(key, delta); + } + } +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapService.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapService.java new file mode 100644 index 0000000000..b895d7132f --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapService.java @@ -0,0 +1,303 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import java.util.HashMap; +import java.util.Map; + +import io.atomix.protocols.raft.service.AbstractRaftService; +import io.atomix.protocols.raft.service.Commit; +import io.atomix.protocols.raft.service.RaftServiceExecutor; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.AddAndGet; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.DecrementAndGet; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Get; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GetAndAdd; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GetAndDecrement; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GetAndIncrement; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.IncrementAndGet; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Put; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PutIfAbsent; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Remove; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.RemoveValue; +import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.Replace; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.Serializer; + +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.ADD_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.CLEAR; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.DECREMENT_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET_AND_ADD; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET_AND_DECREMENT; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET_AND_INCREMENT; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.INCREMENT_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.IS_EMPTY; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PUT; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PUT_IF_ABSENT; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.REMOVE; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.REMOVE_VALUE; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.REPLACE; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.SIZE; + +/** + * Atomic counter map state for Atomix. + *

+ * The counter map state is implemented as a snapshottable state machine. Snapshots are necessary + * since incremental compaction is impractical for counters where the value of a counter is the sum + * of all its increments. Note that this snapshotting large state machines may risk blocking of the + * Raft cluster with the current implementation of snapshotting in Copycat. + */ +public class AtomixAtomicCounterMapService extends AbstractRaftService { + + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixAtomicCounterMapOperations.NAMESPACE) + .build()); + + private Map map = new HashMap<>(); + + @Override + protected void configure(RaftServiceExecutor executor) { + executor.register(PUT, SERIALIZER::decode, this::put, SERIALIZER::encode); + executor.register(PUT_IF_ABSENT, SERIALIZER::decode, this::putIfAbsent, SERIALIZER::encode); + executor.register(GET, SERIALIZER::decode, this::get, SERIALIZER::encode); + executor.register(REPLACE, SERIALIZER::decode, this::replace, SERIALIZER::encode); + executor.register(REMOVE, SERIALIZER::decode, this::remove, SERIALIZER::encode); + executor.register(REMOVE_VALUE, SERIALIZER::decode, this::removeValue, SERIALIZER::encode); + executor.register(GET_AND_INCREMENT, SERIALIZER::decode, this::getAndIncrement, SERIALIZER::encode); + executor.register(GET_AND_DECREMENT, SERIALIZER::decode, this::getAndDecrement, SERIALIZER::encode); + executor.register(INCREMENT_AND_GET, SERIALIZER::decode, this::incrementAndGet, SERIALIZER::encode); + executor.register(DECREMENT_AND_GET, SERIALIZER::decode, this::decrementAndGet, SERIALIZER::encode); + executor.register(ADD_AND_GET, SERIALIZER::decode, this::addAndGet, SERIALIZER::encode); + executor.register(GET_AND_ADD, SERIALIZER::decode, this::getAndAdd, SERIALIZER::encode); + executor.register(SIZE, this::size, SERIALIZER::encode); + executor.register(IS_EMPTY, this::isEmpty, SERIALIZER::encode); + executor.register(CLEAR, this::clear); + } + + @Override + public void snapshot(SnapshotWriter writer) { + writer.writeObject(map, SERIALIZER::encode); + } + + @Override + public void install(SnapshotReader reader) { + map = reader.readObject(SERIALIZER::decode); + } + + /** + * Returns the primitive value for the given primitive wrapper. + */ + private long primitive(Long value) { + if (value != null) { + return value; + } else { + return 0; + } + } + + /** + * Handles a {@link Put} command which implements {@link AtomixAtomicCounterMap#put(String, long)}. + * + * @param commit put commit + * @return put result + */ + protected long put(Commit commit) { + return primitive(map.put(commit.value().key(), commit.value().value())); + } + + /** + * Handles a {@link PutIfAbsent} command which implements {@link AtomixAtomicCounterMap#putIfAbsent(String, long)}. + * + * @param commit putIfAbsent commit + * @return putIfAbsent result + */ + protected long putIfAbsent(Commit commit) { + return primitive(map.putIfAbsent(commit.value().key(), commit.value().value())); + } + + /** + * Handles a {@link Get} query which implements {@link AtomixAtomicCounterMap#get(String)}}. + * + * @param commit get commit + * @return get result + */ + protected long get(Commit commit) { + return primitive(map.get(commit.value().key())); + } + + /** + * Handles a {@link Replace} command which implements {@link AtomixAtomicCounterMap#replace(String, long, long)}. + * + * @param commit replace commit + * @return replace result + */ + protected boolean replace(Commit commit) { + Long value = map.get(commit.value().key()); + if (value == null) { + if (commit.value().replace() == 0) { + map.put(commit.value().key(), commit.value().value()); + return true; + } else { + return false; + } + } else if (value == commit.value().replace()) { + map.put(commit.value().key(), commit.value().value()); + return true; + } + return false; + } + + /** + * Handles a {@link Remove} command which implements {@link AtomixAtomicCounterMap#remove(String)}. + * + * @param commit remove commit + * @return remove result + */ + protected long remove(Commit commit) { + return primitive(map.remove(commit.value().key())); + } + + /** + * Handles a {@link RemoveValue} command which implements {@link AtomixAtomicCounterMap#remove(String, long)}. + * + * @param commit removeValue commit + * @return removeValue result + */ + protected boolean removeValue(Commit commit) { + Long value = map.get(commit.value().key()); + if (value == null) { + if (commit.value().value() == 0) { + map.remove(commit.value().key()); + return true; + } + return false; + } else if (value == commit.value().value()) { + map.remove(commit.value().key()); + return true; + } + return false; + } + + /** + * Handles a {@link GetAndIncrement} command which implements + * {@link AtomixAtomicCounterMap#getAndIncrement(String)}. + * + * @param commit getAndIncrement commit + * @return getAndIncrement result + */ + protected long getAndIncrement(Commit commit) { + long value = primitive(map.get(commit.value().key())); + map.put(commit.value().key(), value + 1); + return value; + } + + /** + * Handles a {@link GetAndDecrement} command which implements + * {@link AtomixAtomicCounterMap#getAndDecrement(String)}. + * + * @param commit getAndDecrement commit + * @return getAndDecrement result + */ + protected long getAndDecrement(Commit commit) { + long value = primitive(map.get(commit.value().key())); + map.put(commit.value().key(), value - 1); + return value; + } + + /** + * Handles a {@link IncrementAndGet} command which implements + * {@link AtomixAtomicCounterMap#incrementAndGet(String)}. + * + * @param commit incrementAndGet commit + * @return incrementAndGet result + */ + protected long incrementAndGet(Commit commit) { + long value = primitive(map.get(commit.value().key())); + map.put(commit.value().key(), ++value); + return value; + } + + /** + * Handles a {@link DecrementAndGet} command which implements + * {@link AtomixAtomicCounterMap#decrementAndGet(String)}. + * + * @param commit decrementAndGet commit + * @return decrementAndGet result + */ + protected long decrementAndGet(Commit commit) { + long value = primitive(map.get(commit.value().key())); + map.put(commit.value().key(), --value); + return value; + } + + /** + * Handles a {@link AddAndGet} command which implements {@link AtomixAtomicCounterMap#addAndGet(String, long)}. + * + * @param commit addAndGet commit + * @return addAndGet result + */ + protected long addAndGet(Commit commit) { + long value = primitive(map.get(commit.value().key())); + value += commit.value().delta(); + map.put(commit.value().key(), value); + return value; + } + + /** + * Handles a {@link GetAndAdd} command which implements {@link AtomixAtomicCounterMap#getAndAdd(String, long)}. + * + * @param commit getAndAdd commit + * @return getAndAdd result + */ + protected long getAndAdd(Commit commit) { + long value = primitive(map.get(commit.value().key())); + map.put(commit.value().key(), value + commit.value().delta()); + return value; + } + + /** + * Handles a {@code Size} query which implements {@link AtomixAtomicCounterMap#size()}. + * + * @param commit size commit + * @return size result + */ + protected int size(Commit commit) { + return map.size(); + } + + /** + * Handles an {@code IsEmpty} query which implements {@link AtomixAtomicCounterMap#isEmpty()}. + * + * @param commit isEmpty commit + * @return isEmpty result + */ + protected boolean isEmpty(Commit commit) { + return map.isEmpty(); + } + + /** + * Handles a {@code Clear} command which implements {@link AtomixAtomicCounterMap#clear()}. + * + * @param commit clear commit + */ + protected void clear(Commit commit) { + map.clear(); + } +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapState.java deleted file mode 100644 index 1aed4a896d..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapState.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.resources.impl; - -import io.atomix.copycat.server.Commit; -import io.atomix.copycat.server.Snapshottable; -import io.atomix.copycat.server.StateMachineExecutor; -import io.atomix.copycat.server.storage.snapshot.SnapshotReader; -import io.atomix.copycat.server.storage.snapshot.SnapshotWriter; -import io.atomix.resource.ResourceStateMachine; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.AddAndGet; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Clear; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.DecrementAndGet; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Get; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.GetAndAdd; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.GetAndDecrement; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.GetAndIncrement; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.IncrementAndGet; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.IsEmpty; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Put; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.PutIfAbsent; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Remove; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.RemoveValue; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Replace; -import org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapCommands.Size; - -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -/** - * Atomic counter map state for Atomix. - *

- * The counter map state is implemented as a snapshottable state machine. Snapshots are necessary - * since incremental compaction is impractical for counters where the value of a counter is the sum - * of all its increments. Note that this snapshotting large state machines may risk blocking of the - * Raft cluster with the current implementation of snapshotting in Copycat. - */ -public class AtomixAtomicCounterMapState extends ResourceStateMachine implements Snapshottable { - private Map map = new HashMap<>(); - - public AtomixAtomicCounterMapState(Properties config) { - super(config); - } - - @Override - protected void configure(StateMachineExecutor executor) { - executor.register(Put.class, this::put); - executor.register(PutIfAbsent.class, this::putIfAbsent); - executor.register(Get.class, this::get); - executor.register(Replace.class, this::replace); - executor.register(Remove.class, this::remove); - executor.register(RemoveValue.class, this::removeValue); - executor.register(GetAndIncrement.class, this::getAndIncrement); - executor.register(GetAndDecrement.class, this::getAndDecrement); - executor.register(IncrementAndGet.class, this::incrementAndGet); - executor.register(DecrementAndGet.class, this::decrementAndGet); - executor.register(AddAndGet.class, this::addAndGet); - executor.register(GetAndAdd.class, this::getAndAdd); - executor.register(Size.class, this::size); - executor.register(IsEmpty.class, this::isEmpty); - executor.register(Clear.class, this::clear); - } - - @Override - public void snapshot(SnapshotWriter writer) { - writer.writeObject(map); - } - - @Override - public void install(SnapshotReader reader) { - map = reader.readObject(); - } - - /** - * Returns the primitive value for the given primitive wrapper. - */ - private long primitive(Long value) { - if (value != null) { - return value; - } else { - return 0; - } - } - - /** - * Handles a {@link Put} command which implements {@link AtomixAtomicCounterMap#put(String, long)}. - * - * @param commit put commit - * @return put result - */ - protected long put(Commit commit) { - try { - return primitive(map.put(commit.operation().key(), commit.operation().value())); - } finally { - commit.close(); - } - } - - /** - * Handles a {@link PutIfAbsent} command which implements {@link AtomixAtomicCounterMap#putIfAbsent(String, long)}. - * - * @param commit putIfAbsent commit - * @return putIfAbsent result - */ - protected long putIfAbsent(Commit commit) { - try { - return primitive(map.putIfAbsent(commit.operation().key(), commit.operation().value())); - } finally { - commit.close(); - } - } - - /** - * Handles a {@link Get} query which implements {@link AtomixAtomicCounterMap#get(String)}}. - * - * @param commit get commit - * @return get result - */ - protected long get(Commit commit) { - try { - return primitive(map.get(commit.operation().key())); - } finally { - commit.close(); - } - } - - /** - * Handles a {@link Replace} command which implements {@link AtomixAtomicCounterMap#replace(String, long, long)}. - * - * @param commit replace commit - * @return replace result - */ - protected boolean replace(Commit commit) { - try { - Long value = map.get(commit.operation().key()); - if (value == null) { - if (commit.operation().replace() == 0) { - map.put(commit.operation().key(), commit.operation().value()); - return true; - } else { - return false; - } - } else if (value == commit.operation().replace()) { - map.put(commit.operation().key(), commit.operation().value()); - return true; - } - return false; - } finally { - commit.close(); - } - } - - /** - * Handles a {@link Remove} command which implements {@link AtomixAtomicCounterMap#remove(String)}. - * - * @param commit remove commit - * @return remove result - */ - protected long remove(Commit commit) { - try { - return primitive(map.remove(commit.operation().key())); - } finally { - commit.close(); - } - } - - /** - * Handles a {@link RemoveValue} command which implements {@link AtomixAtomicCounterMap#remove(String, long)}. - * - * @param commit removeValue commit - * @return removeValue result - */ - protected boolean removeValue(Commit commit) { - try { - Long value = map.get(commit.operation().key()); - if (value == null) { - if (commit.operation().value() == 0) { - map.remove(commit.operation().key()); - return true; - } - return false; - } else if (value == commit.operation().value()) { - map.remove(commit.operation().key()); - return true; - } - return false; - } finally { - commit.close(); - } - } - - /** - * Handles a {@link GetAndIncrement} command which implements - * {@link AtomixAtomicCounterMap#getAndIncrement(String)}. - * - * @param commit getAndIncrement commit - * @return getAndIncrement result - */ - protected long getAndIncrement(Commit commit) { - try { - long value = primitive(map.get(commit.operation().key())); - map.put(commit.operation().key(), value + 1); - return value; - } finally { - commit.close(); - } - } - - /** - * Handles a {@link GetAndDecrement} command which implements - * {@link AtomixAtomicCounterMap#getAndDecrement(String)}. - * - * @param commit getAndDecrement commit - * @return getAndDecrement result - */ - protected long getAndDecrement(Commit commit) { - try { - long value = primitive(map.get(commit.operation().key())); - map.put(commit.operation().key(), value - 1); - return value; - } finally { - commit.close(); - } - } - - /** - * Handles a {@link IncrementAndGet} command which implements - * {@link AtomixAtomicCounterMap#incrementAndGet(String)}. - * - * @param commit incrementAndGet commit - * @return incrementAndGet result - */ - protected long incrementAndGet(Commit commit) { - try { - long value = primitive(map.get(commit.operation().key())); - map.put(commit.operation().key(), ++value); - return value; - } finally { - commit.close(); - } - } - - /** - * Handles a {@link DecrementAndGet} command which implements - * {@link AtomixAtomicCounterMap#decrementAndGet(String)}. - * - * @param commit decrementAndGet commit - * @return decrementAndGet result - */ - protected long decrementAndGet(Commit commit) { - try { - long value = primitive(map.get(commit.operation().key())); - map.put(commit.operation().key(), --value); - return value; - } finally { - commit.close(); - } - } - - /** - * Handles a {@link AddAndGet} command which implements {@link AtomixAtomicCounterMap#addAndGet(String, long)}. - * - * @param commit addAndGet commit - * @return addAndGet result - */ - protected long addAndGet(Commit commit) { - try { - long value = primitive(map.get(commit.operation().key())); - value += commit.operation().delta(); - map.put(commit.operation().key(), value); - return value; - } finally { - commit.close(); - } - } - - /** - * Handles a {@link GetAndAdd} command which implements {@link AtomixAtomicCounterMap#getAndAdd(String, long)}. - * - * @param commit getAndAdd commit - * @return getAndAdd result - */ - protected long getAndAdd(Commit commit) { - try { - long value = primitive(map.get(commit.operation().key())); - map.put(commit.operation().key(), value + commit.operation().delta()); - return value; - } finally { - commit.close(); - } - } - - /** - * Handles a {@link Size} query which implements {@link AtomixAtomicCounterMap#size()}. - * - * @param commit size commit - * @return size result - */ - protected int size(Commit commit) { - try { - return map.size(); - } finally { - commit.close(); - } - } - - /** - * Handles an {@link IsEmpty} query which implements {@link AtomixAtomicCounterMap#isEmpty()}. - * - * @param commit isEmpty commit - * @return isEmpty result - */ - protected boolean isEmpty(Commit commit) { - try { - return map.isEmpty(); - } finally { - commit.close(); - } - } - - /** - * Handles a {@link Clear} command which implements {@link AtomixAtomicCounterMap#clear()}. - * - * @param commit clear commit - */ - protected void clear(Commit commit) { - try { - map.clear(); - } finally { - commit.close(); - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMap.java index 7220c37092..f3a1ea2d8c 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMap.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMap.java @@ -15,146 +15,149 @@ */ package org.onosproject.store.primitives.resources.impl; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.AbstractResource; -import io.atomix.resource.ResourceTypeInfo; - -import java.util.concurrent.ConcurrentHashMap; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Properties; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiFunction; -import java.util.function.Consumer; import java.util.function.Predicate; +import io.atomix.protocols.raft.proxy.RaftProxy; +import org.onlab.util.KryoNamespace; import org.onlab.util.Match; import org.onlab.util.Tools; import org.onosproject.store.primitives.MapUpdate; import org.onosproject.store.primitives.TransactionId; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Clear; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.ContainsKey; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.ContainsValue; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.EntrySet; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Get; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.GetOrDefault; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.IsEmpty; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.KeySet; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Listen; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Size; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionBegin; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionCommit; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionPrepare; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionPrepareAndCommit; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionRollback; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Unlisten; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.UpdateAndGet; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Values; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ContainsKey; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ContainsValue; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.Get; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GetOrDefault; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionBegin; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionCommit; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionPrepare; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionPrepareAndCommit; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionRollback; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.UpdateAndGet; +import org.onosproject.store.serializers.KryoNamespaces; import org.onosproject.store.service.AsyncConsistentMap; import org.onosproject.store.service.ConsistentMapException; import org.onosproject.store.service.MapEvent; import org.onosproject.store.service.MapEventListener; +import org.onosproject.store.service.Serializer; import org.onosproject.store.service.TransactionLog; import org.onosproject.store.service.Version; import org.onosproject.store.service.Versioned; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapEvents.CHANGE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ADD_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.BEGIN; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.CLEAR; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.COMMIT; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.CONTAINS_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.CONTAINS_VALUE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ENTRY_SET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GET_OR_DEFAULT; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.IS_EMPTY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.KEY_SET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.PREPARE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.PREPARE_AND_COMMIT; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.REMOVE_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ROLLBACK; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.SIZE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.UPDATE_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.VALUES; /** * Distributed resource providing the {@link AsyncConsistentMap} primitive. */ -@ResourceTypeInfo(id = -151, factory = AtomixConsistentMapFactory.class) -public class AtomixConsistentMap extends AbstractResource - implements AsyncConsistentMap { +public class AtomixConsistentMap extends AbstractRaftPrimitive implements AsyncConsistentMap { + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixConsistentMapOperations.NAMESPACE) + .register(AtomixConsistentMapEvents.NAMESPACE) + .build()); - private final Set> statusChangeListeners = Sets.newCopyOnWriteArraySet(); private final Map, Executor> mapEventListeners = new ConcurrentHashMap<>(); - public static final String CHANGE_SUBJECT = "changeEvents"; - - public AtomixConsistentMap(CopycatClient client, Properties properties) { - super(client, properties); - } - - @Override - public String name() { - return null; - } - - @Override - public CompletableFuture open() { - return super.open().thenApply(result -> { - client.onStateChange(state -> { - if (state == CopycatClient.State.CONNECTED && isListening()) { - client.submit(new Listen()); - } - }); - client.onEvent(CHANGE_SUBJECT, this::handleEvent); - return result; + public AtomixConsistentMap(RaftProxy proxy) { + super(proxy); + proxy.addEventListener(CHANGE, SERIALIZER::decode, this::handleEvent); + proxy.addStateChangeListener(state -> { + if (state == RaftProxy.State.CONNECTED && isListening()) { + proxy.invoke(ADD_LISTENER); + } }); } private void handleEvent(List> events) { events.forEach(event -> - mapEventListeners.forEach((listener, executor) -> executor.execute(() -> listener.event(event)))); + mapEventListeners.forEach((listener, executor) -> executor.execute(() -> listener.event(event)))); } @Override public CompletableFuture isEmpty() { - return client.submit(new IsEmpty()); + return proxy.invoke(IS_EMPTY, SERIALIZER::decode); } @Override public CompletableFuture size() { - return client.submit(new Size()); + return proxy.invoke(SIZE, SERIALIZER::decode); } @Override public CompletableFuture containsKey(String key) { - return client.submit(new ContainsKey(key)); + return proxy.invoke(CONTAINS_KEY, SERIALIZER::encode, new ContainsKey(key), SERIALIZER::decode); } @Override public CompletableFuture containsValue(byte[] value) { - return client.submit(new ContainsValue(value)); + return proxy.invoke(CONTAINS_VALUE, SERIALIZER::encode, new ContainsValue(value), SERIALIZER::decode); } @Override public CompletableFuture> get(String key) { - return client.submit(new Get(key)); + return proxy.invoke(GET, SERIALIZER::encode, new Get(key), SERIALIZER::decode); } @Override public CompletableFuture> getOrDefault(String key, byte[] defaultValue) { - return client.submit(new GetOrDefault(key, defaultValue)); + return proxy.invoke( + GET_OR_DEFAULT, + SERIALIZER::encode, + new GetOrDefault(key, defaultValue), + SERIALIZER::decode); } @Override public CompletableFuture> keySet() { - return client.submit(new KeySet()); + return proxy.invoke(KEY_SET, SERIALIZER::decode); } @Override public CompletableFuture>> values() { - return client.submit(new Values()); + return proxy.invoke(VALUES, SERIALIZER::decode); } @Override public CompletableFuture>>> entrySet() { - return client.submit(new EntrySet()); + return proxy.invoke(ENTRY_SET, SERIALIZER::decode); } @Override @SuppressWarnings("unchecked") public CompletableFuture> put(String key, byte[] value) { - return client.submit(new UpdateAndGet(key, value, Match.ANY, Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, value, Match.ANY, Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.oldValue()); } @@ -162,7 +165,11 @@ public class AtomixConsistentMap extends AbstractResource @Override @SuppressWarnings("unchecked") public CompletableFuture> putAndGet(String key, byte[] value) { - return client.submit(new UpdateAndGet(key, value, Match.ANY, Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, value, Match.ANY, Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.newValue()); } @@ -170,14 +177,23 @@ public class AtomixConsistentMap extends AbstractResource @Override @SuppressWarnings("unchecked") public CompletableFuture> putIfAbsent(String key, byte[] value) { - return client.submit(new UpdateAndGet(key, value, Match.NULL, Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, value, Match.NULL, Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.oldValue()); } + @Override @SuppressWarnings("unchecked") public CompletableFuture> remove(String key) { - return client.submit(new UpdateAndGet(key, null, Match.ANY, Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, null, Match.ANY, Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.oldValue()); } @@ -185,7 +201,11 @@ public class AtomixConsistentMap extends AbstractResource @Override @SuppressWarnings("unchecked") public CompletableFuture remove(String key, byte[] value) { - return client.submit(new UpdateAndGet(key, null, Match.ifValue(value), Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, null, Match.ifValue(value), Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.updated()); } @@ -193,7 +213,11 @@ public class AtomixConsistentMap extends AbstractResource @Override @SuppressWarnings("unchecked") public CompletableFuture remove(String key, long version) { - return client.submit(new UpdateAndGet(key, null, Match.ANY, Match.ifValue(version))) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, null, Match.ANY, Match.ifValue(version)), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.updated()); } @@ -201,7 +225,11 @@ public class AtomixConsistentMap extends AbstractResource @Override @SuppressWarnings("unchecked") public CompletableFuture> replace(String key, byte[] value) { - return client.submit(new UpdateAndGet(key, value, Match.NOT_NULL, Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, value, Match.NOT_NULL, Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.oldValue()); } @@ -209,7 +237,11 @@ public class AtomixConsistentMap extends AbstractResource @Override @SuppressWarnings("unchecked") public CompletableFuture replace(String key, byte[] oldValue, byte[] newValue) { - return client.submit(new UpdateAndGet(key, newValue, Match.ifValue(oldValue), Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, newValue, Match.ifValue(oldValue), Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.updated()); } @@ -217,14 +249,18 @@ public class AtomixConsistentMap extends AbstractResource @Override @SuppressWarnings("unchecked") public CompletableFuture replace(String key, long oldVersion, byte[] newValue) { - return client.submit(new UpdateAndGet(key, newValue, Match.ANY, Match.ifValue(oldVersion))) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, newValue, Match.ANY, Match.ifValue(oldVersion)), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.updated()); } @Override public CompletableFuture clear() { - return client.submit(new Clear()) + return proxy.invoke(CLEAR, SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r)) .thenApply(v -> null); } @@ -233,7 +269,7 @@ public class AtomixConsistentMap extends AbstractResource @SuppressWarnings("unchecked") public CompletableFuture> computeIf(String key, Predicate condition, - BiFunction remappingFunction) { + BiFunction remappingFunction) { return get(key).thenCompose(r1 -> { byte[] existingValue = r1 == null ? null : r1.value(); // if the condition evaluates to false, return existing value. @@ -255,27 +291,31 @@ public class AtomixConsistentMap extends AbstractResource } Match valueMatch = r1 == null ? Match.NULL : Match.ANY; Match versionMatch = r1 == null ? Match.ANY : Match.ifValue(r1.version()); - return client.submit(new UpdateAndGet(key, - computedValue.get(), - valueMatch, - versionMatch)) - .whenComplete((r, e) -> throwIfLocked(r.status())) - .thenCompose(r -> { - if (r.status() == MapEntryUpdateResult.Status.PRECONDITION_FAILED || - r.status() == MapEntryUpdateResult.Status.WRITE_LOCK) { - return Tools.exceptionalFuture(new ConsistentMapException.ConcurrentModification()); - } - return CompletableFuture.completedFuture(r); - }) - .thenApply(v -> v.newValue()); + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, + computedValue.get(), + valueMatch, + versionMatch), + SERIALIZER::decode) + .whenComplete((r, e) -> throwIfLocked(r.status())) + .thenCompose(r -> { + if (r.status() == MapEntryUpdateResult.Status.PRECONDITION_FAILED || + r.status() == MapEntryUpdateResult.Status.WRITE_LOCK) { + return Tools.exceptionalFuture(new ConsistentMapException.ConcurrentModification()); + } + return CompletableFuture.completedFuture(r); + }) + .thenApply(v -> v.newValue()); }); } @Override public synchronized CompletableFuture addListener(MapEventListener listener, - Executor executor) { + Executor executor) { if (mapEventListeners.isEmpty()) { - return client.submit(new Listen()).thenRun(() -> mapEventListeners.put(listener, executor)); + return proxy.invoke(ADD_LISTENER).thenRun(() -> mapEventListeners.put(listener, executor)); } else { mapEventListeners.put(listener, executor); return CompletableFuture.completedFuture(null); @@ -285,7 +325,7 @@ public class AtomixConsistentMap extends AbstractResource @Override public synchronized CompletableFuture removeListener(MapEventListener listener) { if (mapEventListeners.remove(listener) != null && mapEventListeners.isEmpty()) { - return client.submit(new Unlisten()).thenApply(v -> null); + return proxy.invoke(REMOVE_LISTENER).thenApply(v -> null); } return CompletableFuture.completedFuture(null); } @@ -298,49 +338,55 @@ public class AtomixConsistentMap extends AbstractResource @Override public CompletableFuture begin(TransactionId transactionId) { - return client.submit(new TransactionBegin(transactionId)).thenApply(Version::new); + return proxy.invoke( + BEGIN, + SERIALIZER::encode, + new TransactionBegin(transactionId), + SERIALIZER::decode) + .thenApply(Version::new); } @Override - public CompletableFuture prepare( - TransactionLog> transactionLog) { - return client.submit(new TransactionPrepare(transactionLog)) + public CompletableFuture prepare(TransactionLog> transactionLog) { + return proxy.invoke( + PREPARE, + SERIALIZER::encode, + new TransactionPrepare(transactionLog), + SERIALIZER::decode) .thenApply(v -> v == PrepareResult.OK); } @Override - public CompletableFuture prepareAndCommit( - TransactionLog> transactionLog) { - return client.submit(new TransactionPrepareAndCommit(transactionLog)) + public CompletableFuture prepareAndCommit(TransactionLog> transactionLog) { + return proxy.invoke( + PREPARE_AND_COMMIT, + SERIALIZER::encode, + new TransactionPrepareAndCommit(transactionLog), + SERIALIZER::decode) .thenApply(v -> v == PrepareResult.OK); } @Override public CompletableFuture commit(TransactionId transactionId) { - return client.submit(new TransactionCommit(transactionId)).thenApply(v -> null); + return proxy.invoke( + COMMIT, + SERIALIZER::encode, + new TransactionCommit(transactionId), + SERIALIZER::decode) + .thenApply(v -> null); } @Override public CompletableFuture rollback(TransactionId transactionId) { - return client.submit(new TransactionRollback(transactionId)).thenApply(v -> null); - } - - @Override - public void addStatusChangeListener(Consumer listener) { - statusChangeListeners.add(listener); - } - - @Override - public void removeStatusChangeListener(Consumer listener) { - statusChangeListeners.remove(listener); - } - - @Override - public Collection> statusChangeListeners() { - return ImmutableSet.copyOf(statusChangeListeners); + return proxy.invoke( + ROLLBACK, + SERIALIZER::encode, + new TransactionRollback(transactionId), + SERIALIZER::decode) + .thenApply(v -> null); } private boolean isListening() { return !mapEventListeners.isEmpty(); } -} +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapCommands.java deleted file mode 100644 index 8dd4096541..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapCommands.java +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.resources.impl; - -import java.util.Collection; -import java.util.Map; -import java.util.Set; - -import com.google.common.base.MoreObjects; -import io.atomix.catalyst.buffer.BufferInput; -import io.atomix.catalyst.buffer.BufferOutput; -import io.atomix.catalyst.serializer.CatalystSerializable; -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.catalyst.serializer.Serializer; -import io.atomix.catalyst.serializer.SerializerRegistry; -import io.atomix.catalyst.util.Assert; -import io.atomix.copycat.Command; -import io.atomix.copycat.Query; -import org.onlab.util.Match; -import org.onosproject.store.primitives.MapUpdate; -import org.onosproject.store.primitives.TransactionId; -import org.onosproject.store.service.TransactionLog; -import org.onosproject.store.service.Versioned; - -/** - * {@link AtomixConsistentMap} resource state machine operations. - */ -public final class AtomixConsistentMapCommands { - - private AtomixConsistentMapCommands() { - } - - /** - * Abstract map command. - */ - @SuppressWarnings("serial") - public abstract static class MapCommand implements Command, CatalystSerializable { - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - } - - /** - * Abstract map query. - */ - @SuppressWarnings("serial") - public abstract static class MapQuery implements Query, CatalystSerializable { - - @Override - public ConsistencyLevel consistency() { - return ConsistencyLevel.SEQUENTIAL; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - } - - /** - * Abstract key-based query. - */ - @SuppressWarnings("serial") - public abstract static class KeyQuery extends MapQuery { - protected String key; - - public KeyQuery() { - } - - public KeyQuery(String key) { - this.key = Assert.notNull(key, "key"); - } - - /** - * Returns the key. - * @return key - */ - public String key() { - return key; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("key", key) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(key, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - key = serializer.readObject(buffer); - } - } - - /** - * Abstract value-based query. - */ - @SuppressWarnings("serial") - public abstract static class ValueQuery extends MapQuery { - protected byte[] value; - - public ValueQuery() { - } - - public ValueQuery(byte[] value) { - this.value = Assert.notNull(value, "value"); - } - - /** - * Returns the value. - * @return value - */ - public byte[] value() { - return value; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("value", value) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(value, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - value = serializer.readObject(buffer); - } - } - - /** - * Contains key command. - */ - @SuppressWarnings("serial") - public static class ContainsKey extends KeyQuery { - public ContainsKey() { - } - - public ContainsKey(String key) { - super(key); - } - } - - /** - * Contains value command. - */ - @SuppressWarnings("serial") - public static class ContainsValue extends ValueQuery { - public ContainsValue() { - } - - public ContainsValue(byte[] value) { - super(value); - } - } - - /** - * Transaction begin command. - */ - public static class TransactionBegin extends MapCommand { - private TransactionId transactionId; - - public TransactionBegin() { - } - - public TransactionBegin(TransactionId transactionId) { - this.transactionId = transactionId; - } - - public TransactionId transactionId() { - return transactionId; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(transactionId, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - transactionId = serializer.readObject(buffer); - } - } - - /** - * Map prepare command. - */ - @SuppressWarnings("serial") - public static class TransactionPrepare extends MapCommand { - private TransactionLog> transactionLog; - - public TransactionPrepare() { - } - - public TransactionPrepare(TransactionLog> transactionLog) { - this.transactionLog = transactionLog; - } - - public TransactionLog> transactionLog() { - return transactionLog; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(transactionLog, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - transactionLog = serializer.readObject(buffer); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("transactionLog", transactionLog) - .toString(); - } - } - - /** - * Map prepareAndCommit command. - */ - @SuppressWarnings("serial") - public static class TransactionPrepareAndCommit extends TransactionPrepare { - public TransactionPrepareAndCommit() { - } - - public TransactionPrepareAndCommit(TransactionLog> transactionLog) { - super(transactionLog); - } - - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - } - - /** - * Map transaction commit command. - */ - @SuppressWarnings("serial") - public static class TransactionCommit extends MapCommand { - private TransactionId transactionId; - - public TransactionCommit() { - } - - public TransactionCommit(TransactionId transactionId) { - this.transactionId = transactionId; - } - - /** - * Returns the transaction identifier. - * @return transaction id - */ - public TransactionId transactionId() { - return transactionId; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(transactionId, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - transactionId = serializer.readObject(buffer); - } - - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("transactionId", transactionId) - .toString(); - } - } - - /** - * Map transaction rollback command. - */ - @SuppressWarnings("serial") - public static class TransactionRollback extends MapCommand { - private TransactionId transactionId; - - public TransactionRollback() { - } - - public TransactionRollback(TransactionId transactionId) { - this.transactionId = transactionId; - } - - /** - * Returns the transaction identifier. - * @return transaction id - */ - public TransactionId transactionId() { - return transactionId; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(transactionId, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - transactionId = serializer.readObject(buffer); - } - - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("transactionId", transactionId) - .toString(); - } - } - - /** - * Map update command. - */ - @SuppressWarnings("serial") - public static class UpdateAndGet extends MapCommand> { - private String key; - private byte[] value; - private Match valueMatch; - private Match versionMatch; - - public UpdateAndGet() { - } - - public UpdateAndGet(String key, - byte[] value, - Match valueMatch, - Match versionMatch) { - this.key = key; - this.value = value; - this.valueMatch = valueMatch; - this.versionMatch = versionMatch; - } - - /** - * Returns the key. - * @return key - */ - public String key() { - return this.key; - } - - /** - * Returns the value. - * @return value - */ - public byte[] value() { - return this.value; - } - - /** - * Returns the value match. - * @return value match - */ - public Match valueMatch() { - return this.valueMatch; - } - - /** - * Returns the version match. - * @return version match - */ - public Match versionMatch() { - return this.versionMatch; - } - - @Override - public CompactionMode compaction() { - return value == null ? CompactionMode.TOMBSTONE : CompactionMode.FULL; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(key, buffer); - serializer.writeObject(value, buffer); - serializer.writeObject(valueMatch, buffer); - serializer.writeObject(versionMatch, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - key = serializer.readObject(buffer); - value = serializer.readObject(buffer); - valueMatch = serializer.readObject(buffer); - versionMatch = serializer.readObject(buffer); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("key", key) - .add("value", value) - .add("valueMatch", valueMatch) - .add("versionMatch", versionMatch) - .toString(); - } - } - - /** - * Get query. - */ - @SuppressWarnings("serial") - public static class Get extends KeyQuery> { - public Get() { - } - - public Get(String key) { - super(key); - } - } - - /** - * Get or default query. - */ - @SuppressWarnings("serial") - public static class GetOrDefault extends KeyQuery> { - private byte[] defaultValue; - - public GetOrDefault() { - } - - public GetOrDefault(String key, byte[] defaultValue) { - super(key); - this.defaultValue = defaultValue; - } - - /** - * Returns the default value. - * - * @return the default value - */ - public byte[] defaultValue() { - return defaultValue; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(defaultValue, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - defaultValue = serializer.readObject(buffer); - } - } - - /** - * Is empty query. - */ - @SuppressWarnings("serial") - public static class IsEmpty extends MapQuery { - } - - /** - * KeySet query. - */ - @SuppressWarnings("serial") - public static class KeySet extends MapQuery> { - } - - /** - * ValueSet query. - */ - @SuppressWarnings("serial") - public static class Values extends MapQuery>> { - } - - /** - * EntrySet query. - */ - @SuppressWarnings("serial") - public static class EntrySet extends MapQuery>>> { - } - - /** - * Size query. - */ - @SuppressWarnings("serial") - public static class Size extends MapQuery { - } - - /** - * Clear command. - */ - @SuppressWarnings("serial") - public static class Clear extends MapCommand { - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - } - - /** - * Change listen. - */ - @SuppressWarnings("serial") - public static class Listen implements Command, CatalystSerializable { - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - - @Override - public CompactionMode compaction() { - return CompactionMode.QUORUM; - } - } - - /** - * Change unlisten. - */ - @SuppressWarnings("serial") - public static class Unlisten implements Command, CatalystSerializable { - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - } - - /** - * Map command type resolver. - */ - public static class TypeResolver implements SerializableTypeResolver { - @Override - public void resolve(SerializerRegistry registry) { - registry.register(ContainsKey.class, -761); - registry.register(ContainsValue.class, -762); - registry.register(Get.class, -763); - registry.register(GetOrDefault.class, -778); - registry.register(EntrySet.class, -764); - registry.register(Values.class, -765); - registry.register(KeySet.class, -766); - registry.register(Clear.class, -767); - registry.register(IsEmpty.class, -768); - registry.register(Size.class, -769); - registry.register(Listen.class, -770); - registry.register(Unlisten.class, -771); - registry.register(TransactionBegin.class, -777); - registry.register(TransactionPrepare.class, -772); - registry.register(TransactionCommit.class, -773); - registry.register(TransactionRollback.class, -774); - registry.register(TransactionPrepareAndCommit.class, -775); - registry.register(UpdateAndGet.class, -776); - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapEvents.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapEvents.java new file mode 100644 index 0000000000..f0e6a4235e --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapEvents.java @@ -0,0 +1,47 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.event.EventType; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.MapEvent; + +/** + * Atomix consistent map events. + */ +public enum AtomixConsistentMapEvents implements EventType { + CHANGE("change"); + + private final String id; + + AtomixConsistentMapEvents(String id) { + this.id = id; + } + + @Override + public String id() { + return id; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 50) + .register(MapEvent.class) + .register(MapEvent.Type.class) + .register(byte[].class) + .build("AtomixConsistentMapEvents"); + +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapFactory.java deleted file mode 100644 index bf1523a67a..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.resources.impl; - -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.ResourceFactory; -import io.atomix.resource.ResourceStateMachine; - -import java.util.Properties; - -/** - * {@link AtomixConsistentMap} resource factory. - * - */ -public class AtomixConsistentMapFactory implements ResourceFactory { - - @Override - public SerializableTypeResolver createSerializableTypeResolver() { - return new AtomixConsistentMapCommands.TypeResolver(); - } - - @Override - public ResourceStateMachine createStateMachine(Properties config) { - return new AtomixConsistentMapState(config); - } - - @Override - public AtomixConsistentMap createInstance(CopycatClient client, Properties options) { - return new AtomixConsistentMap(client, options); - } - } \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapOperations.java new file mode 100644 index 0000000000..2d81787a27 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapOperations.java @@ -0,0 +1,417 @@ +/* + * Copyright 2016-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import com.google.common.base.MoreObjects; +import io.atomix.protocols.raft.operation.OperationId; +import io.atomix.protocols.raft.operation.OperationType; +import org.onlab.util.KryoNamespace; +import org.onlab.util.Match; +import org.onosproject.store.primitives.MapUpdate; +import org.onosproject.store.primitives.TransactionId; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.TransactionLog; +import org.onosproject.store.service.Versioned; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link AtomixConsistentMap} resource state machine operations. + */ +public enum AtomixConsistentMapOperations implements OperationId { + IS_EMPTY("isEmpty", OperationType.QUERY), + SIZE("size", OperationType.QUERY), + CONTAINS_KEY("containsKey", OperationType.QUERY), + CONTAINS_VALUE("containsValue", OperationType.QUERY), + GET("get", OperationType.QUERY), + GET_OR_DEFAULT("getOrDefault", OperationType.QUERY), + KEY_SET("keySet", OperationType.QUERY), + VALUES("values", OperationType.QUERY), + ENTRY_SET("entrySet", OperationType.QUERY), + UPDATE_AND_GET("updateAndGet", OperationType.COMMAND), + CLEAR("clear", OperationType.COMMAND), + ADD_LISTENER("addListener", OperationType.COMMAND), + REMOVE_LISTENER("removeListener", OperationType.COMMAND), + BEGIN("begin", OperationType.COMMAND), + PREPARE("prepare", OperationType.COMMAND), + PREPARE_AND_COMMIT("prepareAndCommit", OperationType.COMMAND), + COMMIT("commit", OperationType.COMMAND), + ROLLBACK("rollback", OperationType.COMMAND); + + private final String id; + private final OperationType type; + + AtomixConsistentMapOperations(String id, OperationType type) { + this.id = id; + this.type = type; + } + + @Override + public String id() { + return id; + } + + @Override + public OperationType type() { + return type; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID) + .register(ContainsKey.class) + .register(ContainsValue.class) + .register(Get.class) + .register(GetOrDefault.class) + .register(UpdateAndGet.class) + .register(TransactionBegin.class) + .register(TransactionPrepare.class) + .register(TransactionPrepareAndCommit.class) + .register(TransactionCommit.class) + .register(TransactionRollback.class) + .register(TransactionId.class) + .register(TransactionLog.class) + .register(MapUpdate.class) + .register(MapUpdate.Type.class) + .register(PrepareResult.class) + .register(CommitResult.class) + .register(RollbackResult.class) + .register(Match.class) + .register(MapEntryUpdateResult.class) + .register(MapEntryUpdateResult.Status.class) + .register(Versioned.class) + .register(byte[].class) + .build("AtomixConsistentMapOperations"); + + /** + * Abstract map command. + */ + @SuppressWarnings("serial") + public abstract static class MapOperation { + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .toString(); + } + } + + /** + * Abstract key-based query. + */ + @SuppressWarnings("serial") + public abstract static class KeyOperation extends MapOperation { + protected String key; + + public KeyOperation() { + } + + public KeyOperation(String key) { + this.key = checkNotNull(key, "key cannot be null"); + } + + /** + * Returns the key. + * @return key + */ + public String key() { + return key; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("key", key) + .toString(); + } + } + + /** + * Abstract value-based query. + */ + @SuppressWarnings("serial") + public abstract static class ValueOperation extends MapOperation { + protected byte[] value; + + public ValueOperation() { + } + + public ValueOperation(byte[] value) { + this.value = checkNotNull(value, "value cannot be null"); + } + + /** + * Returns the value. + * @return value + */ + public byte[] value() { + return value; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("value", value) + .toString(); + } + } + + /** + * Contains key command. + */ + @SuppressWarnings("serial") + public static class ContainsKey extends KeyOperation { + public ContainsKey() { + } + + public ContainsKey(String key) { + super(key); + } + } + + /** + * Contains value command. + */ + @SuppressWarnings("serial") + public static class ContainsValue extends ValueOperation { + public ContainsValue() { + } + + public ContainsValue(byte[] value) { + super(value); + } + } + + /** + * Transaction begin command. + */ + public static class TransactionBegin extends MapOperation { + private TransactionId transactionId; + + public TransactionBegin() { + } + + public TransactionBegin(TransactionId transactionId) { + this.transactionId = transactionId; + } + + public TransactionId transactionId() { + return transactionId; + } + } + + /** + * Map prepare command. + */ + @SuppressWarnings("serial") + public static class TransactionPrepare extends MapOperation { + private TransactionLog> transactionLog; + + public TransactionPrepare() { + } + + public TransactionPrepare(TransactionLog> transactionLog) { + this.transactionLog = transactionLog; + } + + public TransactionLog> transactionLog() { + return transactionLog; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("transactionLog", transactionLog) + .toString(); + } + } + + /** + * Map prepareAndCommit command. + */ + @SuppressWarnings("serial") + public static class TransactionPrepareAndCommit extends TransactionPrepare { + public TransactionPrepareAndCommit() { + } + + public TransactionPrepareAndCommit(TransactionLog> transactionLog) { + super(transactionLog); + } + } + + /** + * Map transaction commit command. + */ + @SuppressWarnings("serial") + public static class TransactionCommit extends MapOperation { + private TransactionId transactionId; + + public TransactionCommit() { + } + + public TransactionCommit(TransactionId transactionId) { + this.transactionId = transactionId; + } + + /** + * Returns the transaction identifier. + * @return transaction id + */ + public TransactionId transactionId() { + return transactionId; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("transactionId", transactionId) + .toString(); + } + } + + /** + * Map transaction rollback command. + */ + @SuppressWarnings("serial") + public static class TransactionRollback extends MapOperation { + private TransactionId transactionId; + + public TransactionRollback() { + } + + public TransactionRollback(TransactionId transactionId) { + this.transactionId = transactionId; + } + + /** + * Returns the transaction identifier. + * @return transaction id + */ + public TransactionId transactionId() { + return transactionId; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("transactionId", transactionId) + .toString(); + } + } + + /** + * Map update command. + */ + @SuppressWarnings("serial") + public static class UpdateAndGet extends MapOperation { + private String key; + private byte[] value; + private Match valueMatch; + private Match versionMatch; + + public UpdateAndGet() { + } + + public UpdateAndGet(String key, + byte[] value, + Match valueMatch, + Match versionMatch) { + this.key = key; + this.value = value; + this.valueMatch = valueMatch; + this.versionMatch = versionMatch; + } + + /** + * Returns the key. + * @return key + */ + public String key() { + return this.key; + } + + /** + * Returns the value. + * @return value + */ + public byte[] value() { + return this.value; + } + + /** + * Returns the value match. + * @return value match + */ + public Match valueMatch() { + return this.valueMatch; + } + + /** + * Returns the version match. + * @return version match + */ + public Match versionMatch() { + return this.versionMatch; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("key", key) + .add("value", value) + .add("valueMatch", valueMatch) + .add("versionMatch", versionMatch) + .toString(); + } + } + + /** + * Get query. + */ + @SuppressWarnings("serial") + public static class Get extends KeyOperation { + public Get() { + } + + public Get(String key) { + super(key); + } + } + + /** + * Get or default query. + */ + @SuppressWarnings("serial") + public static class GetOrDefault extends KeyOperation { + private byte[] defaultValue; + + public GetOrDefault() { + } + + public GetOrDefault(String key, byte[] defaultValue) { + super(key); + this.defaultValue = defaultValue; + } + + /** + * Returns the default value. + * + * @return the default value + */ + public byte[] defaultValue() { + return defaultValue; + } + } +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapService.java similarity index 54% rename from core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapState.java rename to core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapService.java index ea62e72a4a..6d8c1b01f9 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapState.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapService.java @@ -15,119 +15,137 @@ */ package org.onosproject.store.primitives.resources.impl; -import static com.google.common.base.Preconditions.checkState; -import static org.onosproject.store.service.MapEvent.Type.INSERT; -import static org.onosproject.store.service.MapEvent.Type.REMOVE; -import static org.onosproject.store.service.MapEvent.Type.UPDATE; -import static org.slf4j.LoggerFactory.getLogger; -import io.atomix.copycat.server.Commit; -import io.atomix.copycat.server.Snapshottable; -import io.atomix.copycat.server.StateMachineExecutor; -import io.atomix.copycat.server.session.ServerSession; -import io.atomix.copycat.server.session.SessionListener; -import io.atomix.copycat.server.storage.snapshot.SnapshotReader; -import io.atomix.copycat.server.storage.snapshot.SnapshotWriter; -import io.atomix.resource.ResourceStateMachine; - import java.util.Collection; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.Set; import java.util.stream.Collectors; -import org.onlab.util.CountDownCompleter; -import org.onlab.util.Match; -import org.onosproject.store.primitives.MapUpdate; -import org.onosproject.store.primitives.TransactionId; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Clear; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.ContainsKey; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.ContainsValue; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.EntrySet; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Get; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.GetOrDefault; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.IsEmpty; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.KeySet; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Listen; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Size; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionBegin; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionCommit; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionPrepare; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionPrepareAndCommit; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.TransactionRollback; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Unlisten; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.UpdateAndGet; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapCommands.Values; -import org.onosproject.store.service.MapEvent; -import org.onosproject.store.service.TransactionLog; -import org.onosproject.store.service.Versioned; -import org.slf4j.Logger; - import com.google.common.base.Throwables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import io.atomix.protocols.raft.service.AbstractRaftService; +import io.atomix.protocols.raft.service.Commit; +import io.atomix.protocols.raft.service.RaftServiceExecutor; +import io.atomix.protocols.raft.session.RaftSession; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import org.onlab.util.KryoNamespace; +import org.onlab.util.Match; +import org.onosproject.store.primitives.MapUpdate; +import org.onosproject.store.primitives.TransactionId; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ContainsKey; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ContainsValue; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.Get; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GetOrDefault; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionBegin; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionCommit; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionPrepare; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionPrepareAndCommit; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.TransactionRollback; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.UpdateAndGet; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.MapEvent; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.TransactionLog; +import org.onosproject.store.service.Versioned; + +import static com.google.common.base.Preconditions.checkState; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapEvents.CHANGE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ADD_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.BEGIN; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.CLEAR; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.COMMIT; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.CONTAINS_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.CONTAINS_VALUE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ENTRY_SET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GET_OR_DEFAULT; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.IS_EMPTY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.KEY_SET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.PREPARE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.PREPARE_AND_COMMIT; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.REMOVE_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.ROLLBACK; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.SIZE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.UPDATE_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.VALUES; +import static org.onosproject.store.service.MapEvent.Type.INSERT; +import static org.onosproject.store.service.MapEvent.Type.REMOVE; +import static org.onosproject.store.service.MapEvent.Type.UPDATE; /** * State Machine for {@link AtomixConsistentMap} resource. */ -public class AtomixConsistentMapState extends ResourceStateMachine implements SessionListener, Snapshottable { +public class AtomixConsistentMapService extends AbstractRaftService { - private final Logger log = getLogger(getClass()); - private final Map> listeners = new HashMap<>(); - private final Map mapEntries = new HashMap<>(); - private final Set preparedKeys = Sets.newHashSet(); - private final Map activeTransactions = Maps.newHashMap(); + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixConsistentMapOperations.NAMESPACE) + .register(AtomixConsistentMapEvents.NAMESPACE) + .nextId(KryoNamespace.FLOATING_ID) + .register(TransactionScope.class) + .register(TransactionLog.class) + .register(TransactionId.class) + .register(MapEntryValue.class) + .register(MapEntryValue.Type.class) + .register(new HashMap().keySet().getClass()) + .build()); + + private Map listeners = new LinkedHashMap<>(); + private Map mapEntries = new HashMap<>(); + private Set preparedKeys = Sets.newHashSet(); + private Map activeTransactions = Maps.newHashMap(); private long currentVersion; - public AtomixConsistentMapState(Properties properties) { - super(properties); - } - @Override public void snapshot(SnapshotWriter writer) { + writer.writeObject(Sets.newHashSet(listeners.keySet()), SERIALIZER::encode); + writer.writeObject(preparedKeys, SERIALIZER::encode); + writer.writeObject(mapEntries, SERIALIZER::encode); + writer.writeObject(activeTransactions, SERIALIZER::encode); + writer.writeLong(currentVersion); } @Override public void install(SnapshotReader reader) { + listeners = new LinkedHashMap<>(); + for (Long sessionId : reader.>readObject(SERIALIZER::decode)) { + listeners.put(sessionId, getSessions().getSession(sessionId)); + } + preparedKeys = reader.readObject(SERIALIZER::decode); + mapEntries = reader.readObject(SERIALIZER::decode); + activeTransactions = reader.readObject(SERIALIZER::decode); + currentVersion = reader.readLong(); } @Override - protected void configure(StateMachineExecutor executor) { + protected void configure(RaftServiceExecutor executor) { // Listeners - executor.register(Listen.class, this::listen); - executor.register(Unlisten.class, this::unlisten); + executor.register(ADD_LISTENER, this::listen); + executor.register(REMOVE_LISTENER, this::unlisten); // Queries - executor.register(ContainsKey.class, this::containsKey); - executor.register(ContainsValue.class, this::containsValue); - executor.register(EntrySet.class, this::entrySet); - executor.register(Get.class, this::get); - executor.register(GetOrDefault.class, this::getOrDefault); - executor.register(IsEmpty.class, this::isEmpty); - executor.register(KeySet.class, this::keySet); - executor.register(Size.class, this::size); - executor.register(Values.class, this::values); + executor.register(CONTAINS_KEY, SERIALIZER::decode, this::containsKey, SERIALIZER::encode); + executor.register(CONTAINS_VALUE, SERIALIZER::decode, this::containsValue, SERIALIZER::encode); + executor.register(ENTRY_SET, this::entrySet, SERIALIZER::encode); + executor.register(GET, SERIALIZER::decode, this::get, SERIALIZER::encode); + executor.register(GET_OR_DEFAULT, SERIALIZER::decode, this::getOrDefault, SERIALIZER::encode); + executor.register(IS_EMPTY, this::isEmpty, SERIALIZER::encode); + executor.register(KEY_SET, this::keySet, SERIALIZER::encode); + executor.register(SIZE, this::size, SERIALIZER::encode); + executor.register(VALUES, this::values, SERIALIZER::encode); // Commands - executor.register(UpdateAndGet.class, this::updateAndGet); - executor.register(AtomixConsistentMapCommands.Clear.class, this::clear); - executor.register(TransactionBegin.class, this::begin); - executor.register(TransactionPrepare.class, this::prepare); - executor.register(TransactionCommit.class, this::commit); - executor.register(TransactionRollback.class, this::rollback); - executor.register(TransactionPrepareAndCommit.class, this::prepareAndCommit); - } - - @Override - public void delete() { - // Delete Listeners - listeners.values().forEach(Commit::close); - listeners.clear(); - - // Delete Map entries - mapEntries.values().forEach(MapEntryValue::discard); - mapEntries.clear(); + executor.register(UPDATE_AND_GET, SERIALIZER::decode, this::updateAndGet, SERIALIZER::encode); + executor.register(CLEAR, this::clear, SERIALIZER::encode); + executor.register(BEGIN, SERIALIZER::decode, this::begin, SERIALIZER::encode); + executor.register(PREPARE, SERIALIZER::decode, this::prepare, SERIALIZER::encode); + executor.register(PREPARE_AND_COMMIT, SERIALIZER::decode, this::prepareAndCommit, SERIALIZER::encode); + executor.register(COMMIT, SERIALIZER::decode, this::commit, SERIALIZER::encode); + executor.register(ROLLBACK, SERIALIZER::decode, this::rollback, SERIALIZER::encode); } /** @@ -137,12 +155,8 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @return {@code true} if map contains key */ protected boolean containsKey(Commit commit) { - try { - MapEntryValue value = mapEntries.get(commit.operation().key()); - return value != null && value.type() != MapEntryValue.Type.TOMBSTONE; - } finally { - commit.close(); - } + MapEntryValue value = mapEntries.get(commit.value().key()); + return value != null && value.type() != MapEntryValue.Type.TOMBSTONE; } /** @@ -152,14 +166,10 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @return {@code true} if map contains value */ protected boolean containsValue(Commit commit) { - try { - Match valueMatch = Match.ifValue(commit.operation().value()); - return mapEntries.values().stream() - .filter(value -> value.type() != MapEntryValue.Type.TOMBSTONE) - .anyMatch(value -> valueMatch.matches(value.value())); - } finally { - commit.close(); - } + Match valueMatch = Match.ifValue(commit.value().value()); + return mapEntries.values().stream() + .filter(value -> value.type() != MapEntryValue.Type.TOMBSTONE) + .anyMatch(value -> valueMatch.matches(value.value())); } /** @@ -169,11 +179,7 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @return value mapped to key */ protected Versioned get(Commit commit) { - try { - return toVersioned(mapEntries.get(commit.operation().key())); - } finally { - commit.close(); - } + return toVersioned(mapEntries.get(commit.value().key())); } /** @@ -183,17 +189,13 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @return value mapped to key */ protected Versioned getOrDefault(Commit commit) { - try { - MapEntryValue value = mapEntries.get(commit.operation().key()); - if (value == null) { - return new Versioned<>(commit.operation().defaultValue(), 0); - } else if (value.type() == MapEntryValue.Type.TOMBSTONE) { - return new Versioned<>(commit.operation().defaultValue(), value.version); - } else { - return new Versioned<>(value.value(), value.version); - } - } finally { - commit.close(); + MapEntryValue value = mapEntries.get(commit.value().key()); + if (value == null) { + return new Versioned<>(commit.value().defaultValue(), 0); + } else if (value.type() == MapEntryValue.Type.TOMBSTONE) { + return new Versioned<>(commit.value().defaultValue(), value.version); + } else { + return new Versioned<>(value.value(), value.version); } } @@ -203,14 +205,10 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @param commit size commit * @return number of entries in map */ - protected int size(Commit commit) { - try { - return (int) mapEntries.values().stream() - .filter(value -> value.type() != MapEntryValue.Type.TOMBSTONE) - .count(); - } finally { - commit.close(); - } + protected int size(Commit commit) { + return (int) mapEntries.values().stream() + .filter(value -> value.type() != MapEntryValue.Type.TOMBSTONE) + .count(); } /** @@ -219,13 +217,9 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @param commit isEmpty commit * @return {@code true} if map is empty */ - protected boolean isEmpty(Commit commit) { - try { - return mapEntries.values().stream() - .noneMatch(value -> value.type() != MapEntryValue.Type.TOMBSTONE); - } finally { - commit.close(); - } + protected boolean isEmpty(Commit commit) { + return mapEntries.values().stream() + .noneMatch(value -> value.type() != MapEntryValue.Type.TOMBSTONE); } /** @@ -234,15 +228,11 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @param commit keySet commit * @return set of keys in map */ - protected Set keySet(Commit commit) { - try { - return mapEntries.entrySet().stream() - .filter(entry -> entry.getValue().type() != MapEntryValue.Type.TOMBSTONE) - .map(Map.Entry::getKey) - .collect(Collectors.toSet()); - } finally { - commit.close(); - } + protected Set keySet(Commit commit) { + return mapEntries.entrySet().stream() + .filter(entry -> entry.getValue().type() != MapEntryValue.Type.TOMBSTONE) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); } /** @@ -251,33 +241,24 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @param commit values commit * @return collection of values in map */ - protected Collection> values(Commit commit) { - try { - return mapEntries.entrySet().stream() - .filter(entry -> entry.getValue().type() != MapEntryValue.Type.TOMBSTONE) - .map(entry -> toVersioned(entry.getValue())) - .collect(Collectors.toList()); - } finally { - commit.close(); - } + protected Collection> values(Commit commit) { + return mapEntries.entrySet().stream() + .filter(entry -> entry.getValue().type() != MapEntryValue.Type.TOMBSTONE) + .map(entry -> toVersioned(entry.getValue())) + .collect(Collectors.toList()); } /** * Handles a entry set commit. * - * @param commit - * entrySet commit + * @param commit entrySet commit * @return set of map entries */ - protected Set>> entrySet(Commit commit) { - try { - return mapEntries.entrySet().stream() - .filter(entry -> entry.getValue().type() != MapEntryValue.Type.TOMBSTONE) - .map(e -> Maps.immutableEntry(e.getKey(), toVersioned(e.getValue()))) - .collect(Collectors.toSet()); - } finally { - commit.close(); - } + protected Set>> entrySet(Commit commit) { + return mapEntries.entrySet().stream() + .filter(entry -> entry.getValue().type() != MapEntryValue.Type.TOMBSTONE) + .map(e -> Maps.immutableEntry(e.getKey(), toVersioned(e.getValue()))) + .collect(Collectors.toSet()); } /** @@ -288,17 +269,16 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se */ protected MapEntryUpdateResult updateAndGet(Commit commit) { try { - MapEntryUpdateResult.Status updateStatus = validate(commit.operation()); - String key = commit.operation().key(); - MapEntryValue oldCommitValue = mapEntries.get(commit.operation().key()); + MapEntryUpdateResult.Status updateStatus = validate(commit.value()); + String key = commit.value().key(); + MapEntryValue oldCommitValue = mapEntries.get(commit.value().key()); Versioned oldMapValue = toVersioned(oldCommitValue); if (updateStatus != MapEntryUpdateResult.Status.OK) { - commit.close(); return new MapEntryUpdateResult<>(updateStatus, "", key, oldMapValue, oldMapValue); } - byte[] newValue = commit.operation().value(); + byte[] newValue = commit.value().value(); currentVersion = commit.index(); Versioned newMapValue = newValue == null ? null : new Versioned<>(newValue, currentVersion); @@ -309,28 +289,24 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se // If a value existed in the map, remove and discard the value to ensure disk can be freed. if (updateType == REMOVE || updateType == UPDATE) { mapEntries.remove(key); - oldCommitValue.discard(); } // If this is an insert/update commit, add the commit to the map entries. if (updateType == INSERT || updateType == UPDATE) { - mapEntries.put(key, new NonTransactionalCommit(commit)); + mapEntries.put(key, new MapEntryValue( + MapEntryValue.Type.VALUE, + commit.index(), + commit.value().value())); } else if (!activeTransactions.isEmpty()) { // If this is a delete but transactions are currently running, ensure tombstones are retained // for version checks. - TombstoneCommit tombstone = new TombstoneCommit( - commit.index(), - new CountDownCompleter<>(commit, 1, Commit::close)); - mapEntries.put(key, tombstone); - } else { - // If no transactions are in progress, we can safely delete the key from memory. - commit.close(); + mapEntries.put(key, new MapEntryValue(MapEntryValue.Type.TOMBSTONE, commit.index(), null)); } publish(Lists.newArrayList(new MapEvent<>("", key, newMapValue, oldMapValue))); return new MapEntryUpdateResult<>(updateStatus, "", key, oldMapValue, newMapValue); } catch (Exception e) { - log.error("State machine operation failed", e); + getLogger().error("State machine operation failed", e); throw Throwables.propagate(e); } } @@ -341,24 +317,19 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @param commit clear commit * @return clear result */ - protected MapEntryUpdateResult.Status clear(Commit commit) { - try { - Iterator> iterator = mapEntries - .entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - String key = entry.getKey(); - MapEntryValue value = entry.getValue(); - Versioned removedValue = new Versioned<>(value.value(), - value.version()); - publish(Lists.newArrayList(new MapEvent<>("", key, null, removedValue))); - value.discard(); - iterator.remove(); - } - return MapEntryUpdateResult.Status.OK; - } finally { - commit.close(); + protected MapEntryUpdateResult.Status clear(Commit commit) { + Iterator> iterator = mapEntries + .entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + String key = entry.getKey(); + MapEntryValue value = entry.getValue(); + Versioned removedValue = new Versioned<>(value.value(), + value.version()); + publish(Lists.newArrayList(new MapEvent<>("", key, null, removedValue))); + iterator.remove(); } + return MapEntryUpdateResult.Status.OK; } /** @@ -366,23 +337,8 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * * @param commit listen commit */ - protected void listen(Commit commit) { - Long sessionId = commit.session().id(); - if (listeners.putIfAbsent(sessionId, commit) != null) { - commit.close(); - return; - } - commit.session() - .onStateChange( - state -> { - if (state == ServerSession.State.CLOSED - || state == ServerSession.State.EXPIRED) { - Commit listener = listeners.remove(sessionId); - if (listener != null) { - listener.close(); - } - } - }); + protected void listen(Commit commit) { + listeners.put(commit.session().sessionId().id(), commit.session()); } /** @@ -390,15 +346,8 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * * @param commit unlisten commit */ - protected void unlisten(Commit commit) { - try { - Commit listener = listeners.remove(commit.session().id()); - if (listener != null) { - listener.close(); - } - } finally { - commit.close(); - } + protected void unlisten(Commit commit) { + listeners.remove(commit.session().sessionId().id()); } /** @@ -408,13 +357,9 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @return transaction state version */ protected long begin(Commit commit) { - try { - long version = commit.index(); - activeTransactions.put(commit.operation().transactionId(), new TransactionScope(version)); - return version; - } finally { - commit.close(); - } + long version = commit.index(); + activeTransactions.put(commit.value().transactionId(), new TransactionScope(version)); + return version; } /** @@ -424,15 +369,13 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @return prepare result */ protected PrepareResult prepareAndCommit(Commit commit) { - TransactionId transactionId = commit.operation().transactionLog().transactionId(); + TransactionId transactionId = commit.value().transactionLog().transactionId(); PrepareResult prepareResult = prepare(commit); TransactionScope transactionScope = activeTransactions.remove(transactionId); if (prepareResult == PrepareResult.OK) { this.currentVersion = commit.index(); transactionScope = transactionScope.prepared(commit); - commit(transactionScope); - } else if (transactionScope != null) { - transactionScope.close(); + commitTransaction(transactionScope); } discardTombstones(); return prepareResult; @@ -445,10 +388,8 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @return prepare result */ protected PrepareResult prepare(Commit commit) { - boolean ok = false; - try { - TransactionLog> transactionLog = commit.operation().transactionLog(); + TransactionLog> transactionLog = commit.value().transactionLog(); // Iterate through records in the transaction log and perform isolation checks. for (MapUpdate record : transactionLog.records()) { @@ -495,8 +436,6 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se } }); - ok = true; - // Update the transaction scope. If the transaction scope is not set on this node, that indicates the // coordinator is communicating with another node. Transactions assume that the client is communicating // with a single leader in order to limit the overhead of retaining tombstones. @@ -504,7 +443,7 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se if (transactionScope == null) { activeTransactions.put( transactionLog.transactionId(), - new TransactionScope(transactionLog.version(), commit)); + new TransactionScope(transactionLog.version(), commit.value().transactionLog())); return PrepareResult.PARTIAL_FAILURE; } else { activeTransactions.put( @@ -513,12 +452,8 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se return PrepareResult.OK; } } catch (Exception e) { - log.warn("Failure applying {}", commit, e); + getLogger().warn("Failure applying {}", commit, e); throw Throwables.propagate(e); - } finally { - if (!ok) { - commit.close(); - } } } @@ -529,7 +464,7 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @return commit result */ protected CommitResult commit(Commit commit) { - TransactionId transactionId = commit.operation().transactionId(); + TransactionId transactionId = commit.value().transactionId(); TransactionScope transactionScope = activeTransactions.remove(transactionId); if (transactionScope == null) { return CommitResult.UNKNOWN_TRANSACTION_ID; @@ -537,9 +472,9 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se try { this.currentVersion = commit.index(); - return commit(transactionScope.committed(commit)); + return commitTransaction(transactionScope); } catch (Exception e) { - log.warn("Failure applying {}", commit, e); + getLogger().warn("Failure applying {}", commit, e); throw Throwables.propagate(e); } finally { discardTombstones(); @@ -549,23 +484,10 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se /** * Applies committed operations to the state machine. */ - private CommitResult commit(TransactionScope transactionScope) { + private CommitResult commitTransaction(TransactionScope transactionScope) { TransactionLog> transactionLog = transactionScope.transactionLog(); boolean retainTombstones = !activeTransactions.isEmpty(); - // Count the total number of keys that will be set by this transaction. This is necessary to do reference - // counting for garbage collection. - long totalReferencesToCommit = transactionLog.records().stream() - // No keys are set for version checks. For deletes, references are only retained of tombstones - // need to be retained for concurrent transactions. - .filter(record -> record.type() != MapUpdate.Type.VERSION_MATCH && record.type() != MapUpdate.Type.LOCK - && (record.type() != MapUpdate.Type.REMOVE_IF_VERSION_MATCH || retainTombstones)) - .count(); - - // Create a count down completer that counts references to the transaction commit for garbage collection. - CountDownCompleter completer = new CountDownCompleter<>( - transactionScope, totalReferencesToCommit, TransactionScope::close); - List> eventsToPublish = Lists.newArrayList(); for (MapUpdate record : transactionLog.records()) { if (record.type() == MapUpdate.Type.VERSION_MATCH) { @@ -584,10 +506,10 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se // If the record is not a delete, create a transactional commit. if (record.type() != MapUpdate.Type.REMOVE_IF_VERSION_MATCH) { - newValue = new TransactionalCommit(currentVersion, record.value(), completer); + newValue = new MapEntryValue(MapEntryValue.Type.VALUE, currentVersion, record.value()); } else if (retainTombstones) { // For deletes, if tombstones need to be retained then create and store a tombstone commit. - newValue = new TombstoneCommit(currentVersion, completer); + newValue = new MapEntryValue(MapEntryValue.Type.TOMBSTONE, currentVersion, null); } eventsToPublish.add(new MapEvent<>("", key, toVersioned(newValue), toVersioned(previousValue))); @@ -595,10 +517,6 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se if (newValue != null) { mapEntries.put(key, newValue); } - - if (previousValue != null) { - previousValue.discard(); - } } publish(eventsToPublish); return CommitResult.OK; @@ -611,14 +529,12 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @return rollback result */ protected RollbackResult rollback(Commit commit) { - TransactionId transactionId = commit.operation().transactionId(); + TransactionId transactionId = commit.value().transactionId(); TransactionScope transactionScope = activeTransactions.remove(transactionId); if (transactionScope == null) { return RollbackResult.UNKNOWN_TRANSACTION_ID; } else if (!transactionScope.isPrepared()) { discardTombstones(); - transactionScope.close(); - commit.close(); return RollbackResult.OK; } else { try { @@ -631,8 +547,6 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se return RollbackResult.OK; } finally { discardTombstones(); - transactionScope.close(); - commit.close(); } } @@ -648,7 +562,6 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se MapEntryValue value = iterator.next().getValue(); if (value.type() == MapEntryValue.Type.TOMBSTONE) { iterator.remove(); - value.discard(); } } } else { @@ -660,7 +573,6 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se MapEntryValue value = iterator.next().getValue(); if (value.type() == MapEntryValue.Type.TOMBSTONE && value.version < lowWaterMark) { iterator.remove(); - value.discard(); } } } @@ -705,45 +617,37 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @param events list of map event to publish */ private void publish(List> events) { - listeners.values().forEach(commit -> commit.session().publish(AtomixConsistentMap.CHANGE_SUBJECT, events)); + listeners.values().forEach(session -> { + session.publish(CHANGE, SERIALIZER::encode, events); + }); } @Override - public void register(ServerSession session) { + public void onExpire(RaftSession session) { + closeListener(session.sessionId().id()); } @Override - public void unregister(ServerSession session) { - closeListener(session.id()); - } - - @Override - public void expire(ServerSession session) { - closeListener(session.id()); - } - - @Override - public void close(ServerSession session) { - closeListener(session.id()); + public void onClose(RaftSession session) { + closeListener(session.sessionId().id()); } private void closeListener(Long sessionId) { - Commit commit = listeners.remove(sessionId); - if (commit != null) { - commit.close(); - } + listeners.remove(sessionId); } /** * Interface implemented by map values. */ - private abstract static class MapEntryValue { + private static class MapEntryValue { protected final Type type; protected final long version; + protected final byte[] value; - MapEntryValue(Type type, long version) { + MapEntryValue(Type type, long version, byte[] value) { this.type = type; this.version = version; + this.value = value; } /** @@ -769,12 +673,9 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * * @return raw value */ - abstract byte[] value(); - - /** - * Discards the value by invoke appropriate clean up actions. - */ - abstract void discard(); + byte[] value() { + return value; + } /** * Value type. @@ -785,101 +686,20 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se } } - /** - * A {@code MapEntryValue} that is derived from a non-transactional update - * i.e. via any standard map update operation. - */ - private static class NonTransactionalCommit extends MapEntryValue { - private final Commit commit; - - NonTransactionalCommit(Commit commit) { - super(Type.VALUE, commit.index()); - this.commit = commit; - } - - @Override - byte[] value() { - return commit.operation().value(); - } - - @Override - void discard() { - commit.close(); - } - } - - /** - * A {@code MapEntryValue} that is derived from updates submitted via a - * transaction. - */ - private static class TransactionalCommit extends MapEntryValue { - private final byte[] value; - private final CountDownCompleter completer; - - TransactionalCommit(long version, byte[] value, CountDownCompleter completer) { - super(Type.VALUE, version); - this.value = value; - this.completer = completer; - } - - @Override - byte[] value() { - return value; - } - - @Override - void discard() { - completer.countDown(); - } - } - - /** - * A {@code MapEntryValue} that represents a deleted entry. - */ - private static class TombstoneCommit extends MapEntryValue { - private final CountDownCompleter completer; - - public TombstoneCommit(long version, CountDownCompleter completer) { - super(Type.TOMBSTONE, version); - this.completer = completer; - } - - @Override - byte[] value() { - throw new UnsupportedOperationException(); - } - - @Override - void discard() { - completer.countDown(); - } - } - /** * Map transaction scope. */ private static final class TransactionScope { private final long version; - private final Commit prepareCommit; - private final Commit commitCommit; + private final TransactionLog> transactionLog; private TransactionScope(long version) { - this(version, null, null); + this(version, null); } - private TransactionScope( - long version, - Commit prepareCommit) { - this(version, prepareCommit, null); - } - - private TransactionScope( - long version, - Commit prepareCommit, - Commit commitCommit) { + private TransactionScope(long version, TransactionLog> transactionLog) { this.version = version; - this.prepareCommit = prepareCommit; - this.commitCommit = commitCommit; + this.transactionLog = transactionLog; } /** @@ -897,7 +717,7 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @return whether this is a prepared transaction scope */ boolean isPrepared() { - return prepareCommit != null; + return transactionLog != null; } /** @@ -907,7 +727,7 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se */ TransactionLog> transactionLog() { checkState(isPrepared()); - return prepareCommit.operation().transactionLog(); + return transactionLog; } /** @@ -917,30 +737,7 @@ public class AtomixConsistentMapState extends ResourceStateMachine implements Se * @return new transaction scope updated with the prepare commit */ TransactionScope prepared(Commit commit) { - return new TransactionScope(version, commit); - } - - /** - * Returns a new transaction scope with a commit commit. - * - * @param commit the commit commit ;-) - * @return new transaction scope updated with the commit commit - */ - TransactionScope committed(Commit commit) { - checkState(isPrepared()); - return new TransactionScope(version, prepareCommit, commit); - } - - /** - * Closes the transaction and all associated commits. - */ - void close() { - if (prepareCommit != null) { - prepareCommit.close(); - } - if (commitCommit != null) { - commitCommit.close(); - } + return new TransactionScope(version, commit.value().transactionLog()); } } -} +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMultimapCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMultimapCommands.java deleted file mode 100644 index b8e5d4c357..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMultimapCommands.java +++ /dev/null @@ -1,629 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.onosproject.store.primitives.resources.impl; - -import com.google.common.base.MoreObjects; -import com.google.common.collect.Multiset; -import io.atomix.catalyst.buffer.BufferInput; -import io.atomix.catalyst.buffer.BufferOutput; -import io.atomix.catalyst.serializer.CatalystSerializable; -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.catalyst.serializer.Serializer; -import io.atomix.catalyst.serializer.SerializerRegistry; -import io.atomix.catalyst.util.Assert; -import io.atomix.copycat.Command; -import io.atomix.copycat.Query; -import org.onlab.util.Match; -import org.onosproject.store.service.Versioned; - -import java.util.Collection; -import java.util.Map; -import java.util.Set; - -/** - * AsyncConsistentMultimap state machine commands. - */ -public final class AtomixConsistentMultimapCommands { - - private AtomixConsistentMultimapCommands() { - } - - /** - * Abstract multimap command. - */ - @SuppressWarnings("serial") - public abstract static class MultimapCommand implements Command, - CatalystSerializable { - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - } - - /** - * Abstract multimap query. - */ - @SuppressWarnings("serial") - public abstract static class MultimapQuery implements Query, - CatalystSerializable { - @Override - public ConsistencyLevel consistency() { - return ConsistencyLevel.SEQUENTIAL; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, - Serializer serializer) { - } - } - - /** - * Abstract key-based multimap query. - */ - @SuppressWarnings("serial") - public abstract static class KeyQuery extends MultimapQuery { - protected String key; - - public KeyQuery() { - } - - public KeyQuery(String key) { - this.key = Assert.notNull(key, "key"); - } - - public String key() { - return key; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("key", key) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(key, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - key = serializer.readObject(buffer); - } - } - - /** - * Abstract value-based query. - */ - @SuppressWarnings("serial") - public abstract static class ValueQuery extends MultimapQuery { - protected byte[] value; - - public ValueQuery() { - } - - public ValueQuery(byte[] value) { - this.value = Assert.notNull(value, "value"); - } - - /** - * Returns the value. - * - * @return value. - */ - public byte[] value() { - return value; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("value", value) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(value, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - value = serializer.readObject(buffer); - } - } - - /** - * Size query. - */ - public static class Size extends MultimapQuery { - } - - /** - * Is empty query. - */ - public static class IsEmpty extends MultimapQuery { - } - - /** - * Contains key query. - */ - @SuppressWarnings("serial") - public static class ContainsKey extends KeyQuery { - public ContainsKey() { - } - - public ContainsKey(String key) { - super(key); - } - - } - - /** - * Contains value query. - */ - @SuppressWarnings("serial") - public static class ContainsValue extends ValueQuery { - public ContainsValue() { - } - - public ContainsValue(byte[] value) { - super(value); - } - } - - /** - * Contains entry query. - */ - @SuppressWarnings("serial") - public static class ContainsEntry extends MultimapQuery { - protected String key; - protected byte[] value; - - public ContainsEntry() { - } - - public ContainsEntry(String key, byte[] value) { - this.key = Assert.notNull(key, "key"); - this.value = Assert.notNull(value, "value"); - } - - public String key() { - return key; - } - - public byte[] value() { - return value; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("key", key) - .add("value", value) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(key, buffer); - serializer.writeObject(value, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - key = serializer.readObject(buffer); - value = serializer.readObject(buffer); - - } - } - - /** - * Remove command, backs remove and removeAll's that return booleans. - */ - @SuppressWarnings("serial") - public static class RemoveAll extends - MultimapCommand>> { - private String key; - private Match versionMatch; - - public RemoveAll() { - } - - public RemoveAll(String key, Match versionMatch) { - this.key = Assert.notNull(key, "key"); - this.versionMatch = versionMatch; - } - - public String key() { - return this.key; - } - - public Match versionMatch() { - return versionMatch; - } - - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(key, buffer); - serializer.writeObject(versionMatch, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - key = serializer.readObject(buffer); - versionMatch = serializer.readObject(buffer); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("key", key) - .add("versionMatch", versionMatch) - .toString(); - } - } - - /** - * Remove command, backs remove and removeAll's that return booleans. - */ - @SuppressWarnings("serial") - public static class MultiRemove extends - MultimapCommand { - private String key; - private Collection values; - private Match versionMatch; - - public MultiRemove() { - } - - public MultiRemove(String key, Collection valueMatches, - Match versionMatch) { - this.key = Assert.notNull(key, "key"); - this.values = valueMatches; - this.versionMatch = versionMatch; - } - - public String key() { - return this.key; - } - - public Collection values() { - return values; - } - - public Match versionMatch() { - return versionMatch; - } - - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(key, buffer); - serializer.writeObject(values, buffer); - serializer.writeObject(versionMatch, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - key = serializer.readObject(buffer); - values = serializer.readObject(buffer); - versionMatch = serializer.readObject(buffer); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("key", key) - .add("values", values) - .add("versionMatch", versionMatch) - .toString(); - } - } - - /** - * Command to back the put and putAll methods. - */ - @SuppressWarnings("serial") - public static class Put extends MultimapCommand { - private String key; - private Collection values; - private Match versionMatch; - - public Put() { - } - - public Put(String key, Collection values, - Match versionMatch) { - this.key = Assert.notNull(key, "key"); - this.values = values; - this.versionMatch = versionMatch; - } - - public String key() { - return key; - } - - public Collection values() { - return values; - } - - public Match versionMatch() { - return versionMatch; - } - - @Override - public CompactionMode compaction() { - return CompactionMode.QUORUM; - } - - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(key, buffer); - serializer.writeObject(values, buffer); - serializer.writeObject(versionMatch, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - key = serializer.readObject(buffer); - values = serializer.readObject(buffer); - versionMatch = serializer.readObject(buffer); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("key", key) - .add("values", values) - .add("versionMatch", versionMatch) - .toString(); - } - } - - /** - * Replace command, returns the collection that was replaced. - */ - @SuppressWarnings("serial") - public static class Replace extends - MultimapCommand>> { - private String key; - private Collection values; - private Match versionMatch; - - public Replace() { - } - - public Replace(String key, Collection values, - Match versionMatch) { - this.key = Assert.notNull(key, "key"); - this.values = values; - this.versionMatch = versionMatch; - } - - public String key() { - return this.key; - } - - public Match versionMatch() { - return versionMatch; - } - - public Collection values() { - return values; - } - - @Override - public CompactionMode compaction() { - return CompactionMode.QUORUM; - } - - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(key, buffer); - serializer.writeObject(values, buffer); - serializer.writeObject(versionMatch, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - key = serializer.readObject(buffer); - values = serializer.readObject(buffer); - versionMatch = serializer.readObject(buffer); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("key", key) - .add("values", values) - .add("versionMatch", versionMatch) - .toString(); - } - } - - /** - * Clear multimap command. - */ - @SuppressWarnings("serial") - public static class Clear extends MultimapCommand { - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - } - - /** - * Key set query. - */ - @SuppressWarnings("serial") - public static class KeySet extends MultimapQuery> { - } - - /** - * Key multiset query. - */ - @SuppressWarnings("serial") - public static class Keys extends MultimapQuery> { - } - - /** - * Value collection query. - */ - @SuppressWarnings("serial") - public static class Values extends MultimapQuery> { - } - - /** - * Entry set query. - */ - @SuppressWarnings("serial") - public static class Entries extends - MultimapQuery>> { - } - - /** - * Get value query. - */ - public static class Get extends - KeyQuery>> { - public Get() { - } - - public Get(String key) { - super(key); - } - } - - /** - * Change listen. - */ - @SuppressWarnings("serial") - public static class Listen implements Command, CatalystSerializable { - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - - @Override - public CompactionMode compaction() { - return CompactionMode.QUORUM; - } - } - - /** - * Change unlisten. - */ - @SuppressWarnings("serial") - public static class Unlisten implements Command, CatalystSerializable { - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - } - - /** - * Multimap command type resolver. - */ - @SuppressWarnings("serial") - public static class TypeResolver implements SerializableTypeResolver { - @Override - public void resolve(SerializerRegistry registry) { - registry.register(ContainsKey.class, -1000); - registry.register(ContainsValue.class, -1001); - registry.register(ContainsEntry.class, -1002); - registry.register(Replace.class, -1003); - registry.register(Clear.class, -1004); - registry.register(KeySet.class, -1005); - registry.register(Keys.class, -1006); - registry.register(Values.class, -1007); - registry.register(Entries.class, -1008); - registry.register(Size.class, -1009); - registry.register(IsEmpty.class, -1010); - registry.register(Get.class, -1011); - registry.register(Put.class, -1012); - registry.register(RemoveAll.class, -1013); - registry.register(MultiRemove.class, -1014); - registry.register(Listen.class, -1015); - registry.register(Unlisten.class, -1016); - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimap.java index 22a034509e..a9ea68bc83 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimap.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimap.java @@ -16,43 +16,52 @@ package org.onosproject.store.primitives.resources.impl; -import com.google.common.collect.Lists; -import com.google.common.collect.Multiset; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.AbstractResource; -import io.atomix.resource.ResourceTypeInfo; -import org.onosproject.store.service.AsyncConsistentMultimap; -import org.onosproject.store.service.MultimapEvent; -import org.onosproject.store.service.MultimapEventListener; -import org.onosproject.store.service.Versioned; - import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Clear; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.ContainsEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.ContainsKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.ContainsValue; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Entries; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Get; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.IsEmpty; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.KeySet; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Keys; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Listen; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.MultiRemove; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Put; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.RemoveAll; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Replace; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Size; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Unlisten; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Values; +import com.google.common.collect.Lists; +import com.google.common.collect.Multiset; +import io.atomix.protocols.raft.proxy.RaftProxy; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.AsyncConsistentMultimap; +import org.onosproject.store.service.MultimapEvent; +import org.onosproject.store.service.MultimapEventListener; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.Versioned; + +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapEvents.CHANGE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ADD_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CLEAR; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CONTAINS_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CONTAINS_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CONTAINS_VALUE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ContainsEntry; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ContainsKey; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ContainsValue; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ENTRIES; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.Get; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.IS_EMPTY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.KEYS; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.KEY_SET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.MultiRemove; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.PUT; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.Put; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REMOVE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REMOVE_ALL; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REMOVE_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REPLACE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.RemoveAll; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.Replace; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.SIZE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.VALUES; /** @@ -60,30 +69,25 @@ import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMu *

* Note: this implementation does not allow null entries or duplicate entries. */ -@ResourceTypeInfo(id = -153, factory = AtomixConsistentSetMultimapFactory.class) public class AtomixConsistentSetMultimap - extends AbstractResource + extends AbstractRaftPrimitive implements AsyncConsistentMultimap { + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixConsistentSetMultimapOperations.NAMESPACE) + .register(AtomixConsistentSetMultimapEvents.NAMESPACE) + .build()); + private final Map, Executor> mapEventListeners = new ConcurrentHashMap<>(); - public static final String CHANGE_SUBJECT = "multimapChangeEvents"; - - public AtomixConsistentSetMultimap(CopycatClient client, - Properties properties) { - super(client, properties); - } - - @Override - public CompletableFuture open() { - return super.open().thenApply(result -> { - client.onStateChange(state -> { - if (state == CopycatClient.State.CONNECTED && isListening()) { - client.submit(new Listen()); - } - }); - client.onEvent(CHANGE_SUBJECT, this::handleEvent); - return result; + public AtomixConsistentSetMultimap(RaftProxy proxy) { + super(proxy); + proxy.addEventListener(CHANGE, SERIALIZER::decode, this::handleEvent); + proxy.addStateChangeListener(state -> { + if (state == RaftProxy.State.CONNECTED && isListening()) { + proxy.invoke(ADD_LISTENER); + } }); } @@ -94,97 +98,109 @@ public class AtomixConsistentSetMultimap @Override public CompletableFuture size() { - return client.submit(new Size()); + return proxy.invoke(SIZE, SERIALIZER::decode); } @Override public CompletableFuture isEmpty() { - return client.submit(new IsEmpty()); + return proxy.invoke(IS_EMPTY, SERIALIZER::decode); } @Override public CompletableFuture containsKey(String key) { - return client.submit(new ContainsKey(key)); + return proxy.invoke(CONTAINS_KEY, SERIALIZER::encode, new ContainsKey(key), SERIALIZER::decode); } @Override public CompletableFuture containsValue(byte[] value) { - return client.submit(new ContainsValue(value)); + return proxy.invoke(CONTAINS_VALUE, SERIALIZER::encode, new ContainsValue(value), SERIALIZER::decode); } @Override public CompletableFuture containsEntry(String key, byte[] value) { - return client.submit(new ContainsEntry(key, value)); + return proxy.invoke(CONTAINS_ENTRY, SERIALIZER::encode, new ContainsEntry(key, value), SERIALIZER::decode); } @Override public CompletableFuture put(String key, byte[] value) { - return client.submit(new Put(key, Lists.newArrayList(value), null)); + return proxy.invoke( + PUT, + SERIALIZER::encode, + new Put(key, Lists.newArrayList(value), null), + SERIALIZER::decode); } @Override public CompletableFuture remove(String key, byte[] value) { - return client.submit(new MultiRemove(key, - Lists.newArrayList(value), - null)); + return proxy.invoke(REMOVE, SERIALIZER::encode, new MultiRemove(key, + Lists.newArrayList(value), + null), SERIALIZER::decode); } @Override public CompletableFuture removeAll(String key, Collection values) { - return client.submit(new MultiRemove(key, (Collection) values, null)); + return proxy.invoke( + REMOVE, + SERIALIZER::encode, + new MultiRemove(key, (Collection) values, null), + SERIALIZER::decode); } @Override public CompletableFuture>> removeAll(String key) { - return client.submit(new RemoveAll(key, null)); + return proxy.invoke(REMOVE_ALL, SERIALIZER::encode, new RemoveAll(key, null), SERIALIZER::decode); } @Override public CompletableFuture putAll( String key, Collection values) { - return client.submit(new Put(key, values, null)); + return proxy.invoke(PUT, SERIALIZER::encode, new Put(key, values, null), SERIALIZER::decode); } @Override public CompletableFuture>> replaceValues( String key, Collection values) { - return client.submit(new Replace(key, values, null)); + return proxy.invoke( + REPLACE, + SERIALIZER::encode, + new Replace(key, values, null), + SERIALIZER::decode); } @Override public CompletableFuture clear() { - return client.submit(new Clear()); + return proxy.invoke(CLEAR); } @Override public CompletableFuture>> get(String key) { - return client.submit(new Get(key)); + return proxy.invoke(GET, SERIALIZER::encode, new Get(key), SERIALIZER::decode); } @Override public CompletableFuture> keySet() { - return client.submit(new KeySet()); + return proxy.invoke(KEY_SET, SERIALIZER::decode); } @Override public CompletableFuture> keys() { - return client.submit(new Keys()); + return proxy.invoke(KEYS, SERIALIZER::decode); } @Override public CompletableFuture> values() { - return client.submit(new Values()); + return proxy.invoke(VALUES, SERIALIZER::decode); } @Override public CompletableFuture>> entries() { - return client.submit(new Entries()); + return proxy.invoke(ENTRIES, SERIALIZER::decode); } @Override public CompletableFuture addListener(MultimapEventListener listener, Executor executor) { if (mapEventListeners.isEmpty()) { - return client.submit(new Listen()).thenRun(() -> mapEventListeners.put(listener, executor)); + return proxy.invoke(ADD_LISTENER).thenRun(() -> mapEventListeners.put(listener, executor)); } else { mapEventListeners.put(listener, executor); return CompletableFuture.completedFuture(null); @@ -194,7 +210,7 @@ public class AtomixConsistentSetMultimap @Override public CompletableFuture removeListener(MultimapEventListener listener) { if (mapEventListeners.remove(listener) != null && mapEventListeners.isEmpty()) { - return client.submit(new Unlisten()).thenApply(v -> null); + return proxy.invoke(REMOVE_LISTENER).thenApply(v -> null); } return CompletableFuture.completedFuture(null); } @@ -204,11 +220,6 @@ public class AtomixConsistentSetMultimap throw new UnsupportedOperationException("Expensive operation."); } - @Override - public String name() { - return null; - } - /** * Helper to check if there was a lock based issue. * @param status the status of an update result @@ -216,12 +227,12 @@ public class AtomixConsistentSetMultimap private void throwIfLocked(MapEntryUpdateResult.Status status) { if (status == MapEntryUpdateResult.Status.WRITE_LOCK) { throw new ConcurrentModificationException("Cannot update map: " + - "Another transaction " + - "in progress"); + "Another transaction " + + "in progress"); } } private boolean isListening() { return !mapEventListeners.isEmpty(); } -} +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapEvents.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapEvents.java new file mode 100644 index 0000000000..6c71915f1f --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapEvents.java @@ -0,0 +1,46 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.event.EventType; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.MultimapEvent; + +/** + * Atomix consistent set multimap events. + */ +public enum AtomixConsistentSetMultimapEvents implements EventType { + CHANGE("change"); + + private final String id; + + AtomixConsistentSetMultimapEvents(String id) { + this.id = id; + } + + @Override + public String id() { + return id; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 50) + .register(MultimapEvent.class) + .register(MultimapEvent.Type.class) + .register(byte[].class) + .build("AtomixConsistentSetMultimapEvents"); +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapFactory.java deleted file mode 100644 index e6ebbda971..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapFactory.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.onosproject.store.primitives.resources.impl; - -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.ResourceFactory; -import io.atomix.resource.ResourceStateMachine; - -import java.util.Properties; - -/** - * {@link AtomixConsistentSetMultimap} resource factory. - */ -public class AtomixConsistentSetMultimapFactory implements - ResourceFactory { - @Override - public SerializableTypeResolver createSerializableTypeResolver() { - return new AtomixConsistentMultimapCommands.TypeResolver(); - } - - @Override - public ResourceStateMachine createStateMachine(Properties config) { - return new AtomixConsistentSetMultimapState(config); - } - - @Override - public AtomixConsistentSetMultimap createInstance(CopycatClient client, - Properties properties) { - return new AtomixConsistentSetMultimap(client, properties); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapOperations.java new file mode 100644 index 0000000000..ddbbc5be55 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapOperations.java @@ -0,0 +1,384 @@ +/* + * Copyright 2016-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.store.primitives.resources.impl; + +import java.util.ArrayList; +import java.util.Collection; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.Maps; +import io.atomix.protocols.raft.operation.OperationId; +import io.atomix.protocols.raft.operation.OperationType; +import org.onlab.util.KryoNamespace; +import org.onlab.util.Match; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.Versioned; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * AsyncConsistentMultimap state machine commands. + */ +public enum AtomixConsistentSetMultimapOperations implements OperationId { + GET("get", OperationType.QUERY), + SIZE("size", OperationType.QUERY), + IS_EMPTY("isEmpty", OperationType.QUERY), + CONTAINS_KEY("containsKey", OperationType.QUERY), + CONTAINS_VALUE("containsValue", OperationType.QUERY), + CONTAINS_ENTRY("containsEntry", OperationType.QUERY), + KEY_SET("keySet", OperationType.QUERY), + KEYS("keys", OperationType.QUERY), + VALUES("values", OperationType.QUERY), + ENTRIES("entries", OperationType.QUERY), + PUT("put", OperationType.COMMAND), + REMOVE("remove", OperationType.COMMAND), + REMOVE_ALL("removeAll", OperationType.COMMAND), + REPLACE("replace", OperationType.COMMAND), + CLEAR("clear", OperationType.COMMAND), + ADD_LISTENER("addListener", OperationType.COMMAND), + REMOVE_LISTENER("removeListener", OperationType.COMMAND); + + private final String id; + private final OperationType type; + + AtomixConsistentSetMultimapOperations(String id, OperationType type) { + this.id = id; + this.type = type; + } + + @Override + public String id() { + return id; + } + + @Override + public OperationType type() { + return type; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID) + .register(ContainsEntry.class) + .register(ContainsKey.class) + .register(ContainsValue.class) + .register(Get.class) + .register(MultiRemove.class) + .register(Put.class) + .register(RemoveAll.class) + .register(Replace.class) + .register(Match.class) + .register(Versioned.class) + .register(ArrayList.class) + .register(Maps.immutableEntry("", "").getClass()) + .build("AtomixConsistentSetMultimapOperations"); + + /** + * Abstract multimap command. + */ + @SuppressWarnings("serial") + public abstract static class MultimapOperation { + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .toString(); + } + } + + /** + * Abstract key-based multimap query. + */ + @SuppressWarnings("serial") + public abstract static class KeyOperation extends MultimapOperation { + protected String key; + + public KeyOperation() { + } + + public KeyOperation(String key) { + this.key = checkNotNull(key); + } + + public String key() { + return key; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("key", key) + .toString(); + } + } + + /** + * Abstract value-based query. + */ + @SuppressWarnings("serial") + public abstract static class ValueOperation extends MultimapOperation { + protected byte[] value; + + public ValueOperation() { + } + + public ValueOperation(byte[] value) { + this.value = checkNotNull(value); + } + + /** + * Returns the value. + * + * @return value. + */ + public byte[] value() { + return value; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("value", value) + .toString(); + } + } + + /** + * Contains key query. + */ + @SuppressWarnings("serial") + public static class ContainsKey extends KeyOperation { + public ContainsKey() { + } + + public ContainsKey(String key) { + super(key); + } + } + + /** + * Contains value query. + */ + @SuppressWarnings("serial") + public static class ContainsValue extends ValueOperation { + public ContainsValue() { + } + + public ContainsValue(byte[] value) { + super(value); + } + } + + /** + * Contains entry query. + */ + @SuppressWarnings("serial") + public static class ContainsEntry extends MultimapOperation { + protected String key; + protected byte[] value; + + public ContainsEntry() { + } + + public ContainsEntry(String key, byte[] value) { + this.key = checkNotNull(key); + this.value = checkNotNull(value); + } + + public String key() { + return key; + } + + public byte[] value() { + return value; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("key", key) + .add("value", value) + .toString(); + } + } + + /** + * Remove command, backs remove and removeAll's that return booleans. + */ + @SuppressWarnings("serial") + public static class RemoveAll extends MultimapOperation { + private String key; + private Match versionMatch; + + public RemoveAll() { + } + + public RemoveAll(String key, Match versionMatch) { + this.key = checkNotNull(key); + this.versionMatch = versionMatch; + } + + public String key() { + return this.key; + } + + public Match versionMatch() { + return versionMatch; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("key", key) + .add("versionMatch", versionMatch) + .toString(); + } + } + + /** + * Remove command, backs remove and removeAll's that return booleans. + */ + @SuppressWarnings("serial") + public static class MultiRemove extends MultimapOperation { + private String key; + private Collection values; + private Match versionMatch; + + public MultiRemove() { + } + + public MultiRemove(String key, Collection valueMatches, + Match versionMatch) { + this.key = checkNotNull(key); + this.values = valueMatches; + this.versionMatch = versionMatch; + } + + public String key() { + return this.key; + } + + public Collection values() { + return values; + } + + public Match versionMatch() { + return versionMatch; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("key", key) + .add("values", values) + .add("versionMatch", versionMatch) + .toString(); + } + } + + /** + * Command to back the put and putAll methods. + */ + @SuppressWarnings("serial") + public static class Put extends MultimapOperation { + private String key; + private Collection values; + private Match versionMatch; + + public Put() { + } + + public Put(String key, Collection values, Match versionMatch) { + this.key = checkNotNull(key); + this.values = values; + this.versionMatch = versionMatch; + } + + public String key() { + return key; + } + + public Collection values() { + return values; + } + + public Match versionMatch() { + return versionMatch; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("key", key) + .add("values", values) + .add("versionMatch", versionMatch) + .toString(); + } + } + + /** + * Replace command, returns the collection that was replaced. + */ + @SuppressWarnings("serial") + public static class Replace extends MultimapOperation { + private String key; + private Collection values; + private Match versionMatch; + + public Replace() { + } + + public Replace(String key, Collection values, + Match versionMatch) { + this.key = checkNotNull(key); + this.values = values; + this.versionMatch = versionMatch; + } + + public String key() { + return this.key; + } + + public Match versionMatch() { + return versionMatch; + } + + public Collection values() { + return values; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("key", key) + .add("values", values) + .add("versionMatch", versionMatch) + .toString(); + } + } + + /** + * Get value query. + */ + public static class Get extends KeyOperation { + public Get() { + } + + public Get(String key) { + super(key); + } + } +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapService.java similarity index 53% rename from core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapState.java rename to core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapService.java index 47972d17c1..3029bd86eb 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapState.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapService.java @@ -16,38 +16,16 @@ package org.onosproject.store.primitives.resources.impl; -import com.google.common.base.Preconditions; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.HashMultiset; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Multiset; -import com.google.common.collect.Sets; -import io.atomix.copycat.server.Commit; -import io.atomix.copycat.server.Snapshottable; -import io.atomix.copycat.server.StateMachineExecutor; -import io.atomix.copycat.server.session.ServerSession; -import io.atomix.copycat.server.session.SessionListener; -import io.atomix.copycat.server.storage.snapshot.SnapshotReader; -import io.atomix.copycat.server.storage.snapshot.SnapshotWriter; -import io.atomix.resource.ResourceStateMachine; -import org.onlab.util.CountDownCompleter; -import org.onlab.util.Match; -import org.onosproject.store.service.MultimapEvent; -import org.onosproject.store.service.Versioned; -import org.slf4j.Logger; - import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.Set; -import java.util.TreeMap; +import java.util.TreeSet; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; @@ -56,68 +34,137 @@ import java.util.function.Supplier; import java.util.stream.Collector; import java.util.stream.Collectors; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Clear; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.ContainsEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.ContainsKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.ContainsValue; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Entries; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Get; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.IsEmpty; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.KeySet; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Keys; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Listen; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.MultiRemove; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.MultimapCommand; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Put; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.RemoveAll; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Replace; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Size; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Unlisten; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMultimapCommands.Values; -import static org.slf4j.LoggerFactory.getLogger; +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.google.common.base.Preconditions; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multiset; +import com.google.common.collect.Sets; +import io.atomix.protocols.raft.service.AbstractRaftService; +import io.atomix.protocols.raft.service.Commit; +import io.atomix.protocols.raft.service.RaftServiceExecutor; +import io.atomix.protocols.raft.session.RaftSession; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import org.onlab.util.KryoNamespace; +import org.onlab.util.Match; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.MultimapEvent; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.Versioned; + +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapEvents.CHANGE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ADD_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CLEAR; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CONTAINS_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CONTAINS_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.CONTAINS_VALUE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ContainsEntry; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ContainsKey; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ContainsValue; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.ENTRIES; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.Get; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.IS_EMPTY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.KEYS; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.KEY_SET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.MultiRemove; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.MultimapOperation; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.PUT; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.Put; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REMOVE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REMOVE_ALL; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REMOVE_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.REPLACE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.RemoveAll; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.Replace; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.SIZE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.VALUES; /** * State Machine for {@link AtomixConsistentSetMultimap} resource. */ -public class AtomixConsistentSetMultimapState extends ResourceStateMachine - implements SessionListener, Snapshottable { +public class AtomixConsistentSetMultimapService extends AbstractRaftService { - private final Logger log = getLogger(getClass()); - private final AtomicLong globalVersion = new AtomicLong(1); - private final Map> listeners = new HashMap<>(); - private final Map backingMap = Maps.newHashMap(); + private final Serializer serializer = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixConsistentSetMultimapOperations.NAMESPACE) + .register(AtomixConsistentSetMultimapEvents.NAMESPACE) + .register(ByteArrayComparator.class) + .register(new HashMap().keySet().getClass()) + .register(TreeSet.class) + .register(new com.esotericsoftware.kryo.Serializer() { + @Override + public void write(Kryo kryo, Output output, NonTransactionalCommit object) { + kryo.writeClassAndObject(output, object.valueSet); + } - public AtomixConsistentSetMultimapState(Properties properties) { - super(properties); - } + @Override + @SuppressWarnings("unchecked") + public NonTransactionalCommit read(Kryo kryo, Input input, Class type) { + NonTransactionalCommit commit = new NonTransactionalCommit(); + commit.valueSet.addAll((Collection) kryo.readClassAndObject(input)); + return commit; + } + }, NonTransactionalCommit.class) + .build()); + + private AtomicLong globalVersion = new AtomicLong(1); + private Map listeners = new LinkedHashMap<>(); + private Map backingMap = Maps.newHashMap(); @Override public void snapshot(SnapshotWriter writer) { + writer.writeLong(globalVersion.get()); + writer.writeObject(Sets.newHashSet(listeners.keySet()), serializer::encode); + writer.writeObject(backingMap, serializer::encode); } @Override public void install(SnapshotReader reader) { + globalVersion = new AtomicLong(reader.readLong()); + + listeners = new LinkedHashMap<>(); + for (Long sessionId : reader.>readObject(serializer::decode)) { + listeners.put(sessionId, getSessions().getSession(sessionId)); + } + + backingMap = reader.readObject(serializer::decode); } @Override - protected void configure(StateMachineExecutor executor) { - executor.register(Size.class, this::size); - executor.register(IsEmpty.class, this::isEmpty); - executor.register(ContainsKey.class, this::containsKey); - executor.register(ContainsValue.class, this::containsValue); - executor.register(ContainsEntry.class, this::containsEntry); - executor.register(Clear.class, this::clear); - executor.register(KeySet.class, this::keySet); - executor.register(Keys.class, this::keys); - executor.register(Values.class, this::values); - executor.register(Entries.class, this::entries); - executor.register(Get.class, this::get); - executor.register(RemoveAll.class, this::removeAll); - executor.register(MultiRemove.class, this::multiRemove); - executor.register(Put.class, this::put); - executor.register(Replace.class, this::replace); - executor.register(Listen.class, this::listen); - executor.register(Unlisten.class, this::unlisten); + protected void configure(RaftServiceExecutor executor) { + executor.register(SIZE, this::size, serializer::encode); + executor.register(IS_EMPTY, this::isEmpty, serializer::encode); + executor.register(CONTAINS_KEY, serializer::decode, this::containsKey, serializer::encode); + executor.register(CONTAINS_VALUE, serializer::decode, this::containsValue, serializer::encode); + executor.register(CONTAINS_ENTRY, serializer::decode, this::containsEntry, serializer::encode); + executor.register(CLEAR, this::clear); + executor.register(KEY_SET, this::keySet, serializer::encode); + executor.register(KEYS, this::keys, serializer::encode); + executor.register(VALUES, this::values, serializer::encode); + executor.register(ENTRIES, this::entries, serializer::encode); + executor.register(GET, serializer::decode, this::get, serializer::encode); + executor.register(REMOVE_ALL, serializer::decode, this::removeAll, serializer::encode); + executor.register(REMOVE, serializer::decode, this::multiRemove, serializer::encode); + executor.register(PUT, serializer::decode, this::put, serializer::encode); + executor.register(REPLACE, serializer::decode, this::replace, serializer::encode); + executor.register(ADD_LISTENER, this::listen); + executor.register(REMOVE_LISTENER, this::unlisten); + } + + @Override + public void onExpire(RaftSession session) { + listeners.remove(session.sessionId().id()); + } + + @Override + public void onClose(RaftSession session) { + listeners.remove(session.sessionId().id()); } /** @@ -126,15 +173,11 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @param commit Size commit * @return number of unique key value pairs in the multimap */ - protected int size(Commit commit) { - try { - return backingMap.values() - .stream() - .map(valueCollection -> valueCollection.values().size()) - .collect(Collectors.summingInt(size -> size)); - } finally { - commit.close(); - } + protected int size(Commit commit) { + return backingMap.values() + .stream() + .map(valueCollection -> valueCollection.values().size()) + .collect(Collectors.summingInt(size -> size)); } /** @@ -143,12 +186,8 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @param commit IsEmpty commit * @return true if the multimap contains no key-value pairs, else false */ - protected boolean isEmpty(Commit commit) { - try { - return backingMap.isEmpty(); - } finally { - commit.close(); - } + protected boolean isEmpty(Commit commit) { + return backingMap.isEmpty(); } /** @@ -158,11 +197,7 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @return returns true if the key is in the multimap, else false */ protected boolean containsKey(Commit commit) { - try { - return backingMap.containsKey(commit.operation().key()); - } finally { - commit.close(); - } + return backingMap.containsKey(commit.value().key()); } /** @@ -172,23 +207,19 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @return true if the value is in the multimap, else false */ protected boolean containsValue(Commit commit) { - try { - if (backingMap.values().isEmpty()) { - return false; - } - Match match = Match.ifValue(commit.operation().value()); - return backingMap - .values() - .stream() - .anyMatch(valueList -> - valueList - .values() - .stream() - .anyMatch(byteValue -> - match.matches(byteValue))); - } finally { - commit.close(); + if (backingMap.values().isEmpty()) { + return false; } + Match match = Match.ifValue(commit.value().value()); + return backingMap + .values() + .stream() + .anyMatch(valueList -> + valueList + .values() + .stream() + .anyMatch(byteValue -> + match.matches(byteValue))); } /** @@ -198,20 +229,16 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @return true if the key-value pair exists, else false */ protected boolean containsEntry(Commit commit) { - try { - MapEntryValue entryValue = - backingMap.get(commit.operation().key()); - if (entryValue == null) { - return false; - } else { - Match valueMatch = Match.ifValue(commit.operation().value()); - return entryValue - .values() - .stream() - .anyMatch(byteValue -> valueMatch.matches(byteValue)); - } - } finally { - commit.close(); + MapEntryValue entryValue = + backingMap.get(commit.value().key()); + if (entryValue == null) { + return false; + } else { + Match valueMatch = Match.ifValue(commit.value().value()); + return entryValue + .values() + .stream() + .anyMatch(byteValue -> valueMatch.matches(byteValue)); } } @@ -220,12 +247,8 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * * @param commit Clear commit */ - protected void clear(Commit commit) { - try { - backingMap.clear(); - } finally { - commit.close(); - } + protected void clear(Commit commit) { + backingMap.clear(); } /** @@ -234,12 +257,8 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @param commit KeySet commit * @return a set of all keys in the multimap */ - protected Set keySet(Commit commit) { - try { - return ImmutableSet.copyOf(backingMap.keySet()); - } finally { - commit.close(); - } + protected Set keySet(Commit commit) { + return ImmutableSet.copyOf(backingMap.keySet()); } /** @@ -249,16 +268,12 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @return a multiset of keys with each key included an equal number of * times to the total key-value pairs in which that key participates */ - protected Multiset keys(Commit commit) { - try { - Multiset keys = HashMultiset.create(); - backingMap.forEach((key, mapEntryValue) -> { - keys.add(key, mapEntryValue.values().size()); - }); - return keys; - } finally { - commit.close(); - } + protected Multiset keys(Commit commit) { + Multiset keys = HashMultiset.create(); + backingMap.forEach((key, mapEntryValue) -> { + keys.add(key, mapEntryValue.values().size()); + }); + return keys; } /** @@ -267,15 +282,11 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @param commit Values commit * @return the set of values in the multimap with duplicates included */ - protected Multiset values(Commit commit) { - try { - return backingMap - .values() - .stream() - .collect(new HashMultisetValueCollector()); - } finally { - commit.close(); - } + protected Multiset values(Commit commit) { + return backingMap + .values() + .stream() + .collect(new HashMultisetValueCollector()); } /** @@ -284,16 +295,11 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @param commit Entries commit * @return a set of all key-value pairs in the multimap */ - protected Collection> entries( - Commit commit) { - try { - return backingMap - .entrySet() - .stream() - .collect(new EntrySetCollector()); - } finally { - commit.close(); - } + protected Collection> entries(Commit commit) { + return backingMap + .entrySet() + .stream() + .collect(new EntrySetCollector()); } /** @@ -303,14 +309,8 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @return the collection of values associated with the key or an empty * list if none exist */ - protected Versioned> get( - Commit commit) { - try { - MapEntryValue mapEntryValue = backingMap.get(commit.operation().key()); - return toVersioned(backingMap.get(commit.operation().key())); - } finally { - commit.close(); - } + protected Versioned> get(Commit commit) { + return toVersioned(backingMap.get(commit.value().key())); } /** @@ -319,12 +319,10 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @param commit removeAll commit * @return collection of removed values */ - protected Versioned> removeAll( - Commit commit) { - String key = commit.operation().key(); + protected Versioned> removeAll(Commit commit) { + String key = commit.value().key(); if (!backingMap.containsKey(key)) { - commit.close(); return new Versioned<>(Sets.newHashSet(), -1); } @@ -344,10 +342,9 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @return true if any change results, else false */ protected boolean multiRemove(Commit commit) { - String key = commit.operation().key(); + String key = commit.value().key(); if (!backingMap.containsKey(key)) { - commit.close(); return false; } @@ -356,6 +353,10 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine .addCommit(commit); if (removedValues != null) { + if (removedValues.value().isEmpty()) { + backingMap.remove(key); + } + publish(removedValues.value().stream() .map(value -> new MultimapEvent( "", key, null, value)) @@ -373,12 +374,12 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @return true if this commit results in a change, else false */ protected boolean put(Commit commit) { - String key = commit.operation().key(); - if (commit.operation().values().isEmpty()) { + String key = commit.value().key(); + if (commit.value().values().isEmpty()) { return false; } if (!backingMap.containsKey(key)) { - backingMap.put(key, new NonTransactionalCommit(1)); + backingMap.put(key, new NonTransactionalCommit()); } Versioned> addedValues = backingMap @@ -398,11 +399,11 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine protected Versioned> replace( Commit commit) { - if (!backingMap.containsKey(commit.operation().key())) { - backingMap.put(commit.operation().key(), - new NonTransactionalCommit(1)); + if (!backingMap.containsKey(commit.value().key())) { + backingMap.put(commit.value().key(), + new NonTransactionalCommit()); } - return backingMap.get(commit.operation().key()).addCommit(commit); + return backingMap.get(commit.value().key()).addCommit(commit); } /** @@ -410,23 +411,8 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * * @param commit listen commit */ - protected void listen(Commit commit) { - Long sessionId = commit.session().id(); - if (listeners.putIfAbsent(sessionId, commit) != null) { - commit.close(); - return; - } - commit.session() - .onStateChange( - state -> { - if (state == ServerSession.State.CLOSED - || state == ServerSession.State.EXPIRED) { - Commit listener = listeners.remove(sessionId); - if (listener != null) { - listener.close(); - } - } - }); + protected void listen(Commit commit) { + listeners.put(commit.session().sessionId().id(), commit.session()); } /** @@ -434,15 +420,8 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * * @param commit unlisten commit */ - protected void unlisten(Commit commit) { - try { - Commit listener = listeners.remove(commit.session().id()); - if (listener != null) { - listener.close(); - } - } finally { - commit.close(); - } + protected void unlisten(Commit commit) { + listeners.remove(commit.session().sessionId().id()); } /** @@ -451,8 +430,7 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @param events list of map event to publish */ private void publish(List> events) { - listeners.values().forEach(commit -> - commit.session().publish(AtomixConsistentSetMultimap.CHANGE_SUBJECT, events)); + listeners.values().forEach(session -> session.publish(CHANGE, serializer::encode, events)); } private interface MapEntryValue { @@ -471,11 +449,6 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine */ long version(); - /** - * Discards the value by invoke appropriate clean up actions. - */ - void discard(); - /** * Add a new commit and modifies the set of values accordingly. * In the case of a replace or removeAll it returns the set of removed @@ -486,24 +459,14 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine * @param commit the commit to be added */ Versioned> addCommit( - Commit commit); + Commit commit); } private class NonTransactionalCommit implements MapEntryValue { private long version; - private final TreeMap> - valueCountdownMap = Maps.newTreeMap(new ByteArrayComparator()); - /*This is a mapping of commits that added values to the commits - * removing those values, they will not be circular because keys will - * be exclusively Put and Replace commits and values will be exclusively - * Multiremove commits, each time a Put or replace is removed it should - * as part of closing go through and countdown each of the remove - * commits depending on it.*/ - private final HashMultimap> - additiveToRemovalCommits = HashMultimap.create(); + private final TreeSet valueSet = Sets.newTreeSet(new ByteArrayComparator()); - public NonTransactionalCommit( - long version) { + public NonTransactionalCommit() { //Set the version to current it will only be updated once this is // populated this.version = globalVersion.get(); @@ -511,7 +474,7 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine @Override public Collection values() { - return ImmutableSet.copyOf(valueCountdownMap.keySet()); + return ImmutableSet.copyOf(valueSet); } @Override @@ -519,156 +482,94 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine return version; } - @Override - public void discard() { - valueCountdownMap.values().forEach(completer -> - completer.object().close()); - } - @Override public Versioned> addCommit( - Commit commit) { + Commit commit) { Preconditions.checkNotNull(commit); - Preconditions.checkNotNull(commit.operation()); + Preconditions.checkNotNull(commit.value()); Versioned> retVersion; - if (commit.operation() instanceof Put) { + if (commit.value() instanceof Put) { //Using a treeset here sanitizes the input, removing duplicates Set valuesToAdd = Sets.newTreeSet(new ByteArrayComparator()); - ((Put) commit.operation()).values().forEach(value -> { - if (!valueCountdownMap.containsKey(value)) { + ((Put) commit.value()).values().forEach(value -> { + if (!valueSet.contains(value)) { valuesToAdd.add(value); } }); if (valuesToAdd.isEmpty()) { //Do not increment or add the commit if no change resulted - commit.close(); return null; } - //When all values from a commit have been removed decrement all - //removal commits relying on it and remove itself from the - //mapping of additive commits to the commits removing the - //values it added. (Only multiremoves will be dependent) - CountDownCompleter completer = - new CountDownCompleter<>(commit, valuesToAdd.size(), - c -> { - if (additiveToRemovalCommits.containsKey(c)) { - additiveToRemovalCommits. - get(c). - forEach(countdown -> - countdown.countDown()); - additiveToRemovalCommits.removeAll(c); - } - c.close(); - }); retVersion = new Versioned<>(valuesToAdd, version); - valuesToAdd.forEach(value -> valueCountdownMap.put(value, - completer)); + valuesToAdd.forEach(value -> valueSet.add(value)); version++; return retVersion; - } else if (commit.operation() instanceof Replace) { + } else if (commit.value() instanceof Replace) { //Will this work?? Need to check before check-in! Set removedValues = Sets.newHashSet(); - removedValues.addAll(valueCountdownMap.keySet()); + removedValues.addAll(valueSet); retVersion = new Versioned<>(removedValues, version); - valueCountdownMap.values().forEach(countdown -> - countdown.countDown()); - valueCountdownMap.clear(); + valueSet.clear(); Set valuesToAdd = Sets.newTreeSet(new ByteArrayComparator()); - ((Replace) commit.operation()).values().forEach(value -> { + ((Replace) commit.value()).values().forEach(value -> { valuesToAdd.add(value); }); if (valuesToAdd.isEmpty()) { version = globalVersion.incrementAndGet(); - backingMap.remove(((Replace) commit.operation()).key()); - //Order is important here, the commit must be closed last - //(or minimally after all uses) - commit.close(); + backingMap.remove(((Replace) commit.value()).key()); return retVersion; } - CountDownCompleter completer = - new CountDownCompleter<>(commit, valuesToAdd.size(), - c -> { - if (additiveToRemovalCommits - .containsKey(c)) { - additiveToRemovalCommits. - get(c). - forEach(countdown -> - countdown.countDown()); - additiveToRemovalCommits. - removeAll(c); - } - c.close(); - }); - valuesToAdd.forEach(value -> - valueCountdownMap.put(value, completer)); + valuesToAdd.forEach(value -> valueSet.add(value)); version = globalVersion.incrementAndGet(); return retVersion; - } else if (commit.operation() instanceof RemoveAll) { + } else if (commit.value() instanceof RemoveAll) { Set removed = Sets.newHashSet(); //We can assume here that values only appear once and so we //do not need to sanitize the return for duplicates. - removed.addAll(valueCountdownMap.keySet()); + removed.addAll(valueSet); retVersion = new Versioned<>(removed, version); - valueCountdownMap.values().forEach(countdown -> - countdown.countDown()); - valueCountdownMap.clear(); + valueSet.clear(); //In the case of a removeAll all commits will be removed and //unlike the multiRemove case we do not need to consider //dependencies among additive and removal commits. //Save the key for use after the commit is closed - String key = ((RemoveAll) commit.operation()).key(); - commit.close(); + String key = ((RemoveAll) commit.value()).key(); version = globalVersion.incrementAndGet(); backingMap.remove(key); return retVersion; - } else if (commit.operation() instanceof MultiRemove) { + } else if (commit.value() instanceof MultiRemove) { //Must first calculate how many commits the removal depends on. //At this time we also sanitize the removal set by adding to a //set with proper handling of byte[] equality. Set removed = Sets.newHashSet(); - Set commitsRemovedFrom = Sets.newHashSet(); - ((MultiRemove) commit.operation()).values().forEach(value -> { - if (valueCountdownMap.containsKey(value)) { + ((MultiRemove) commit.value()).values().forEach(value -> { + if (valueSet.contains(value)) { removed.add(value); - commitsRemovedFrom - .add(valueCountdownMap.get(value).object()); } }); //If there is nothing to be removed no action should be taken. if (removed.isEmpty()) { - //Do not increment or add the commit if no change resulted - commit.close(); return null; } - //When all additive commits this depends on are closed this can - //be closed as well. - CountDownCompleter completer = - new CountDownCompleter<>(commit, - commitsRemovedFrom.size(), - c -> c.close()); - commitsRemovedFrom.forEach(commitRemovedFrom -> { - additiveToRemovalCommits.put(commitRemovedFrom, completer); - }); //Save key in case countdown results in closing the commit. - String removedKey = ((MultiRemove) commit.operation()).key(); + String removedKey = ((MultiRemove) commit.value()).key(); removed.forEach(removedValue -> { - valueCountdownMap.remove(removedValue).countDown(); + valueSet.remove(removedValue); }); //The version is updated locally as well as globally even if //this object will be removed from the map in case any other //party still holds a reference to this object. retVersion = new Versioned<>(removed, version); version = globalVersion.incrementAndGet(); - if (valueCountdownMap.isEmpty()) { - backingMap - .remove(removedKey); + if (valueSet.isEmpty()) { + backingMap.remove(removedKey); } return retVersion; @@ -744,11 +645,11 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine Map.Entry> accumulator() { return (set, entry) -> { entry - .getValue() - .values() - .forEach(byteValue -> - set.add(Maps.immutableEntry(entry.getKey(), - byteValue))); + .getValue() + .values() + .forEach(byteValue -> + set.add(Maps.immutableEntry(entry.getKey(), + byteValue))); }; } @@ -781,10 +682,10 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine MapEntryValue value) { return value == null ? new Versioned<>(Lists.newArrayList(), -1) : new Versioned<>(value.values(), - value.version()); + value.version()); } - private class ByteArrayComparator implements Comparator { + private static class ByteArrayComparator implements Comparator { @Override public int compare(byte[] o1, byte[] o2) { @@ -802,4 +703,4 @@ public class AtomixConsistentSetMultimapState extends ResourceStateMachine } } } - } +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMap.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMap.java index bf462c9071..8f91b3dd1e 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMap.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMap.java @@ -16,32 +16,12 @@ package org.onosproject.store.primitives.resources.impl; -import com.google.common.collect.Maps; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.AbstractResource; -import io.atomix.resource.ResourceTypeInfo; -import org.onlab.util.Match; -import org.onosproject.store.primitives.MapUpdate; -import org.onosproject.store.primitives.TransactionId; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FirstKey; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FloorEntry; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.HigherEntry; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LastEntry; -import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LowerEntry; -import org.onosproject.store.service.AsyncConsistentTreeMap; -import org.onosproject.store.service.MapEvent; -import org.onosproject.store.service.MapEventListener; -import org.onosproject.store.service.TransactionLog; -import org.onosproject.store.service.Version; -import org.onosproject.store.service.Versioned; - import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.NavigableSet; -import java.util.Properties; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; @@ -49,114 +29,145 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiFunction; import java.util.function.Predicate; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.CeilingEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.CeilingKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Clear; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.ContainsKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.ContainsValue; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.EntrySet; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FirstEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FloorKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Get; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.GetOrDefault; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.HigherKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.IsEmpty; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.KeySet; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LastKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Listen; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LowerKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.PollFirstEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.PollLastEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Size; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Unlisten; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.UpdateAndGet; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Values; +import com.google.common.collect.Maps; +import io.atomix.protocols.raft.proxy.RaftProxy; +import org.onlab.util.KryoNamespace; +import org.onlab.util.Match; +import org.onosproject.store.primitives.MapUpdate; +import org.onosproject.store.primitives.TransactionId; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FloorEntry; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HigherEntry; +import org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LowerEntry; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.AsyncConsistentTreeMap; +import org.onosproject.store.service.MapEvent; +import org.onosproject.store.service.MapEventListener; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.TransactionLog; +import org.onosproject.store.service.Version; +import org.onosproject.store.service.Versioned; + +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapEvents.CHANGE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ADD_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CEILING_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CEILING_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CLEAR; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CONTAINS_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CONTAINS_VALUE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CeilingEntry; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CeilingKey; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ContainsKey; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ContainsValue; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ENTRY_SET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FIRST_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FIRST_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FLOOR_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FLOOR_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FloorKey; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GET_OR_DEFAULT; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.Get; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GetOrDefault; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HIGHER_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HIGHER_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HigherKey; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.IS_EMPTY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.KEY_SET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LAST_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LAST_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LOWER_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LOWER_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LowerKey; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.POLL_FIRST_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.POLL_LAST_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.REMOVE_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.SIZE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.UPDATE_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.UpdateAndGet; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.VALUES; /** * Implementation of {@link AsyncConsistentTreeMap}. */ -@ResourceTypeInfo(id = -155, factory = AtomixConsistentTreeMapFactory.class) -public class AtomixConsistentTreeMap extends AbstractResource - implements AsyncConsistentTreeMap { +public class AtomixConsistentTreeMap extends AbstractRaftPrimitive implements AsyncConsistentTreeMap { + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixConsistentTreeMapOperations.NAMESPACE) + .register(AtomixConsistentTreeMapEvents.NAMESPACE) + .build()); private final Map, Executor> mapEventListeners = Maps.newConcurrentMap(); - public static final String CHANGE_SUBJECT = "changeEvents"; - - public AtomixConsistentTreeMap(CopycatClient client, Properties options) { - super(client, options); - } - - @Override - public String name() { - return null; - } - - @Override - public CompletableFuture open() { - return super.open().thenApply(result -> { - client.onEvent(CHANGE_SUBJECT, this::handleEvent); - return result; - }); + public AtomixConsistentTreeMap(RaftProxy proxy) { + super(proxy); + proxy.addEventListener(CHANGE, SERIALIZER::decode, this::handleEvent); } private void handleEvent(List> events) { events.forEach(event -> mapEventListeners. forEach((listener, executor) -> - executor.execute(() -> - listener.event(event)))); + executor.execute(() -> + listener.event(event)))); } @Override public CompletableFuture isEmpty() { - return client.submit(new IsEmpty()); + return proxy.invoke(IS_EMPTY, SERIALIZER::decode); } @Override public CompletableFuture size() { - return client.submit(new Size()); + return proxy.invoke(SIZE, SERIALIZER::decode); } @Override public CompletableFuture containsKey(String key) { - return client.submit(new ContainsKey(key)); + return proxy.invoke(CONTAINS_KEY, SERIALIZER::encode, new ContainsKey(key), SERIALIZER::decode); } @Override public CompletableFuture containsValue(byte[] value) { - return client.submit(new ContainsValue(value)); + return proxy.invoke(CONTAINS_VALUE, SERIALIZER::encode, new ContainsValue(value), SERIALIZER::decode); } @Override public CompletableFuture> get(String key) { - return client.submit(new Get(key)); + return proxy.invoke(GET, SERIALIZER::encode, new Get(key), SERIALIZER::decode); } @Override public CompletableFuture> getOrDefault(String key, byte[] defaultValue) { - return client.submit(new GetOrDefault(key, defaultValue)); + return proxy.invoke( + GET_OR_DEFAULT, + SERIALIZER::encode, + new GetOrDefault(key, defaultValue), + SERIALIZER::decode); } @Override public CompletableFuture> keySet() { - return client.submit(new KeySet()); + return proxy.invoke(KEY_SET, SERIALIZER::decode); } @Override public CompletableFuture>> values() { - return client.submit(new Values()); + return proxy.invoke(VALUES, SERIALIZER::decode); } @Override public CompletableFuture>>> entrySet() { - return client.submit(new EntrySet()); + return proxy.invoke(ENTRY_SET, SERIALIZER::decode); } @Override @SuppressWarnings("unchecked") public CompletableFuture> put(String key, byte[] value) { - return client.submit(new UpdateAndGet(key, value, Match.ANY, Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, value, Match.ANY, Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.oldValue()); } @@ -164,7 +175,11 @@ public class AtomixConsistentTreeMap extends AbstractResource> putAndGet(String key, byte[] value) { - return client.submit(new UpdateAndGet(key, value, Match.ANY, Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, value, Match.ANY, Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.newValue()); } @@ -172,7 +187,11 @@ public class AtomixConsistentTreeMap extends AbstractResource> putIfAbsent(String key, byte[] value) { - return client.submit(new UpdateAndGet(key, value, Match.NULL, Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, value, Match.NULL, Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.oldValue()); } @@ -180,7 +199,11 @@ public class AtomixConsistentTreeMap extends AbstractResource> remove(String key) { - return client.submit(new UpdateAndGet(key, null, Match.ANY, Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, null, Match.ANY, Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.oldValue()); } @@ -188,7 +211,11 @@ public class AtomixConsistentTreeMap extends AbstractResource remove(String key, byte[] value) { - return client.submit(new UpdateAndGet(key, null, Match.ifValue(value), Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, null, Match.ifValue(value), Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.updated()); } @@ -196,7 +223,11 @@ public class AtomixConsistentTreeMap extends AbstractResource remove(String key, long version) { - return client.submit(new UpdateAndGet(key, null, Match.ANY, Match.ifValue(version))) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, null, Match.ANY, Match.ifValue(version)), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.updated()); } @@ -204,7 +235,11 @@ public class AtomixConsistentTreeMap extends AbstractResource> replace(String key, byte[] value) { - return client.submit(new UpdateAndGet(key, value, Match.NOT_NULL, Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, value, Match.NOT_NULL, Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.oldValue()); } @@ -212,7 +247,11 @@ public class AtomixConsistentTreeMap extends AbstractResource replace(String key, byte[] oldValue, byte[] newValue) { - return client.submit(new UpdateAndGet(key, newValue, Match.ifValue(oldValue), Match.ANY)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, newValue, Match.ifValue(oldValue), Match.ANY), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.updated()); } @@ -220,14 +259,18 @@ public class AtomixConsistentTreeMap extends AbstractResource replace(String key, long oldVersion, byte[] newValue) { - return client.submit(new UpdateAndGet(key, newValue, Match.ANY, Match.ifValue(oldVersion))) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, newValue, Match.ANY, Match.ifValue(oldVersion)), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.updated()); } @Override public CompletableFuture clear() { - return client.submit(new Clear()) + return proxy.invoke(CLEAR, SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r)) .thenApply(v -> null); } @@ -235,10 +278,10 @@ public class AtomixConsistentTreeMap extends AbstractResource> computeIf(String key, - Predicate condition, - BiFunction remappingFunction) { + Predicate condition, + BiFunction remappingFunction) { return get(key).thenCompose(r1 -> { byte[] existingValue = r1 == null ? null : r1.value(); @@ -259,8 +302,11 @@ public class AtomixConsistentTreeMap extends AbstractResource valueMatch = r1 == null ? Match.NULL : Match.ANY; Match versionMatch = r1 == null ? Match.ANY : Match.ifValue(r1.version()); - return client.submit(new UpdateAndGet(key, computedValue.get(), - valueMatch, versionMatch)) + return proxy.>invoke( + UPDATE_AND_GET, + SERIALIZER::encode, + new UpdateAndGet(key, computedValue.get(), valueMatch, versionMatch), + SERIALIZER::decode) .whenComplete((r, e) -> throwIfLocked(r.status())) .thenApply(v -> v.newValue()); }); @@ -270,9 +316,9 @@ public class AtomixConsistentTreeMap extends AbstractResource addListener( MapEventListener listener, Executor executor) { if (mapEventListeners.isEmpty()) { - return client.submit(new Listen()).thenRun(() -> - mapEventListeners.put(listener, - executor)); + return proxy.invoke(ADD_LISTENER).thenRun(() -> + mapEventListeners.put(listener, + executor)); } else { mapEventListeners.put(listener, executor); return CompletableFuture.completedFuture(null); @@ -283,7 +329,7 @@ public class AtomixConsistentTreeMap extends AbstractResource removeListener(MapEventListener listener) { if (mapEventListeners.remove(listener) != null && mapEventListeners.isEmpty()) { - return client.submit(new Unlisten()) + return proxy.invoke(REMOVE_LISTENER) .thenApply(v -> null); } return CompletableFuture.completedFuture(null); @@ -298,74 +344,74 @@ public class AtomixConsistentTreeMap extends AbstractResource firstKey() { - return client.submit(new FirstKey()); + return proxy.invoke(FIRST_KEY, SERIALIZER::decode); } @Override public CompletableFuture lastKey() { - return client.submit(new LastKey()); + return proxy.invoke(LAST_KEY, SERIALIZER::decode); } @Override public CompletableFuture>> ceilingEntry(String key) { - return client.submit(new CeilingEntry(key)); + return proxy.invoke(CEILING_ENTRY, SERIALIZER::encode, new CeilingEntry(key), SERIALIZER::decode); } @Override public CompletableFuture>> floorEntry(String key) { - return client.submit(new FloorEntry>(key)); + return proxy.invoke(FLOOR_ENTRY, SERIALIZER::encode, new FloorEntry(key), SERIALIZER::decode); } @Override public CompletableFuture>> higherEntry( String key) { - return client.submit(new HigherEntry>(key)); + return proxy.invoke(HIGHER_ENTRY, SERIALIZER::encode, new HigherEntry(key), SERIALIZER::decode); } @Override public CompletableFuture>> lowerEntry( String key) { - return client.submit(new LowerEntry<>(key)); + return proxy.invoke(LOWER_ENTRY, SERIALIZER::encode, new LowerEntry(key), SERIALIZER::decode); } @Override public CompletableFuture>> firstEntry() { - return client.submit(new FirstEntry()); + return proxy.invoke(FIRST_ENTRY, SERIALIZER::decode); } @Override public CompletableFuture>> lastEntry() { - return client.submit(new LastEntry>()); + return proxy.invoke(LAST_ENTRY, SERIALIZER::decode); } @Override public CompletableFuture>> pollFirstEntry() { - return client.submit(new PollFirstEntry()); + return proxy.invoke(POLL_FIRST_ENTRY, SERIALIZER::decode); } @Override public CompletableFuture>> pollLastEntry() { - return client.submit(new PollLastEntry()); + return proxy.invoke(POLL_LAST_ENTRY, SERIALIZER::decode); } @Override public CompletableFuture lowerKey(String key) { - return client.submit(new LowerKey(key)); + return proxy.invoke(LOWER_KEY, SERIALIZER::encode, new LowerKey(key), SERIALIZER::decode); } @Override public CompletableFuture floorKey(String key) { - return client.submit(new FloorKey(key)); + return proxy.invoke(FLOOR_KEY, SERIALIZER::encode, new FloorKey(key), SERIALIZER::decode); } @Override public CompletableFuture ceilingKey(String key) { - return client.submit(new CeilingKey(key)); + return proxy.invoke(CEILING_KEY, SERIALIZER::encode, new CeilingKey(key), SERIALIZER::decode); } @Override public CompletableFuture higherKey(String key) { - return client.submit(new HigherKey(key)); + return proxy.invoke(HIGHER_KEY, SERIALIZER::encode, new HigherKey(key), SERIALIZER::decode); } @Override @@ -404,4 +450,4 @@ public class AtomixConsistentTreeMap extends AbstractResource rollback(TransactionId transactionId) { throw new UnsupportedOperationException("This operation is not yet supported."); } -} +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapCommands.java deleted file mode 100644 index 59cf1a5ab0..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapCommands.java +++ /dev/null @@ -1,700 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.onosproject.store.primitives.resources.impl; - -import com.google.common.base.MoreObjects; -import io.atomix.catalyst.buffer.BufferInput; -import io.atomix.catalyst.buffer.BufferOutput; -import io.atomix.catalyst.serializer.CatalystSerializable; -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.catalyst.serializer.Serializer; -import io.atomix.catalyst.serializer.SerializerRegistry; -import io.atomix.catalyst.util.Assert; -import io.atomix.copycat.Command; -import io.atomix.copycat.Query; -import org.onlab.util.Match; -import org.onosproject.store.service.Versioned; - -import java.util.Collection; -import java.util.Map; -import java.util.NavigableMap; -import java.util.NavigableSet; -import java.util.Set; - -/** - * {@link org.onosproject.store.service.AsyncConsistentTreeMap} Resource - * state machine operations. - */ -public final class AtomixConsistentTreeMapCommands { - - private AtomixConsistentTreeMapCommands() { - } - - /** - * Abstract treeMap command. - */ - @SuppressWarnings("serial") - public abstract static class TreeCommand - implements Command, CatalystSerializable { - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - } - - /** - * Abstract treeMap query. - */ - @SuppressWarnings("serial") - public abstract static class TreeQuery - implements Query, CatalystSerializable { - @Override - public ConsistencyLevel consistency() { - return ConsistencyLevel.LINEARIZABLE_LEASE; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .toString(); - } - - @Override - public void writeObject(BufferOutput bufferOutput, - Serializer serializer) { - - } - - @Override - public void readObject(BufferInput bufferInput, - Serializer serializer) { - - } - } - /** - * Abstract key-based query. - */ - @SuppressWarnings("serial") - public abstract static class KeyQuery extends TreeQuery { - protected String key; - - public KeyQuery(String key) { - this.key = Assert.notNull(key, "key"); - } - - public KeyQuery() { - } - - public String key() { - return key; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("key", key) - .toString(); - } - - @Override - public void writeObject(BufferOutput bufferOutput, - Serializer serializer) { - super.writeObject(bufferOutput, serializer); - serializer.writeObject(key, bufferOutput); - } - - @Override - public void readObject(BufferInput bufferInput, - Serializer serializer) { - super.readObject(bufferInput, serializer); - key = serializer.readObject(bufferInput); - } - } - - /** - * Abstract value-based query. - */ - @SuppressWarnings("serial") - public abstract static class ValueQuery extends TreeQuery { - protected byte[] value; - - public ValueQuery() {} - - public ValueQuery(byte[] value) { - this.value = Assert.notNull(value, "value"); - } - - public byte[] value() { - return value; - } - - @Override - public void writeObject(BufferOutput bufferOutput, - Serializer serializer) { - super.writeObject(bufferOutput, serializer); - serializer.writeObject(value, bufferOutput); - } - - @Override - public void readObject(BufferInput bufferInput, - Serializer serializer) { - super.readObject(bufferInput, serializer); - value = serializer.readObject(bufferInput); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("value", value) - .toString(); - } - } - - /** - * Contains key command. - */ - @SuppressWarnings("serial") - public static class ContainsKey extends KeyQuery { - - public ContainsKey(String key) { - super(key); - } - - public ContainsKey() { - } - } - /** - * Contains value command. - */ - @SuppressWarnings("serial") - public static class ContainsValue extends ValueQuery { - public ContainsValue() { - } - - public ContainsValue(byte[] value) { - super(value); - } - - } - - /** - * AsyncConsistentTreeMap update command. - */ - @SuppressWarnings("serial") - public static class UpdateAndGet - extends TreeCommand> { - private String key; - private byte[] value; - private Match valueMatch; - private Match versionMatch; - public UpdateAndGet() { - } - - public UpdateAndGet(String key, - byte[] value, - Match valueMatch, - Match versionMatch) { - this.key = key; - this.value = value; - this.valueMatch = valueMatch; - this.versionMatch = versionMatch; - } - - public String key() { - return this.key; - } - - public byte[] value() { - return this.value; - } - - public Match valueMatch() { - return this.valueMatch; - } - - public Match versionMatch() { - return this.versionMatch; - } - - @Override - public CompactionMode compaction() { - return value == null ? CompactionMode.TOMBSTONE : CompactionMode.QUORUM; - } - - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(key, buffer); - serializer.writeObject(value, buffer); - serializer.writeObject(valueMatch, buffer); - serializer.writeObject(versionMatch, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - key = serializer.readObject(buffer); - value = serializer.readObject(buffer); - valueMatch = serializer.readObject(buffer); - versionMatch = serializer.readObject(buffer); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("key", key) - .add("value", value) - .add("valueMatch", valueMatch) - .add("versionMatch", versionMatch) - .toString(); - } - } - - /** - * Get query. - */ - @SuppressWarnings("serial") - public static class Get extends KeyQuery> { - public Get() { - } - - public Get(String key) { - super(key); - } - } - - /** - * Get or default query. - */ - @SuppressWarnings("serial") - public static class GetOrDefault extends KeyQuery> { - private byte[] defaultValue; - - public GetOrDefault() { - } - - public GetOrDefault(String key, byte[] defaultValue) { - super(key); - this.defaultValue = defaultValue; - } - - /** - * Returns the default value. - * - * @return the default value - */ - public byte[] defaultValue() { - return defaultValue; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(defaultValue, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - defaultValue = serializer.readObject(buffer); - } - } - - /** - * Is empty query. - */ - @SuppressWarnings("serial") - public static class IsEmpty extends TreeQuery { - - } - - /** - * Key set query. - */ - @SuppressWarnings("serial") - public static class KeySet extends TreeQuery> { - } - - /** - * Value set query. - */ - @SuppressWarnings("serial") - public static class Values - extends TreeQuery>> { - } - - /** - * Entry set query. - */ - @SuppressWarnings("serial") - public static class EntrySet - extends TreeQuery>>> { - } - - /** - * Size query. - */ - @SuppressWarnings("serial") - public static class Size extends TreeQuery { - } - - /** - * Clear command. - */ - @SuppressWarnings("serial") - public static class Clear - extends TreeCommand { - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - } - - /** - * Change listen. - */ - @SuppressWarnings("serial") - public static class Listen implements Command, CatalystSerializable { - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - - @Override - public CompactionMode compaction() { - return CompactionMode.QUORUM; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .toString(); - } - } - - /** - * Change unlisten. - */ - @SuppressWarnings("serial") - public static class Unlisten implements Command, - CatalystSerializable { - @Override - public void writeObject(BufferOutput buffer, - Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .toString(); - } - } - - /* Tree map specific commands below */ - - /** - * First key query. - */ - @SuppressWarnings("serial") - public static class FirstKey extends TreeQuery { - } - - /** - * Last key query. - */ - @SuppressWarnings("serial") - public static class LastKey extends TreeQuery { - } - - /** - * First entry query. - */ - @SuppressWarnings("serial") - public static class FirstEntry extends TreeQuery> { - } - - /** - * Last entry query. - */ - @SuppressWarnings("serial") - public static class LastEntry extends TreeQuery> { - } - - /** - * First entry query, if none exists returns null. - */ - @SuppressWarnings("serial") - public static class PollFirstEntry - extends TreeQuery> { - } - - /** - * Last entry query, if none exists returns null. - */ - @SuppressWarnings("serial") - public static class PollLastEntry - extends TreeQuery> { - } - - /** - * Query returns the entry associated with the largest key less than the - * passed in key. - */ - @SuppressWarnings("serial") - public static class LowerEntry extends KeyQuery { - public LowerEntry() { - } - - public LowerEntry(String key) { - super(key); - } - } - - /** - * Query returns the largest key less than the specified key. - */ - @SuppressWarnings("serial") - public static class LowerKey extends KeyQuery { - public LowerKey() { - } - - public LowerKey(String key) { - super(key); - } - } - - /** - * Query returns the entry associated with the largest key smaller than or - * equal to the specified key. - */ - @SuppressWarnings("serial") - public static class FloorEntry extends KeyQuery> { - public FloorEntry() { - } - - public FloorEntry(String key) { - super(key); - } - } - - /** - * Query returns the largest key smaller than or equal to the passed in - * key. - */ - @SuppressWarnings("serial") - public static class FloorKey extends KeyQuery { - public FloorKey() { - } - - public FloorKey(String key) { - super(key); - } - } - - /** - * Returns the entry associated with the smallest key larger than or equal - * to the specified key. - */ - @SuppressWarnings("serial") - public static class CeilingEntry extends KeyQuery> { - public CeilingEntry() { - } - - public CeilingEntry(String key) { - super(key); - } - } - - /** - * Returns the smallest key larger than or equal to the specified key. - * - * @param key type - */ - @SuppressWarnings("serial") - public static class CeilingKey extends KeyQuery { - public CeilingKey() { - } - - public CeilingKey(String key) { - super(key); - } - } - - /** - * Returns the entry associated with the smallest key larger than the - * specified key. - */ - @SuppressWarnings("serial") - public static class HigherEntry extends KeyQuery> { - public HigherEntry() { - } - - public HigherEntry(String key) { - super(key); - } - } - - /** - * Returns the smallest key larger than the specified key. - */ - @SuppressWarnings("serial") - public static class HigherKey extends KeyQuery { - public HigherKey() { - } - - public HigherKey(String key) { - super(key); - } - } - - @SuppressWarnings("serial") - public static class NavigableKeySet - extends TreeQuery> { - } - - @SuppressWarnings("serial") - public static class SubMap extends TreeQuery> { - private K fromKey; - private K toKey; - private boolean inclusiveFrom; - private boolean inclusiveTo; - - public SubMap() { - } - - public SubMap(K fromKey, K toKey, boolean inclusiveFrom, - boolean inclusiveTo) { - this.fromKey = fromKey; - this.toKey = toKey; - this.inclusiveFrom = inclusiveFrom; - this.inclusiveTo = inclusiveTo; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("getFromKey", fromKey) - .add("getToKey", toKey) - .add("inclusiveFrotBound", inclusiveFrom) - .add("inclusiveToBound", inclusiveTo) - .toString(); - } - - @Override - public void writeObject(BufferOutput bufferOutput, - Serializer serializer) { - super.writeObject(bufferOutput, serializer); - serializer.writeObject(fromKey, bufferOutput); - serializer.writeObject(toKey, bufferOutput); - serializer.writeObject(inclusiveFrom, bufferOutput); - serializer.writeObject(inclusiveTo, bufferOutput); - } - - @Override - public void readObject(BufferInput bufferInput, - Serializer serializer) { - super.readObject(bufferInput, serializer); - fromKey = serializer.readObject(bufferInput); - toKey = serializer.readObject(bufferInput); - inclusiveFrom = serializer.readObject(bufferInput); - inclusiveTo = serializer.readObject(bufferInput); - } - - public K fromKey() { - return fromKey; - } - - public K toKey() { - return toKey; - } - - public boolean isInclusiveFrom() { - return inclusiveFrom; - } - - public boolean isInclusiveTo() { - return inclusiveTo; - } - } - - /** - * Tree map command type resolver. - */ - public static class TypeResolver implements SerializableTypeResolver { - @Override - public void resolve(SerializerRegistry registry) { - //NOTE the registration values must be unique throughout the - // project. - registry.register(ContainsKey.class, -1161); - registry.register(ContainsValue.class, -1162); - registry.register(Get.class, -1163); - registry.register(GetOrDefault.class, -1192); - registry.register(EntrySet.class, -1164); - registry.register(Values.class, -1165); - registry.register(KeySet.class, -1166); - registry.register(Clear.class, -1167); - registry.register(IsEmpty.class, -1168); - registry.register(Size.class, -1169); - registry.register(Listen.class, -1170); - registry.register(Unlisten.class, -1171); - //Transaction related commands will be added here with numbers - // -1172 to -1174 - registry.register(UpdateAndGet.class, -1175); - registry.register(FirstKey.class, -1176); - registry.register(LastKey.class, -1177); - registry.register(FirstEntry.class, -1178); - registry.register(LastEntry.class, -1179); - registry.register(PollFirstEntry.class, -1180); - registry.register(PollLastEntry.class, -1181); - registry.register(LowerEntry.class, -1182); - registry.register(LowerKey.class, -1183); - registry.register(FloorEntry.class, -1184); - registry.register(FloorKey.class, -1185); - registry.register(CeilingEntry.class, -1186); - registry.register(CeilingKey.class, -1187); - registry.register(HigherEntry.class, -1188); - registry.register(HigherKey.class, -1189); - registry.register(SubMap.class, -1190); - registry.register(NavigableKeySet.class, -1191); - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapEvents.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapEvents.java new file mode 100644 index 0000000000..ab3b972211 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapEvents.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.event.EventType; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.MapEvent; + +/** + * Atomix consistent tree map events. + */ +public enum AtomixConsistentTreeMapEvents implements EventType { + CHANGE("change"); + + private final String id; + + AtomixConsistentTreeMapEvents(String id) { + this.id = id; + } + + @Override + public String id() { + return id; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 50) + .register(MapEvent.class) + .register(MapEvent.Type.class) + .build("AtomixConsistentTreeMapEvents"); +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapFactory.java deleted file mode 100644 index 8ce85576a8..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapFactory.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.onosproject.store.primitives.resources.impl; - -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.ResourceFactory; -import io.atomix.resource.ResourceStateMachine; - -import java.util.Properties; - -/** - * Factory for {@link AtomixConsistentTreeMap}. - */ -public class AtomixConsistentTreeMapFactory implements ResourceFactory { - @Override - public SerializableTypeResolver createSerializableTypeResolver() { - return new AtomixConsistentTreeMapCommands.TypeResolver(); - } - - @Override - public ResourceStateMachine createStateMachine(Properties config) { - return new AtomixConsistentTreeMapState(config); - } - - @Override - public AtomixConsistentTreeMap createInstance(CopycatClient client, Properties options) { - return new AtomixConsistentTreeMap(client, options); - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapOperations.java new file mode 100644 index 0000000000..c6b8d88f5a --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapOperations.java @@ -0,0 +1,438 @@ +/* + * Copyright 2016-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.store.primitives.resources.impl; + +import java.util.AbstractMap; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.Maps; +import io.atomix.protocols.raft.operation.OperationId; +import io.atomix.protocols.raft.operation.OperationType; +import org.onlab.util.KryoNamespace; +import org.onlab.util.Match; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.Versioned; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link org.onosproject.store.service.AsyncConsistentTreeMap} Resource + * state machine operations. + */ +public enum AtomixConsistentTreeMapOperations implements OperationId { + CONTAINS_KEY("containsKey", OperationType.QUERY), + CONTAINS_VALUE("containsValue", OperationType.QUERY), + ENTRY_SET("entrySet", OperationType.QUERY), + GET("get", OperationType.QUERY), + GET_OR_DEFAULT("getOrDefault", OperationType.QUERY), + IS_EMPTY("isEmpty", OperationType.QUERY), + KEY_SET("keySet", OperationType.QUERY), + SIZE("size", OperationType.QUERY), + VALUES("values", OperationType.QUERY), + SUB_MAP("subMap", OperationType.QUERY), + FIRST_KEY("firstKey", OperationType.QUERY), + LAST_KEY("lastKey", OperationType.QUERY), + FIRST_ENTRY("firstEntry", OperationType.QUERY), + LAST_ENTRY("lastEntry", OperationType.QUERY), + POLL_FIRST_ENTRY("pollFirstEntry", OperationType.QUERY), + POLL_LAST_ENTRY("pollLastEntry", OperationType.QUERY), + LOWER_ENTRY("lowerEntry", OperationType.QUERY), + LOWER_KEY("lowerKey", OperationType.QUERY), + FLOOR_ENTRY("floorEntry", OperationType.QUERY), + FLOOR_KEY("floorKey", OperationType.QUERY), + CEILING_ENTRY("ceilingEntry", OperationType.QUERY), + CEILING_KEY("ceilingKey", OperationType.QUERY), + HIGHER_ENTRY("higherEntry", OperationType.QUERY), + HIGHER_KEY("higherKey", OperationType.QUERY), + UPDATE_AND_GET("updateAndGet", OperationType.COMMAND), + CLEAR("clear", OperationType.COMMAND), + ADD_LISTENER("addListener", OperationType.COMMAND), + REMOVE_LISTENER("removeListener", OperationType.COMMAND); + + private final String id; + private final OperationType type; + + AtomixConsistentTreeMapOperations(String id, OperationType type) { + this.id = id; + this.type = type; + } + + @Override + public String id() { + return id; + } + + @Override + public OperationType type() { + return type; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID) + .register(ContainsKey.class) + .register(ContainsValue.class) + .register(Get.class) + .register(GetOrDefault.class) + .register(LowerKey.class) + .register(LowerEntry.class) + .register(HigherKey.class) + .register(HigherEntry.class) + .register(FloorKey.class) + .register(FloorEntry.class) + .register(CeilingKey.class) + .register(CeilingEntry.class) + .register(UpdateAndGet.class) + .register(Match.class) + .register(Versioned.class) + .register(MapEntryUpdateResult.class) + .register(MapEntryUpdateResult.Status.class) + .register(AbstractMap.SimpleImmutableEntry.class) + .register(Maps.immutableEntry("", "").getClass()) + .build("AtomixConsistentTreeMapOperations"); + + /** + * Abstract treeMap command. + */ + @SuppressWarnings("serial") + public abstract static class TreeOperation { + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .toString(); + } + } + + /** + * Abstract key-based query. + */ + @SuppressWarnings("serial") + public abstract static class KeyOperation extends TreeOperation { + protected String key; + + public KeyOperation(String key) { + this.key = checkNotNull(key); + } + + public KeyOperation() { + } + + public String key() { + return key; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("key", key) + .toString(); + } + } + + /** + * Abstract value-based query. + */ + @SuppressWarnings("serial") + public abstract static class ValueOperation extends TreeOperation { + protected byte[] value; + + public ValueOperation() {} + + public ValueOperation(byte[] value) { + this.value = checkNotNull(value); + } + + public byte[] value() { + return value; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("value", value) + .toString(); + } + } + + /** + * Contains key command. + */ + @SuppressWarnings("serial") + public static class ContainsKey extends KeyOperation { + + public ContainsKey(String key) { + super(key); + } + + public ContainsKey() { + } + } + /** + * Contains value command. + */ + @SuppressWarnings("serial") + public static class ContainsValue extends ValueOperation { + public ContainsValue() { + } + + public ContainsValue(byte[] value) { + super(value); + } + + } + + /** + * AsyncConsistentTreeMap update command. + */ + @SuppressWarnings("serial") + public static class UpdateAndGet extends TreeOperation { + private String key; + private byte[] value; + private Match valueMatch; + private Match versionMatch; + public UpdateAndGet() { + } + + public UpdateAndGet(String key, + byte[] value, + Match valueMatch, + Match versionMatch) { + this.key = key; + this.value = value; + this.valueMatch = valueMatch; + this.versionMatch = versionMatch; + } + + public String key() { + return this.key; + } + + public byte[] value() { + return this.value; + } + + public Match valueMatch() { + return this.valueMatch; + } + + public Match versionMatch() { + return this.versionMatch; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("key", key) + .add("value", value) + .add("valueMatch", valueMatch) + .add("versionMatch", versionMatch) + .toString(); + } + } + + /** + * Get query. + */ + @SuppressWarnings("serial") + public static class Get extends KeyOperation { + public Get() { + } + + public Get(String key) { + super(key); + } + } + + /** + * Get or default query. + */ + @SuppressWarnings("serial") + public static class GetOrDefault extends KeyOperation { + private byte[] defaultValue; + + public GetOrDefault() { + } + + public GetOrDefault(String key, byte[] defaultValue) { + super(key); + this.defaultValue = defaultValue; + } + + /** + * Returns the default value. + * + * @return the default value + */ + public byte[] defaultValue() { + return defaultValue; + } + } + + /** + * Query returns the entry associated with the largest key less than the + * passed in key. + */ + @SuppressWarnings("serial") + public static class LowerEntry extends KeyOperation { + public LowerEntry() { + } + + public LowerEntry(String key) { + super(key); + } + } + + /** + * Query returns the largest key less than the specified key. + */ + @SuppressWarnings("serial") + public static class LowerKey extends KeyOperation { + public LowerKey() { + } + + public LowerKey(String key) { + super(key); + } + } + + /** + * Query returns the entry associated with the largest key smaller than or + * equal to the specified key. + */ + @SuppressWarnings("serial") + public static class FloorEntry extends KeyOperation { + public FloorEntry() { + } + + public FloorEntry(String key) { + super(key); + } + } + + /** + * Query returns the largest key smaller than or equal to the passed in + * key. + */ + @SuppressWarnings("serial") + public static class FloorKey extends KeyOperation { + public FloorKey() { + } + + public FloorKey(String key) { + super(key); + } + } + + /** + * Returns the entry associated with the smallest key larger than or equal + * to the specified key. + */ + @SuppressWarnings("serial") + public static class CeilingEntry extends KeyOperation { + public CeilingEntry() { + } + + public CeilingEntry(String key) { + super(key); + } + } + + /** + * Returns the smallest key larger than or equal to the specified key. + */ + @SuppressWarnings("serial") + public static class CeilingKey extends KeyOperation { + public CeilingKey() { + } + + public CeilingKey(String key) { + super(key); + } + } + + /** + * Returns the entry associated with the smallest key larger than the + * specified key. + */ + @SuppressWarnings("serial") + public static class HigherEntry extends KeyOperation { + public HigherEntry() { + } + + public HigherEntry(String key) { + super(key); + } + } + + /** + * Returns the smallest key larger than the specified key. + */ + @SuppressWarnings("serial") + public static class HigherKey extends KeyOperation { + public HigherKey() { + } + + public HigherKey(String key) { + super(key); + } + } + + @SuppressWarnings("serial") + public static class SubMap extends TreeOperation { + private K fromKey; + private K toKey; + private boolean inclusiveFrom; + private boolean inclusiveTo; + + public SubMap() { + } + + public SubMap(K fromKey, K toKey, boolean inclusiveFrom, + boolean inclusiveTo) { + this.fromKey = fromKey; + this.toKey = toKey; + this.inclusiveFrom = inclusiveFrom; + this.inclusiveTo = inclusiveTo; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("getFromKey", fromKey) + .add("getToKey", toKey) + .add("inclusiveFrotBound", inclusiveFrom) + .add("inclusiveToBound", inclusiveTo) + .toString(); + } + + public K fromKey() { + return fromKey; + } + + public K toKey() { + return toKey; + } + + public boolean isInclusiveFrom() { + return inclusiveFrom; + } + + public boolean isInclusiveTo() { + return inclusiveTo; + } + } +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapService.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapService.java new file mode 100644 index 0000000000..b9e71356f9 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapService.java @@ -0,0 +1,416 @@ +/* + * Copyright 2016-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.store.primitives.resources.impl; + +import java.util.AbstractMap.SimpleImmutableEntry; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NavigableMap; +import java.util.Set; +import java.util.TreeMap; +import java.util.stream.Collectors; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import io.atomix.protocols.raft.service.AbstractRaftService; +import io.atomix.protocols.raft.service.Commit; +import io.atomix.protocols.raft.service.RaftServiceExecutor; +import io.atomix.protocols.raft.session.RaftSession; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import org.onlab.util.KryoNamespace; +import org.onlab.util.Match; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.MapEvent; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.Versioned; + +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapEvents.CHANGE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ADD_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CEILING_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CEILING_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CLEAR; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CONTAINS_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CONTAINS_VALUE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CeilingEntry; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.CeilingKey; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ContainsKey; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ContainsValue; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.ENTRY_SET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FIRST_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FIRST_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FLOOR_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FLOOR_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FloorEntry; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.FloorKey; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GET_OR_DEFAULT; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.Get; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GetOrDefault; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HIGHER_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HIGHER_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HigherEntry; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.HigherKey; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.IS_EMPTY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.KEY_SET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LAST_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LAST_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LOWER_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LOWER_KEY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LowerEntry; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.LowerKey; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.POLL_FIRST_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.POLL_LAST_ENTRY; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.REMOVE_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.SIZE; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.SUB_MAP; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.SubMap; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.UPDATE_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.UpdateAndGet; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.VALUES; +import static org.onosproject.store.primitives.resources.impl.MapEntryUpdateResult.Status; + +/** + * State machine corresponding to {@link AtomixConsistentTreeMap} backed by a + * {@link TreeMap}. + */ +public class AtomixConsistentTreeMapService extends AbstractRaftService { + + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixConsistentTreeMapOperations.NAMESPACE) + .register(AtomixConsistentTreeMapEvents.NAMESPACE) + .register(TreeMapEntryValue.class) + .register(new HashMap<>().keySet().getClass()) + .register(TreeMap.class) + .build()); + + private final Map listeners = Maps.newHashMap(); + private TreeMap tree = Maps.newTreeMap(); + private final Set preparedKeys = Sets.newHashSet(); + + @Override + public void snapshot(SnapshotWriter writer) { + writer.writeObject(Sets.newHashSet(listeners.keySet()), SERIALIZER::encode); + writer.writeObject(preparedKeys, SERIALIZER::encode); + writer.writeObject(tree, SERIALIZER::encode); + } + + @Override + public void install(SnapshotReader reader) { + listeners.clear(); + for (long sessionId : reader.>readObject(SERIALIZER::decode)) { + listeners.put(sessionId, getSessions().getSession(sessionId)); + } + + preparedKeys.clear(); + preparedKeys.addAll(reader.readObject(SERIALIZER::decode)); + + tree.clear(); + tree.putAll(reader.readObject(SERIALIZER::decode)); + } + + @Override + public void configure(RaftServiceExecutor executor) { + // Listeners + executor.register(ADD_LISTENER, this::listen); + executor.register(REMOVE_LISTENER, this::unlisten); + // Queries + executor.register(CONTAINS_KEY, SERIALIZER::decode, this::containsKey, SERIALIZER::encode); + executor.register(CONTAINS_VALUE, SERIALIZER::decode, this::containsValue, SERIALIZER::encode); + executor.register(ENTRY_SET, this::entrySet, SERIALIZER::encode); + executor.register(GET, SERIALIZER::decode, this::get, SERIALIZER::encode); + executor.register(GET_OR_DEFAULT, SERIALIZER::decode, this::getOrDefault, SERIALIZER::encode); + executor.register(IS_EMPTY, this::isEmpty, SERIALIZER::encode); + executor.register(KEY_SET, this::keySet, SERIALIZER::encode); + executor.register(SIZE, this::size, SERIALIZER::encode); + executor.register(VALUES, this::values, SERIALIZER::encode); + executor.register(SUB_MAP, SERIALIZER::decode, this::subMap, SERIALIZER::encode); + executor.register(FIRST_KEY, this::firstKey, SERIALIZER::encode); + executor.register(LAST_KEY, this::lastKey, SERIALIZER::encode); + executor.register(FIRST_ENTRY, this::firstEntry, SERIALIZER::encode); + executor.register(LAST_ENTRY, this::lastEntry, SERIALIZER::encode); + executor.register(POLL_FIRST_ENTRY, this::pollFirstEntry, SERIALIZER::encode); + executor.register(POLL_LAST_ENTRY, this::pollLastEntry, SERIALIZER::encode); + executor.register(LOWER_ENTRY, SERIALIZER::decode, this::lowerEntry, SERIALIZER::encode); + executor.register(LOWER_KEY, SERIALIZER::decode, this::lowerKey, SERIALIZER::encode); + executor.register(FLOOR_ENTRY, SERIALIZER::decode, this::floorEntry, SERIALIZER::encode); + executor.register(FLOOR_KEY, SERIALIZER::decode, this::floorKey, SERIALIZER::encode); + executor.register(CEILING_ENTRY, SERIALIZER::decode, this::ceilingEntry, SERIALIZER::encode); + executor.register(CEILING_KEY, SERIALIZER::decode, this::ceilingKey, SERIALIZER::encode); + executor.register(HIGHER_ENTRY, SERIALIZER::decode, this::higherEntry, SERIALIZER::encode); + executor.register(HIGHER_KEY, SERIALIZER::decode, this::higherKey, SERIALIZER::encode); + + // Commands + executor.register(UPDATE_AND_GET, SERIALIZER::decode, this::updateAndGet, SERIALIZER::encode); + executor.register(CLEAR, this::clear, SERIALIZER::encode); + } + + protected boolean containsKey(Commit commit) { + return toVersioned(tree.get((commit.value().key()))) != null; + } + + protected boolean containsValue(Commit commit) { + Match valueMatch = Match + .ifValue(commit.value().value()); + return tree.values().stream().anyMatch( + value -> valueMatch.matches(value.value())); + } + + protected Versioned get(Commit commit) { + return toVersioned(tree.get(commit.value().key())); + } + + protected Versioned getOrDefault(Commit commit) { + Versioned value = toVersioned(tree.get(commit.value().key())); + return value != null ? value : new Versioned<>(commit.value().defaultValue(), 0); + } + + protected int size(Commit commit) { + return tree.size(); + } + + protected boolean isEmpty(Commit commit) { + return tree.isEmpty(); + } + + protected Set keySet(Commit commit) { + return tree.keySet().stream().collect(Collectors.toSet()); + } + + protected Collection> values(Commit commit) { + return tree.values().stream().map(this::toVersioned) + .collect(Collectors.toList()); + } + + protected Set>> entrySet(Commit commit) { + return tree + .entrySet() + .stream() + .map(e -> Maps.immutableEntry(e.getKey(), + toVersioned(e.getValue()))) + .collect(Collectors.toSet()); + } + + protected MapEntryUpdateResult updateAndGet(Commit commit) { + Status updateStatus = validate(commit.value()); + String key = commit.value().key(); + TreeMapEntryValue oldCommitValue = tree.get(commit.value().key()); + Versioned oldTreeValue = toVersioned(oldCommitValue); + + if (updateStatus != Status.OK) { + return new MapEntryUpdateResult<>(updateStatus, "", key, + oldTreeValue, oldTreeValue); + } + + byte[] newValue = commit.value().value(); + long newVersion = commit.index(); + Versioned newTreeValue = newValue == null ? null + : new Versioned(newValue, newVersion); + + MapEvent.Type updateType = newValue == null ? MapEvent.Type.REMOVE + : oldCommitValue == null ? MapEvent.Type.INSERT : + MapEvent.Type.UPDATE; + if (updateType == MapEvent.Type.REMOVE || + updateType == MapEvent.Type.UPDATE) { + tree.remove(key); + } + if (updateType == MapEvent.Type.INSERT || + updateType == MapEvent.Type.UPDATE) { + tree.put(key, new TreeMapEntryValue(newVersion, commit.value().value())); + } + publish(Lists.newArrayList(new MapEvent<>("", key, newTreeValue, + oldTreeValue))); + return new MapEntryUpdateResult<>(updateStatus, "", key, oldTreeValue, + newTreeValue); + } + + protected Status clear(Commit commit) { + Iterator> iterator = tree + .entrySet() + .iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + String key = entry.getKey(); + TreeMapEntryValue value = entry.getValue(); + Versioned removedValue = + new Versioned(value.value(), + value.version()); + publish(Lists.newArrayList(new MapEvent<>("", key, null, + removedValue))); + iterator.remove(); + } + return Status.OK; + } + + protected void listen(Commit commit) { + listeners.put(commit.session().sessionId().id(), commit.session()); + } + + protected void unlisten(Commit commit) { + closeListener(commit.session().sessionId().id()); + } + + private Status validate(UpdateAndGet update) { + TreeMapEntryValue existingValue = tree.get(update.key()); + if (existingValue == null && update.value() == null) { + return Status.NOOP; + } + if (preparedKeys.contains(update.key())) { + return Status.WRITE_LOCK; + } + byte[] existingRawValue = existingValue == null ? null : + existingValue.value(); + Long existingVersion = existingValue == null ? null : + existingValue.version(); + return update.valueMatch().matches(existingRawValue) + && update.versionMatch().matches(existingVersion) ? + Status.OK + : Status.PRECONDITION_FAILED; + } + + protected NavigableMap subMap( + Commit commit) { + // Do not support this until lazy communication is possible. At present + // it transmits up to the entire map. + SubMap subMap = commit.value(); + return tree.subMap(subMap.fromKey(), subMap.isInclusiveFrom(), + subMap.toKey(), subMap.isInclusiveTo()); + } + + protected String firstKey(Commit commit) { + if (tree.isEmpty()) { + return null; + } + return tree.firstKey(); + } + + protected String lastKey(Commit commit) { + return tree.isEmpty() ? null : tree.lastKey(); + } + + protected Map.Entry> higherEntry(Commit commit) { + if (tree.isEmpty()) { + return null; + } + return toVersionedEntry( + tree.higherEntry(commit.value().key())); + } + + protected Map.Entry> firstEntry(Commit commit) { + if (tree.isEmpty()) { + return null; + } + return toVersionedEntry(tree.firstEntry()); + } + + protected Map.Entry> lastEntry(Commit commit) { + if (tree.isEmpty()) { + return null; + } + return toVersionedEntry(tree.lastEntry()); + } + + protected Map.Entry> pollFirstEntry(Commit commit) { + return toVersionedEntry(tree.pollFirstEntry()); + } + + protected Map.Entry> pollLastEntry(Commit commit) { + return toVersionedEntry(tree.pollLastEntry()); + } + + protected Map.Entry> lowerEntry(Commit commit) { + return toVersionedEntry(tree.lowerEntry(commit.value().key())); + } + + protected String lowerKey(Commit commit) { + return tree.lowerKey(commit.value().key()); + } + + protected Map.Entry> floorEntry(Commit commit) { + return toVersionedEntry(tree.floorEntry(commit.value().key())); + } + + protected String floorKey(Commit commit) { + return tree.floorKey(commit.value().key()); + } + + protected Map.Entry> ceilingEntry(Commit commit) { + return toVersionedEntry( + tree.ceilingEntry(commit.value().key())); + } + + protected String ceilingKey(Commit commit) { + return tree.ceilingKey(commit.value().key()); + } + + protected String higherKey(Commit commit) { + return tree.higherKey(commit.value().key()); + } + + private Versioned toVersioned(TreeMapEntryValue value) { + return value == null ? null : + new Versioned(value.value(), value.version()); + } + + private Map.Entry> toVersionedEntry( + Map.Entry entry) { + //FIXME is this the best type of entry to return? + return entry == null ? null : new SimpleImmutableEntry<>( + entry.getKey(), toVersioned(entry.getValue())); + } + + private void publish(List> events) { + listeners.values().forEach(session -> session.publish(CHANGE, SERIALIZER::encode, events)); + } + + @Override + public void onExpire(RaftSession session) { + closeListener(session.sessionId().id()); + } + + @Override + public void onClose(RaftSession session) { + closeListener(session.sessionId().id()); + } + + private void closeListener(Long sessionId) { + listeners.remove(sessionId); + } + + private static class TreeMapEntryValue { + private final long version; + private final byte[] value; + + public TreeMapEntryValue(long version, byte[] value) { + this.version = version; + this.value = value; + } + + public byte[] value() { + return value; + } + + public long version() { + return version; + } + } +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapState.java deleted file mode 100644 index 576bfea5d6..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapState.java +++ /dev/null @@ -1,575 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.onosproject.store.primitives.resources.impl; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import io.atomix.copycat.server.Commit; -import io.atomix.copycat.server.StateMachineExecutor; -import io.atomix.copycat.server.session.ServerSession; -import io.atomix.copycat.server.session.SessionListener; -import io.atomix.resource.ResourceStateMachine; -import org.onlab.util.Match; -import org.onosproject.store.service.MapEvent; -import org.onosproject.store.service.Versioned; - -import java.util.AbstractMap.SimpleImmutableEntry; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.NavigableMap; -import java.util.Properties; -import java.util.Set; -import java.util.TreeMap; -import java.util.function.Function; -import java.util.stream.Collectors; - -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.CeilingEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.CeilingKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Clear; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.ContainsKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.ContainsValue; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.EntrySet; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FirstEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FirstKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FloorEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.FloorKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Get; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.GetOrDefault; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.HigherEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.HigherKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.IsEmpty; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.KeySet; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LastEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LastKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Listen; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LowerEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.LowerKey; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.PollFirstEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.PollLastEntry; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Size; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.SubMap; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Unlisten; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.UpdateAndGet; -import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapCommands.Values; -import static org.onosproject.store.primitives.resources.impl.MapEntryUpdateResult.*; - -/** - * State machine corresponding to {@link AtomixConsistentTreeMap} backed by a - * {@link TreeMap}. - */ -public class AtomixConsistentTreeMapState extends ResourceStateMachine implements SessionListener { - - private final Map> listeners = - Maps.newHashMap(); - private TreeMap tree = Maps.newTreeMap(); - private final Set preparedKeys = Sets.newHashSet(); - - private Function, NavigableMap> subMapFunction = this::subMap; - private Function, String> firstKeyFunction = this::firstKey; - private Function, String> lastKeyFunction = this::lastKey; - private Function, Map.Entry>> higherEntryFunction = - this::higherEntry; - private Function, Map.Entry>> firstEntryFunction = - this::firstEntry; - private Function, Map.Entry>> lastEntryFunction = - this::lastEntry; - private Function, Map.Entry>> pollFirstEntryFunction = - this::pollFirstEntry; - private Function, Map.Entry>> pollLastEntryFunction = - this::pollLastEntry; - private Function, Map.Entry>> lowerEntryFunction = - this::lowerEntry; - private Function, String> lowerKeyFunction = this::lowerKey; - private Function, Map.Entry>> floorEntryFunction = - this::floorEntry; - private Function, Map.Entry>> ceilingEntryFunction = - this::ceilingEntry; - private Function, String> floorKeyFunction = this::floorKey; - private Function, String> ceilingKeyFunction = this::ceilingKey; - private Function, String> higherKeyFunction = this::higherKey; - - public AtomixConsistentTreeMapState(Properties properties) { - super(properties); - } - - @Override - public void configure(StateMachineExecutor executor) { - // Listeners - executor.register(Listen.class, this::listen); - executor.register(Unlisten.class, this::unlisten); - // Queries - executor.register(ContainsKey.class, this::containsKey); - executor.register(ContainsValue.class, this::containsValue); - executor.register(EntrySet.class, this::entrySet); - executor.register(Get.class, this::get); - executor.register(GetOrDefault.class, this::getOrDefault); - executor.register(IsEmpty.class, this::isEmpty); - executor.register(KeySet.class, this::keySet); - executor.register(Size.class, this::size); - executor.register(Values.class, this::values); - executor.register(SubMap.class, subMapFunction); - executor.register(FirstKey.class, firstKeyFunction); - executor.register(LastKey.class, lastKeyFunction); - executor.register(FirstEntry.class, firstEntryFunction); - executor.register(LastEntry.class, lastEntryFunction); - executor.register(PollFirstEntry.class, pollFirstEntryFunction); - executor.register(PollLastEntry.class, pollLastEntryFunction); - executor.register(LowerEntry.class, lowerEntryFunction); - executor.register(LowerKey.class, lowerKeyFunction); - executor.register(FloorEntry.class, floorEntryFunction); - executor.register(FloorKey.class, floorKeyFunction); - executor.register(CeilingEntry.class, ceilingEntryFunction); - executor.register(CeilingKey.class, ceilingKeyFunction); - executor.register(HigherEntry.class, higherEntryFunction); - executor.register(HigherKey.class, higherKeyFunction); - - // Commands - executor.register(UpdateAndGet.class, this::updateAndGet); - executor.register(Clear.class, this::clear); - } - - @Override - public void delete() { - listeners.values().forEach(Commit::close); - listeners.clear(); - tree.values().forEach(TreeMapEntryValue::discard); - tree.clear(); - } - - protected boolean containsKey(Commit commit) { - try { - return toVersioned(tree.get((commit.operation().key()))) != null; - } finally { - commit.close(); - } - } - - protected boolean containsValue(Commit commit) { - try { - Match valueMatch = Match - .ifValue(commit.operation().value()); - return tree.values().stream().anyMatch( - value -> valueMatch.matches(value.value())); - } finally { - commit.close(); - } - } - - protected Versioned get(Commit commit) { - try { - return toVersioned(tree.get(commit.operation().key())); - } finally { - commit.close(); - } - } - - protected Versioned getOrDefault(Commit commit) { - try { - Versioned value = toVersioned(tree.get(commit.operation().key())); - return value != null ? value : new Versioned<>(commit.operation().defaultValue(), 0); - } finally { - commit.close(); - } - } - - protected int size(Commit commit) { - try { - return tree.size(); - } finally { - commit.close(); - } - } - - protected boolean isEmpty(Commit commit) { - try { - return tree.isEmpty(); - } finally { - commit.close(); - } - } - - protected Set keySet(Commit commit) { - try { - return tree.keySet().stream().collect(Collectors.toSet()); - } finally { - commit.close(); - } - } - - protected Collection> values( - Commit commit) { - try { - return tree.values().stream().map(this::toVersioned) - .collect(Collectors.toList()); - } finally { - commit.close(); - } - } - - protected Set>> entrySet( - Commit commit) { - try { - return tree - .entrySet() - .stream() - .map(e -> Maps.immutableEntry(e.getKey(), - toVersioned(e.getValue()))) - .collect(Collectors.toSet()); - } finally { - commit.close(); - } - } - - protected MapEntryUpdateResult updateAndGet( - Commit commit) { - Status updateStatus = validate(commit.operation()); - String key = commit.operation().key(); - TreeMapEntryValue oldCommitValue = tree.get(commit.operation().key()); - Versioned oldTreeValue = toVersioned(oldCommitValue); - - if (updateStatus != Status.OK) { - commit.close(); - return new MapEntryUpdateResult<>(updateStatus, "", key, - oldTreeValue, oldTreeValue); - } - - byte[] newValue = commit.operation().value(); - long newVersion = commit.index(); - Versioned newTreeValue = newValue == null ? null - : new Versioned(newValue, newVersion); - - MapEvent.Type updateType = newValue == null ? MapEvent.Type.REMOVE - : oldCommitValue == null ? MapEvent.Type.INSERT : - MapEvent.Type.UPDATE; - if (updateType == MapEvent.Type.REMOVE || - updateType == MapEvent.Type.UPDATE) { - tree.remove(key); - oldCommitValue.discard(); - } - if (updateType == MapEvent.Type.INSERT || - updateType == MapEvent.Type.UPDATE) { - tree.put(key, new NonTransactionalCommit(newVersion, commit)); - } else { - commit.close(); - } - publish(Lists.newArrayList(new MapEvent<>("", key, newTreeValue, - oldTreeValue))); - return new MapEntryUpdateResult<>(updateStatus, "", key, oldTreeValue, - newTreeValue); - } - - protected Status clear( - Commit commit) { - try { - Iterator> iterator = tree - .entrySet() - .iterator(); - while (iterator.hasNext()) { - Map.Entry entry = iterator.next(); - String key = entry.getKey(); - TreeMapEntryValue value = entry.getValue(); - Versioned removedValue = - new Versioned(value.value(), - value.version()); - publish(Lists.newArrayList(new MapEvent<>("", key, null, - removedValue))); - value.discard(); - iterator.remove(); - } - return Status.OK; - } finally { - commit.close(); - } - } - - protected void listen( - Commit commit) { - Long sessionId = commit.session().id(); - listeners.put(sessionId, commit); - commit.session() - .onStateChange( - state -> { - if (state == ServerSession.State.CLOSED - || state == ServerSession.State.EXPIRED) { - Commit listener = - listeners.remove(sessionId); - if (listener != null) { - listener.close(); - } - } - }); - } - - protected void unlisten( - Commit commit) { - try { - Commit listener = - listeners.remove(commit.session().id()); - if (listener != null) { - listener.close(); - } - } finally { - commit.close(); - } - } - - private Status validate(UpdateAndGet update) { - TreeMapEntryValue existingValue = tree.get(update.key()); - if (existingValue == null && update.value() == null) { - return Status.NOOP; - } - if (preparedKeys.contains(update.key())) { - return Status.WRITE_LOCK; - } - byte[] existingRawValue = existingValue == null ? null : - existingValue.value(); - Long existingVersion = existingValue == null ? null : - existingValue.version(); - return update.valueMatch().matches(existingRawValue) - && update.versionMatch().matches(existingVersion) ? - Status.OK - : Status.PRECONDITION_FAILED; - } - - protected NavigableMap subMap( - Commit commit) { - //Do not support this until lazy communication is possible. At present - // it transmits up to the entire map. - try { - SubMap subMap = commit.operation(); - return tree.subMap(subMap.fromKey(), subMap.isInclusiveFrom(), - subMap.toKey(), subMap.isInclusiveTo()); - } finally { - commit.close(); - } - } - - protected String firstKey(Commit commit) { - try { - if (tree.isEmpty()) { - return null; - } - return tree.firstKey(); - } finally { - commit.close(); - } - } - - protected String lastKey(Commit commit) { - try { - return tree.isEmpty() ? null : tree.lastKey(); - } finally { - commit.close(); - } - } - - protected Map.Entry> higherEntry( - Commit commit) { - try { - if (tree.isEmpty()) { - return null; - } - return toVersionedEntry( - tree.higherEntry(commit.operation().key())); - } finally { - commit.close(); - } - } - - protected Map.Entry> firstEntry( - Commit commit) { - try { - if (tree.isEmpty()) { - return null; - } - return toVersionedEntry(tree.firstEntry()); - } finally { - commit.close(); - } - } - - protected Map.Entry> lastEntry( - Commit commit) { - try { - if (tree.isEmpty()) { - return null; - } - return toVersionedEntry(tree.lastEntry()); - } finally { - commit.close(); - } - } - - protected Map.Entry> pollFirstEntry( - Commit commit) { - try { - return toVersionedEntry(tree.pollFirstEntry()); - } finally { - commit.close(); - } - } - - protected Map.Entry> pollLastEntry( - Commit commit) { - try { - return toVersionedEntry(tree.pollLastEntry()); - } finally { - commit.close(); - } - } - - protected Map.Entry> lowerEntry( - Commit commit) { - try { - return toVersionedEntry(tree.lowerEntry(commit.operation().key())); - } finally { - commit.close(); - } - } - - protected String lowerKey(Commit commit) { - try { - return tree.lowerKey(commit.operation().key()); - } finally { - commit.close(); - } - } - - protected Map.Entry> floorEntry( - Commit commit) { - try { - return toVersionedEntry(tree.floorEntry(commit.operation().key())); - } finally { - commit.close(); - } - } - - protected String floorKey(Commit commit) { - try { - return tree.floorKey(commit.operation().key()); - } finally { - commit.close(); - } - } - - protected Map.Entry> ceilingEntry( - Commit commit) { - try { - return toVersionedEntry( - tree.ceilingEntry(commit.operation().key())); - } finally { - commit.close(); - } - } - - protected String ceilingKey(Commit commit) { - try { - return tree.ceilingKey(commit.operation().key()); - } finally { - commit.close(); - } - } - - protected String higherKey(Commit commit) { - try { - return tree.higherKey(commit.operation().key()); - } finally { - commit.close(); - } - } - - private Versioned toVersioned(TreeMapEntryValue value) { - return value == null ? null : - new Versioned(value.value(), value.version()); - } - - private Map.Entry> toVersionedEntry( - Map.Entry entry) { - //FIXME is this the best type of entry to return? - return entry == null ? null : new SimpleImmutableEntry<>( - entry.getKey(), toVersioned(entry.getValue())); - } - - private void publish(List> events) { - listeners.values().forEach(commit -> commit.session() - .publish(AtomixConsistentTreeMap.CHANGE_SUBJECT, events)); - } - - @Override - public void register(ServerSession session) { - } - - @Override - public void unregister(ServerSession session) { - closeListener(session.id()); - } - - @Override - public void expire(ServerSession session) { - closeListener(session.id()); - } - - @Override - public void close(ServerSession session) { - closeListener(session.id()); - } - - private void closeListener(Long sessionId) { - Commit commit = listeners.remove(sessionId); - if (commit != null) { - commit.close(); - } - } - - private interface TreeMapEntryValue { - - byte[] value(); - - long version(); - - void discard(); - } - - private class NonTransactionalCommit implements TreeMapEntryValue { - private final long version; - private final Commit commit; - - public NonTransactionalCommit(long version, - Commit commit) { - this.version = version; - this.commit = commit; - } - - @Override - public byte[] value() { - return commit.operation().value(); - } - - @Override - public long version() { - return version; - } - - @Override - public void discard() { - commit.close(); - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounter.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounter.java index e670d40d05..954008c2d4 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounter.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounter.java @@ -15,63 +15,76 @@ */ package org.onosproject.store.primitives.resources.impl; -import io.atomix.variables.DistributedLong; - import java.util.concurrent.CompletableFuture; +import io.atomix.protocols.raft.proxy.RaftProxy; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.serializers.KryoNamespaces; import org.onosproject.store.service.AsyncAtomicCounter; +import org.onosproject.store.service.Serializer; + +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.ADD_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.AddAndGet; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.COMPARE_AND_SET; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.CompareAndSet; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET_AND_ADD; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET_AND_INCREMENT; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GetAndAdd; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.INCREMENT_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.SET; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.Set; /** - * {@code AsyncAtomicCounter} implementation backed by Atomix - * {@link DistributedLong}. + * Atomix counter implementation. */ -public class AtomixCounter implements AsyncAtomicCounter { +public class AtomixCounter extends AbstractRaftPrimitive implements AsyncAtomicCounter { + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixCounterOperations.NAMESPACE) + .build()); - private final String name; - private final DistributedLong distLong; - - public AtomixCounter(String name, DistributedLong distLong) { - this.name = name; - this.distLong = distLong; + public AtomixCounter(RaftProxy proxy) { + super(proxy); } - @Override - public String name() { - return name; - } - - @Override - public CompletableFuture incrementAndGet() { - return distLong.incrementAndGet(); - } - - @Override - public CompletableFuture getAndIncrement() { - return distLong.getAndIncrement(); - } - - @Override - public CompletableFuture getAndAdd(long delta) { - return distLong.getAndAdd(delta); - } - - @Override - public CompletableFuture addAndGet(long delta) { - return distLong.addAndGet(delta); + private long nullOrZero(Long value) { + return value != null ? value : 0; } @Override public CompletableFuture get() { - return distLong.get(); + return proxy.invoke(GET, SERIALIZER::decode).thenApply(this::nullOrZero); } @Override public CompletableFuture set(long value) { - return distLong.set(value); + return proxy.invoke(SET, SERIALIZER::encode, new Set(value)); } @Override public CompletableFuture compareAndSet(long expectedValue, long updateValue) { - return distLong.compareAndSet(expectedValue, updateValue); + return proxy.invoke(COMPARE_AND_SET, SERIALIZER::encode, + new CompareAndSet(expectedValue, updateValue), SERIALIZER::decode); + } + + @Override + public CompletableFuture addAndGet(long delta) { + return proxy.invoke(ADD_AND_GET, SERIALIZER::encode, new AddAndGet(delta), SERIALIZER::decode); + } + + @Override + public CompletableFuture getAndAdd(long delta) { + return proxy.invoke(GET_AND_ADD, SERIALIZER::encode, new GetAndAdd(delta), SERIALIZER::decode); + } + + @Override + public CompletableFuture incrementAndGet() { + return proxy.invoke(INCREMENT_AND_GET, SERIALIZER::decode); + } + + @Override + public CompletableFuture getAndIncrement() { + return proxy.invoke(GET_AND_INCREMENT, SERIALIZER::decode); } } \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterOperations.java new file mode 100644 index 0000000000..0ebe7e462e --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterOperations.java @@ -0,0 +1,188 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.operation.OperationId; +import io.atomix.protocols.raft.operation.OperationType; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.serializers.KryoNamespaces; + +/** + * Counter commands. + */ +public enum AtomixCounterOperations implements OperationId { + SET("set", OperationType.COMMAND), + COMPARE_AND_SET("compareAndSet", OperationType.COMMAND), + INCREMENT_AND_GET("incrementAndGet", OperationType.COMMAND), + GET_AND_INCREMENT("getAndIncrement", OperationType.COMMAND), + ADD_AND_GET("addAndGet", OperationType.COMMAND), + GET_AND_ADD("getAndAdd", OperationType.COMMAND), + GET("get", OperationType.QUERY); + + private final String id; + private final OperationType type; + + AtomixCounterOperations(String id, OperationType type) { + this.id = id; + this.type = type; + } + + @Override + public String id() { + return id; + } + + @Override + public OperationType type() { + return type; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID) + .register(Get.class) + .register(Set.class) + .register(CompareAndSet.class) + .register(AddAndGet.class) + .register(GetAndAdd.class) + .build("AtomixCounterOperations"); + + /** + * Abstract value command. + */ + public abstract static class ValueOperation { + } + + /** + * Get query. + */ + public static class Get extends ValueOperation { + } + + /** + * Set command. + */ + public static class Set extends ValueOperation { + private Long value; + + public Set() { + } + + public Set(Long value) { + this.value = value; + } + + /** + * Returns the command value. + * + * @return The command value. + */ + public Long value() { + return value; + } + + @Override + public String toString() { + return String.format("%s[value=%s]", getClass().getSimpleName(), value); + } + } + + /** + * Compare and set command. + */ + public static class CompareAndSet extends ValueOperation { + private Long expect; + private Long update; + + public CompareAndSet() { + } + + public CompareAndSet(Long expect, Long update) { + this.expect = expect; + this.update = update; + } + + /** + * Returns the expected value. + * + * @return The expected value. + */ + public Long expect() { + return expect; + } + + /** + * Returns the updated value. + * + * @return The updated value. + */ + public Long update() { + return update; + } + + @Override + public String toString() { + return String.format("%s[expect=%s, update=%s]", getClass().getSimpleName(), expect, update); + } + } + + /** + * Delta command. + */ + public abstract static class DeltaOperation extends ValueOperation { + private long delta; + + public DeltaOperation() { + } + + public DeltaOperation(long delta) { + this.delta = delta; + } + + /** + * Returns the delta. + * + * @return The delta. + */ + public long delta() { + return delta; + } + } + + /** + * Get and add command. + */ + public static class GetAndAdd extends DeltaOperation { + public GetAndAdd() { + } + + public GetAndAdd(long delta) { + super(delta); + } + } + + /** + * Add and get command. + */ + public static class AddAndGet extends DeltaOperation { + public AddAndGet() { + } + + public AddAndGet(long delta) { + super(delta); + } + } +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterService.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterService.java new file mode 100644 index 0000000000..a8023933c6 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixCounterService.java @@ -0,0 +1,153 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import java.util.Objects; + +import io.atomix.protocols.raft.service.AbstractRaftService; +import io.atomix.protocols.raft.service.Commit; +import io.atomix.protocols.raft.service.RaftServiceExecutor; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.Serializer; + +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.ADD_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.AddAndGet; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.COMPARE_AND_SET; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.CompareAndSet; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET_AND_ADD; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET_AND_INCREMENT; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GetAndAdd; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.INCREMENT_AND_GET; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.SET; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.Set; + +/** + * Atomix long state. + */ +public class AtomixCounterService extends AbstractRaftService { + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixCounterOperations.NAMESPACE) + .build()); + + private Long value = 0L; + + @Override + protected void configure(RaftServiceExecutor executor) { + executor.register(SET, SERIALIZER::decode, this::set); + executor.register(GET, this::get, SERIALIZER::encode); + executor.register(COMPARE_AND_SET, SERIALIZER::decode, this::compareAndSet, SERIALIZER::encode); + executor.register(INCREMENT_AND_GET, this::incrementAndGet, SERIALIZER::encode); + executor.register(GET_AND_INCREMENT, this::getAndIncrement, SERIALIZER::encode); + executor.register(ADD_AND_GET, SERIALIZER::decode, this::addAndGet, SERIALIZER::encode); + executor.register(GET_AND_ADD, SERIALIZER::decode, this::getAndAdd, SERIALIZER::encode); + } + + @Override + public void snapshot(SnapshotWriter writer) { + writer.writeLong(value); + } + + @Override + public void install(SnapshotReader reader) { + value = reader.readLong(); + } + + /** + * Handles a set commit. + * + * @param commit the commit to handle + */ + protected void set(Commit commit) { + value = commit.value().value(); + } + + /** + * Handles a get commit. + * + * @param commit the commit to handle + * @return counter value + */ + protected Long get(Commit commit) { + return value; + } + + /** + * Handles a compare and set commit. + * + * @param commit the commit to handle + * @return counter value + */ + protected boolean compareAndSet(Commit commit) { + if (Objects.equals(value, commit.value().expect())) { + value = commit.value().update(); + return true; + } + return false; + } + + /** + * Handles an increment and get commit. + * + * @param commit the commit to handle + * @return counter value + */ + protected long incrementAndGet(Commit commit) { + Long oldValue = value; + value = oldValue + 1; + return value; + } + + /** + * Handles a get and increment commit. + * + * @param commit the commit to handle + * @return counter value + */ + protected long getAndIncrement(Commit commit) { + Long oldValue = value; + value = oldValue + 1; + return oldValue; + } + + /** + * Handles an add and get commit. + * + * @param commit the commit to handle + * @return counter value + */ + protected long addAndGet(Commit commit) { + Long oldValue = value; + value = oldValue + commit.value().delta(); + return value; + } + + /** + * Handles a get and add commit. + * + * @param commit the commit to handle + * @return counter value + */ + protected long getAndAdd(Commit commit) { + Long oldValue = value; + value = oldValue + commit.value().delta(); + return oldValue; + } +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTree.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTree.java index 3875e5564f..1730fe843e 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTree.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTree.java @@ -16,70 +16,65 @@ package org.onosproject.store.primitives.resources.impl; -import static com.google.common.base.Preconditions.checkNotNull; -import static org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status.ILLEGAL_MODIFICATION; -import static org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status.INVALID_PATH; -import static org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status.OK; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.AbstractResource; -import io.atomix.resource.ResourceTypeInfo; - import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Properties; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import com.google.common.util.concurrent.MoreExecutors; +import io.atomix.protocols.raft.proxy.RaftProxy; +import org.onlab.util.KryoNamespace; import org.onlab.util.Match; import org.onlab.util.Tools; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Clear; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Get; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.GetChildren; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Listen; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Unlisten; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Update; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Get; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GetChildren; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Listen; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Unlisten; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Update; +import org.onosproject.store.serializers.KryoNamespaces; import org.onosproject.store.service.AsyncDocumentTree; import org.onosproject.store.service.DocumentPath; import org.onosproject.store.service.DocumentTreeEvent; import org.onosproject.store.service.DocumentTreeListener; import org.onosproject.store.service.IllegalDocumentModificationException; import org.onosproject.store.service.NoSuchDocumentPathException; +import org.onosproject.store.service.Serializer; import org.onosproject.store.service.Versioned; -import com.google.common.util.concurrent.MoreExecutors; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeEvents.CHANGE; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.ADD_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.CLEAR; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GET_CHILDREN; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.REMOVE_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.UPDATE; +import static org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status.ILLEGAL_MODIFICATION; +import static org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status.INVALID_PATH; +import static org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status.OK; /** * Distributed resource providing the {@link AsyncDocumentTree} primitive. */ -@ResourceTypeInfo(id = -156, factory = AtomixDocumentTreeFactory.class) -public class AtomixDocumentTree extends AbstractResource - implements AsyncDocumentTree { +public class AtomixDocumentTree extends AbstractRaftPrimitive implements AsyncDocumentTree { + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixDocumentTreeOperations.NAMESPACE) + .register(AtomixDocumentTreeEvents.NAMESPACE) + .build()); private final Map, InternalListener> eventListeners = new HashMap<>(); - public static final String CHANGE_SUBJECT = "changeEvents"; - protected AtomixDocumentTree(CopycatClient client, Properties options) { - super(client, options); - } - - @Override - public CompletableFuture open() { - return super.open().thenApply(result -> { - client.onStateChange(state -> { - if (state == CopycatClient.State.CONNECTED && isListening()) { - client.submit(new Listen()); - } - }); - client.onEvent(CHANGE_SUBJECT, this::processTreeUpdates); - return result; + public AtomixDocumentTree(RaftProxy proxy) { + super(proxy); + proxy.addStateChangeListener(state -> { + if (state == RaftProxy.State.CONNECTED && isListening()) { + proxy.invoke(ADD_LISTENER, SERIALIZER::encode, new Listen()); + } }); - } - - @Override - public String name() { - return null; + proxy.addEventListener(CHANGE, SERIALIZER::decode, this::processTreeUpdates); } @Override @@ -89,7 +84,7 @@ public class AtomixDocumentTree extends AbstractResource @Override public CompletableFuture destroy() { - return client.submit(new Clear()); + return proxy.invoke(CLEAR); } @Override @@ -99,17 +94,20 @@ public class AtomixDocumentTree extends AbstractResource @Override public CompletableFuture>> getChildren(DocumentPath path) { - return client.submit(new GetChildren(checkNotNull(path))); + return proxy.invoke(GET_CHILDREN, SERIALIZER::encode, new GetChildren(checkNotNull(path)), SERIALIZER::decode); } @Override public CompletableFuture> get(DocumentPath path) { - return client.submit(new Get(checkNotNull(path))); + return proxy.invoke(GET, SERIALIZER::encode, new Get(checkNotNull(path)), SERIALIZER::decode); } @Override public CompletableFuture> set(DocumentPath path, byte[] value) { - return client.submit(new Update(checkNotNull(path), Optional.ofNullable(value), Match.any(), Match.any())) + return proxy.>invoke(UPDATE, + SERIALIZER::encode, + new Update(checkNotNull(path), Optional.ofNullable(value), Match.any(), Match.any()), + SERIALIZER::decode) .thenCompose(result -> { if (result.status() == INVALID_PATH) { return Tools.exceptionalFuture(new NoSuchDocumentPathException()); @@ -138,7 +136,7 @@ public class AtomixDocumentTree extends AbstractResource .thenCompose(status -> { if (status == ILLEGAL_MODIFICATION) { return createRecursive(path.parent(), null) - .thenCompose(r -> createInternal(path, value).thenApply(v -> true)); + .thenCompose(r -> createInternal(path, value).thenApply(v -> true)); } return CompletableFuture.completedFuture(status == OK); }); @@ -146,19 +144,24 @@ public class AtomixDocumentTree extends AbstractResource @Override public CompletableFuture replace(DocumentPath path, byte[] newValue, long version) { - return client.submit(new Update(checkNotNull(path), - Optional.ofNullable(newValue), - Match.any(), - Match.ifValue(version))) + return proxy.>invoke(UPDATE, + SERIALIZER::encode, + new Update(checkNotNull(path), + Optional.ofNullable(newValue), + Match.any(), + Match.ifValue(version)), SERIALIZER::decode) .thenApply(result -> result.updated()); } @Override public CompletableFuture replace(DocumentPath path, byte[] newValue, byte[] currentValue) { - return client.submit(new Update(checkNotNull(path), - Optional.ofNullable(newValue), - Match.ifValue(currentValue), - Match.any())) + return proxy.>invoke(UPDATE, + SERIALIZER::encode, + new Update(checkNotNull(path), + Optional.ofNullable(newValue), + Match.ifValue(currentValue), + Match.any()), + SERIALIZER::decode) .thenCompose(result -> { if (result.status() == INVALID_PATH) { return Tools.exceptionalFuture(new NoSuchDocumentPathException()); @@ -175,7 +178,10 @@ public class AtomixDocumentTree extends AbstractResource if (path.equals(DocumentPath.from("root"))) { return Tools.exceptionalFuture(new IllegalDocumentModificationException()); } - return client.submit(new Update(checkNotNull(path), null, Match.any(), Match.ifNotNull())) + return proxy.>invoke(UPDATE, + SERIALIZER::encode, + new Update(checkNotNull(path), null, Match.any(), Match.ifNotNull()), + SERIALIZER::decode) .thenCompose(result -> { if (result.status() == INVALID_PATH) { return Tools.exceptionalFuture(new NoSuchDocumentPathException()); @@ -194,8 +200,8 @@ public class AtomixDocumentTree extends AbstractResource InternalListener internalListener = new InternalListener(path, listener, MoreExecutors.directExecutor()); // TODO: Support API that takes an executor if (!eventListeners.containsKey(listener)) { - return client.submit(new Listen(path)) - .thenRun(() -> eventListeners.put(listener, internalListener)); + return proxy.invoke(ADD_LISTENER, SERIALIZER::encode, new Listen(path)) + .thenRun(() -> eventListeners.put(listener, internalListener)); } return CompletableFuture.completedFuture(null); } @@ -205,14 +211,18 @@ public class AtomixDocumentTree extends AbstractResource checkNotNull(listener); InternalListener internalListener = eventListeners.remove(listener); if (internalListener != null && eventListeners.isEmpty()) { - return client.submit(new Unlisten(internalListener.path)).thenApply(v -> null); + return proxy.invoke(REMOVE_LISTENER, SERIALIZER::encode, new Unlisten(internalListener.path)) + .thenApply(v -> null); } return CompletableFuture.completedFuture(null); } private CompletableFuture createInternal(DocumentPath path, byte[] value) { - return client.submit(new Update(checkNotNull(path), Optional.ofNullable(value), Match.any(), Match.ifNull())) - .thenApply(result -> result.status()); + return proxy.>invoke(UPDATE, + SERIALIZER::encode, + new Update(checkNotNull(path), Optional.ofNullable(value), Match.any(), Match.ifNull()), + SERIALIZER::decode) + .thenApply(result -> result.status()); } private boolean isListening() { @@ -242,4 +252,4 @@ public class AtomixDocumentTree extends AbstractResource } } } -} +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeEvents.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeEvents.java new file mode 100644 index 0000000000..8aa23ce8de --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeEvents.java @@ -0,0 +1,45 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.event.EventType; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.DocumentTreeEvent; + +/** + * Atomix document tree events. + */ +public enum AtomixDocumentTreeEvents implements EventType { + CHANGE("change"); + + private final String id; + + AtomixDocumentTreeEvents(String id) { + this.id = id; + } + + @Override + public String id() { + return id; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 50) + .register(DocumentTreeEvent.class) + .register(DocumentTreeEvent.Type.class) + .build("AtomixDocumentTreeEvents"); +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeFactory.java deleted file mode 100644 index 4282566b29..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.resources.impl; - -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.ResourceFactory; -import io.atomix.resource.ResourceStateMachine; - -import java.util.Properties; - -/** - * {@link AtomixDocumentTree} resource factory. - * - */ -public class AtomixDocumentTreeFactory implements ResourceFactory { - - @Override - public SerializableTypeResolver createSerializableTypeResolver() { - return new AtomixDocumentTreeCommands.TypeResolver(); - } - - @Override - public ResourceStateMachine createStateMachine(Properties config) { - return new AtomixDocumentTreeState(config); - } - - @Override - public AtomixDocumentTree createInstance(CopycatClient client, Properties options) { - return new AtomixDocumentTree(client, options); - } - } \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeOperations.java similarity index 51% rename from core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeCommands.java rename to core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeOperations.java index 153dbcb0d9..f4213e289f 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeCommands.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeOperations.java @@ -16,34 +16,66 @@ package org.onosproject.store.primitives.resources.impl; -import io.atomix.catalyst.buffer.BufferInput; -import io.atomix.catalyst.buffer.BufferOutput; -import io.atomix.catalyst.serializer.CatalystSerializable; -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.catalyst.serializer.Serializer; -import io.atomix.catalyst.serializer.SerializerRegistry; -import io.atomix.copycat.Command; -import io.atomix.copycat.Query; - -import java.util.Map; import java.util.Optional; +import com.google.common.base.MoreObjects; +import io.atomix.protocols.raft.operation.OperationId; +import io.atomix.protocols.raft.operation.OperationType; +import org.onlab.util.KryoNamespace; import org.onlab.util.Match; +import org.onosproject.store.serializers.KryoNamespaces; import org.onosproject.store.service.DocumentPath; import org.onosproject.store.service.Versioned; -import com.google.common.base.MoreObjects; - /** * {@link AtomixDocumentTree} resource state machine operations. */ -public class AtomixDocumentTreeCommands { +public enum AtomixDocumentTreeOperations implements OperationId { + ADD_LISTENER("set", OperationType.COMMAND), + REMOVE_LISTENER("compareAndSet", OperationType.COMMAND), + GET("incrementAndGet", OperationType.QUERY), + GET_CHILDREN("getAndIncrement", OperationType.QUERY), + UPDATE("addAndGet", OperationType.COMMAND), + CLEAR("getAndAdd", OperationType.COMMAND); + + private final String id; + private final OperationType type; + + AtomixDocumentTreeOperations(String id, OperationType type) { + this.id = id; + this.type = type; + } + + @Override + public String id() { + return id; + } + + @Override + public OperationType type() { + return type; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID) + .register(Listen.class) + .register(Unlisten.class) + .register(Get.class) + .register(GetChildren.class) + .register(Update.class) + .register(DocumentPath.class) + .register(Match.class) + .register(Versioned.class) + .register(DocumentTreeUpdateResult.class) + .register(DocumentTreeUpdateResult.Status.class) + .build("AtomixDocumentTreeOperations"); /** - * Abstract DocumentTree operation. + * Abstract DocumentTree command. */ - public abstract static class DocumentTreeOperation implements CatalystSerializable { - + @SuppressWarnings("serial") + public abstract static class DocumentTreeOperation { private DocumentPath path; DocumentTreeOperation(DocumentPath path) { @@ -53,50 +85,13 @@ public class AtomixDocumentTreeCommands { public DocumentPath path() { return path; } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - serializer.writeObject(path, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - path = serializer.readObject(buffer); - } - } - - /** - * Abstract DocumentTree query. - */ - @SuppressWarnings("serial") - public abstract static class DocumentTreeQuery extends DocumentTreeOperation implements Query { - - DocumentTreeQuery(DocumentPath path) { - super(path); - } - - @Override - public ConsistencyLevel consistency() { - return ConsistencyLevel.SEQUENTIAL; - } - } - - /** - * Abstract DocumentTree command. - */ - @SuppressWarnings("serial") - public abstract static class DocumentTreeCommand extends DocumentTreeOperation implements Command { - - DocumentTreeCommand(DocumentPath path) { - super(path); - } } /** * DocumentTree#get query. */ @SuppressWarnings("serial") - public static class Get extends DocumentTreeQuery> { + public static class Get extends DocumentTreeOperation { public Get() { super(null); } @@ -117,7 +112,7 @@ public class AtomixDocumentTreeCommands { * DocumentTree#getChildren query. */ @SuppressWarnings("serial") - public static class GetChildren extends DocumentTreeQuery>> { + public static class GetChildren extends DocumentTreeOperation { public GetChildren() { super(null); } @@ -138,8 +133,7 @@ public class AtomixDocumentTreeCommands { * DocumentTree update command. */ @SuppressWarnings("serial") - public static class Update extends DocumentTreeCommand> { - + public static class Update extends DocumentTreeOperation { private Optional value; private Match valueMatch; private Match versionMatch; @@ -170,27 +164,6 @@ public class AtomixDocumentTreeCommands { return versionMatch; } - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(value, buffer); - serializer.writeObject(valueMatch, buffer); - serializer.writeObject(versionMatch, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - value = serializer.readObject(buffer); - valueMatch = serializer.readObject(buffer); - versionMatch = serializer.readObject(buffer); - } - - @Override - public CompactionMode compaction() { - return value == null ? CompactionMode.TOMBSTONE : CompactionMode.QUORUM; - } - @Override public String toString() { return MoreObjects.toStringHelper(getClass()) @@ -202,31 +175,11 @@ public class AtomixDocumentTreeCommands { } } - /** - * Clear command. - */ - @SuppressWarnings("serial") - public static class Clear implements Command, CatalystSerializable { - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - } - /** * Change listen. */ @SuppressWarnings("serial") - public static class Listen extends DocumentTreeCommand { - + public static class Listen extends DocumentTreeOperation { public Listen() { this(DocumentPath.from("root")); } @@ -235,11 +188,6 @@ public class AtomixDocumentTreeCommands { super(path); } - @Override - public CompactionMode compaction() { - return CompactionMode.QUORUM; - } - @Override public String toString() { return MoreObjects.toStringHelper(getClass()) @@ -252,8 +200,7 @@ public class AtomixDocumentTreeCommands { * Change unlisten. */ @SuppressWarnings("serial") - public static class Unlisten extends DocumentTreeCommand { - + public static class Unlisten extends DocumentTreeOperation { public Unlisten() { this(DocumentPath.from("root")); } @@ -262,11 +209,6 @@ public class AtomixDocumentTreeCommands { super(path); } - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - @Override public String toString() { return MoreObjects.toStringHelper(getClass()) @@ -274,19 +216,4 @@ public class AtomixDocumentTreeCommands { .toString(); } } - - /** - * DocumentTree command type resolver. - */ - public static class TypeResolver implements SerializableTypeResolver { - @Override - public void resolve(SerializerRegistry registry) { - registry.register(Get.class, -911); - registry.register(GetChildren.class, -912); - registry.register(Update.class, -913); - registry.register(Listen.class, -914); - registry.register(Unlisten.class, -915); - registry.register(Clear.class, -916); - } - } } diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeService.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeService.java new file mode 100644 index 0000000000..6b7c550bd0 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeService.java @@ -0,0 +1,306 @@ +/* + * Copyright 2016-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.store.primitives.resources.impl; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Queue; +import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import com.google.common.collect.Queues; +import io.atomix.protocols.raft.event.EventType; +import io.atomix.protocols.raft.service.AbstractRaftService; +import io.atomix.protocols.raft.service.Commit; +import io.atomix.protocols.raft.service.RaftServiceExecutor; +import io.atomix.protocols.raft.session.RaftSession; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import org.onlab.util.KryoNamespace; +import org.onlab.util.Match; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Get; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GetChildren; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Listen; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Unlisten; +import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.Update; +import org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.DocumentPath; +import org.onosproject.store.service.DocumentTree; +import org.onosproject.store.service.DocumentTreeEvent; +import org.onosproject.store.service.DocumentTreeEvent.Type; +import org.onosproject.store.service.IllegalDocumentModificationException; +import org.onosproject.store.service.NoSuchDocumentPathException; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.Versioned; + +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeEvents.CHANGE; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.ADD_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.CLEAR; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GET_CHILDREN; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.REMOVE_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.UPDATE; + +/** + * State Machine for {@link AtomixDocumentTree} resource. + */ +public class AtomixDocumentTreeService extends AbstractRaftService { + private final Serializer serializer = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixDocumentTreeOperations.NAMESPACE) + .register(AtomixDocumentTreeEvents.NAMESPACE) + .register(new com.esotericsoftware.kryo.Serializer() { + @Override + public void write(Kryo kryo, Output output, Listener listener) { + output.writeLong(listener.session.sessionId().id()); + kryo.writeObject(output, listener.path); + } + + @Override + public Listener read(Kryo kryo, Input input, Class type) { + return new Listener(getSessions().getSession(input.readLong()), + kryo.readObjectOrNull(input, DocumentPath.class)); + } + }, Listener.class) + .register(Versioned.class) + .register(DocumentPath.class) + .register(new HashMap().keySet().getClass()) + .register(TreeMap.class) + .register(SessionListenCommits.class) + .register(new com.esotericsoftware.kryo.Serializer() { + @Override + public void write(Kryo kryo, Output output, DefaultDocumentTree object) { + kryo.writeObject(output, object.root); + } + + @Override + @SuppressWarnings("unchecked") + public DefaultDocumentTree read(Kryo kryo, Input input, Class type) { + return new DefaultDocumentTree(versionCounter::incrementAndGet, + kryo.readObject(input, DefaultDocumentTreeNode.class)); + } + }, DefaultDocumentTree.class) + .register(DefaultDocumentTreeNode.class) + .build()); + + private Map listeners = new HashMap<>(); + private AtomicLong versionCounter = new AtomicLong(0); + private DocumentTree docTree = new DefaultDocumentTree<>(versionCounter::incrementAndGet); + + @Override + public void snapshot(SnapshotWriter writer) { + writer.writeLong(versionCounter.get()); + writer.writeObject(listeners, serializer::encode); + writer.writeObject(docTree, serializer::encode); + } + + @Override + public void install(SnapshotReader reader) { + versionCounter = new AtomicLong(reader.readLong()); + listeners = reader.readObject(serializer::decode); + docTree = reader.readObject(serializer::decode); + } + + @Override + protected void configure(RaftServiceExecutor executor) { + // Listeners + executor.register(ADD_LISTENER, serializer::decode, this::listen); + executor.register(REMOVE_LISTENER, serializer::decode, this::unlisten); + // queries + executor.register(GET, serializer::decode, this::get, serializer::encode); + executor.register(GET_CHILDREN, serializer::decode, this::getChildren, serializer::encode); + // commands + executor.register(UPDATE, serializer::decode, this::update, serializer::encode); + executor.register(CLEAR, this::clear); + } + + protected void listen(Commit commit) { + Long sessionId = commit.session().sessionId().id(); + listeners.computeIfAbsent(sessionId, k -> new SessionListenCommits()) + .add(new Listener(commit.session(), commit.value().path())); + } + + protected void unlisten(Commit commit) { + Long sessionId = commit.session().sessionId().id(); + SessionListenCommits listenCommits = listeners.get(sessionId); + if (listenCommits != null) { + listenCommits.remove(commit); + } + } + + protected Versioned get(Commit commit) { + try { + Versioned value = docTree.get(commit.value().path()); + return value == null ? null : value.map(node -> node == null ? null : node); + } catch (IllegalStateException e) { + return null; + } + } + + protected Map> getChildren(Commit commit) { + return docTree.getChildren(commit.value().path()); + } + + protected DocumentTreeUpdateResult update(Commit commit) { + DocumentTreeUpdateResult result = null; + DocumentPath path = commit.value().path(); + boolean updated = false; + Versioned currentValue = docTree.get(path); + try { + Match versionMatch = commit.value().versionMatch(); + Match valueMatch = commit.value().valueMatch(); + + if (versionMatch.matches(currentValue == null ? null : currentValue.version()) + && valueMatch.matches(currentValue == null ? null : currentValue.value())) { + if (commit.value().value() == null) { + docTree.removeNode(path); + } else { + docTree.set(path, commit.value().value().orElse(null)); + } + updated = true; + } + Versioned newValue = updated ? docTree.get(path) : currentValue; + Status updateStatus = updated + ? Status.OK : commit.value().value() == null ? Status.INVALID_PATH : Status.NOOP; + result = new DocumentTreeUpdateResult<>(path, updateStatus, newValue, currentValue); + } catch (IllegalDocumentModificationException e) { + result = DocumentTreeUpdateResult.illegalModification(path); + } catch (NoSuchDocumentPathException e) { + result = DocumentTreeUpdateResult.invalidPath(path); + } catch (Exception e) { + getLogger().error("Failed to apply {} to state machine", commit.value(), e); + throw Throwables.propagate(e); + } + notifyListeners(path, result); + return result; + } + + protected void clear(Commit commit) { + Queue toClearQueue = Queues.newArrayDeque(); + Map> topLevelChildren = docTree.getChildren(DocumentPath.from("root")); + toClearQueue.addAll(topLevelChildren.keySet() + .stream() + .map(name -> new DocumentPath(name, DocumentPath.from("root"))) + .collect(Collectors.toList())); + while (!toClearQueue.isEmpty()) { + DocumentPath path = toClearQueue.remove(); + Map> children = docTree.getChildren(path); + if (children.size() == 0) { + docTree.removeNode(path); + } else { + children.keySet().forEach(name -> toClearQueue.add(new DocumentPath(name, path))); + toClearQueue.add(path); + } + } + } + + private void notifyListeners(DocumentPath path, DocumentTreeUpdateResult result) { + if (result.status() != Status.OK) { + return; + } + DocumentTreeEvent event = + new DocumentTreeEvent<>(path, + result.created() ? Type.CREATED : result.newValue() == null ? Type.DELETED : Type.UPDATED, + Optional.ofNullable(result.newValue()), + Optional.ofNullable(result.oldValue())); + + listeners.values() + .stream() + .filter(l -> event.path().isDescendentOf(l.leastCommonAncestorPath())) + .forEach(listener -> listener.publish(CHANGE, Arrays.asList(event))); + } + + @Override + public void onExpire(RaftSession session) { + closeListener(session.sessionId().id()); + } + + @Override + public void onClose(RaftSession session) { + closeListener(session.sessionId().id()); + } + + private void closeListener(Long sessionId) { + listeners.remove(sessionId); + } + + private class SessionListenCommits { + private final List listeners = Lists.newArrayList(); + private DocumentPath leastCommonAncestorPath; + + public void add(Listener listener) { + listeners.add(listener); + recomputeLeastCommonAncestor(); + } + + public void remove(Commit commit) { + // Remove the first listen commit with path matching path in unlisten commit + Iterator iterator = listeners.iterator(); + while (iterator.hasNext()) { + Listener listener = iterator.next(); + if (listener.path().equals(commit.value().path())) { + iterator.remove(); + } + } + recomputeLeastCommonAncestor(); + } + + public DocumentPath leastCommonAncestorPath() { + return leastCommonAncestorPath; + } + + public void publish(EventType topic, M message) { + listeners.stream().findAny().ifPresent(listener -> + listener.session().publish(topic, serializer::encode, message)); + } + + private void recomputeLeastCommonAncestor() { + this.leastCommonAncestorPath = DocumentPath.leastCommonAncestor(listeners.stream() + .map(Listener::path) + .collect(Collectors.toList())); + } + } + + private static class Listener { + private final RaftSession session; + private final DocumentPath path; + + public Listener(RaftSession session, DocumentPath path) { + this.session = session; + this.path = path; + } + + public DocumentPath path() { + return path; + } + + public RaftSession session() { + return session; + } + } +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeState.java deleted file mode 100644 index 8a8a23cb6b..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeState.java +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.onosproject.store.primitives.resources.impl; - -import static org.slf4j.LoggerFactory.getLogger; -import io.atomix.copycat.server.Commit; -import io.atomix.copycat.server.Snapshottable; -import io.atomix.copycat.server.StateMachineExecutor; -import io.atomix.copycat.server.session.ServerSession; -import io.atomix.copycat.server.session.SessionListener; -import io.atomix.copycat.server.storage.snapshot.SnapshotReader; -import io.atomix.copycat.server.storage.snapshot.SnapshotWriter; -import io.atomix.resource.ResourceStateMachine; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.Queue; -import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Collectors; - -import org.onlab.util.Match; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Clear; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Get; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.GetChildren; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Listen; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Unlisten; -import org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeCommands.Update; -import org.onosproject.store.primitives.resources.impl.DocumentTreeUpdateResult.Status; -import org.onosproject.store.service.DocumentPath; -import org.onosproject.store.service.DocumentTree; -import org.onosproject.store.service.DocumentTreeEvent; -import org.onosproject.store.service.DocumentTreeEvent.Type; -import org.onosproject.store.service.IllegalDocumentModificationException; -import org.onosproject.store.service.NoSuchDocumentPathException; -import org.onosproject.store.service.Versioned; -import org.slf4j.Logger; - -import com.google.common.base.Throwables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Queues; - -/** - * State Machine for {@link AtomixDocumentTree} resource. - */ -public class AtomixDocumentTreeState - extends ResourceStateMachine - implements SessionListener, Snapshottable { - - private final Logger log = getLogger(getClass()); - private final Map listeners = new HashMap<>(); - private AtomicLong versionCounter = new AtomicLong(0); - private final DocumentTree docTree = new DefaultDocumentTree<>(versionCounter::incrementAndGet); - - public AtomixDocumentTreeState(Properties properties) { - super(properties); - } - - @Override - public void snapshot(SnapshotWriter writer) { - writer.writeLong(versionCounter.get()); - } - - @Override - public void install(SnapshotReader reader) { - versionCounter = new AtomicLong(reader.readLong()); - } - - @Override - protected void configure(StateMachineExecutor executor) { - // Listeners - executor.register(Listen.class, this::listen); - executor.register(Unlisten.class, this::unlisten); - // queries - executor.register(Get.class, this::get); - executor.register(GetChildren.class, this::getChildren); - // commands - executor.register(Update.class, this::update); - executor.register(Clear.class, this::clear); - } - - protected void listen(Commit commit) { - Long sessionId = commit.session().id(); - listeners.computeIfAbsent(sessionId, k -> new SessionListenCommits()).add(commit); - commit.session().onStateChange( - state -> { - if (state == ServerSession.State.CLOSED - || state == ServerSession.State.EXPIRED) { - closeListener(commit.session().id()); - } - }); - } - - protected void unlisten(Commit commit) { - Long sessionId = commit.session().id(); - try { - SessionListenCommits listenCommits = listeners.get(sessionId); - if (listenCommits != null) { - listenCommits.remove(commit); - } - } finally { - commit.close(); - } - } - - protected Versioned get(Commit commit) { - try { - Versioned value = docTree.get(commit.operation().path()); - return value == null ? null : value.map(node -> node == null ? null : node.value()); - } catch (IllegalStateException e) { - return null; - } finally { - commit.close(); - } - } - - protected Map> getChildren(Commit commit) { - try { - Map> children = docTree.getChildren(commit.operation().path()); - return children == null - ? null : Maps.newHashMap(Maps.transformValues(children, - value -> value.map(TreeNodeValue::value))); - } finally { - commit.close(); - } - } - - protected DocumentTreeUpdateResult update(Commit commit) { - DocumentTreeUpdateResult result = null; - DocumentPath path = commit.operation().path(); - boolean updated = false; - Versioned currentValue = docTree.get(path); - try { - Match versionMatch = commit.operation().versionMatch(); - Match valueMatch = commit.operation().valueMatch(); - - if (versionMatch.matches(currentValue == null ? null : currentValue.version()) - && valueMatch.matches(currentValue == null ? null : currentValue.value().value())) { - if (commit.operation().value() == null) { - docTree.removeNode(path); - } else { - docTree.set(path, new NonTransactionalCommit(commit)); - } - updated = true; - } - Versioned newValue = updated ? docTree.get(path) : currentValue; - Status updateStatus = updated - ? Status.OK : commit.operation().value() == null ? Status.INVALID_PATH : Status.NOOP; - result = new DocumentTreeUpdateResult<>(path, - updateStatus, - newValue == null - ? null : newValue.map(TreeNodeValue::value), - currentValue == null - ? null : currentValue.map(TreeNodeValue::value)); - } catch (IllegalDocumentModificationException e) { - result = DocumentTreeUpdateResult.illegalModification(path); - } catch (NoSuchDocumentPathException e) { - result = DocumentTreeUpdateResult.invalidPath(path); - } catch (Exception e) { - log.error("Failed to apply {} to state machine", commit.operation(), e); - throw Throwables.propagate(e); - } finally { - if (updated) { - if (currentValue != null) { - currentValue.value().discard(); - } - } else { - commit.close(); - } - } - notifyListeners(path, result); - return result; - } - - protected void clear(Commit commit) { - try { - Queue toClearQueue = Queues.newArrayDeque(); - Map> topLevelChildren = docTree.getChildren(DocumentPath.from("root")); - toClearQueue.addAll(topLevelChildren.keySet() - .stream() - .map(name -> new DocumentPath(name, DocumentPath.from("root"))) - .collect(Collectors.toList())); - while (!toClearQueue.isEmpty()) { - DocumentPath path = toClearQueue.remove(); - Map> children = docTree.getChildren(path); - if (children.size() == 0) { - docTree.removeNode(path).value().discard(); - } else { - children.keySet() - .stream() - .forEach(name -> toClearQueue.add(new DocumentPath(name, path))); - toClearQueue.add(path); - } - } - } finally { - commit.close(); - } - } - - /** - * Interface implemented by tree node values. - */ - private interface TreeNodeValue { - /** - * Returns the raw {@code byte[]}. - * - * @return raw value - */ - byte[] value(); - - /** - * Discards the value by invoke appropriate clean up actions. - */ - void discard(); - } - - /** - * A {@code TreeNodeValue} that is derived from a non-transactional update - * i.e. via any standard tree update operation. - */ - private class NonTransactionalCommit implements TreeNodeValue { - private final Commit commit; - - public NonTransactionalCommit(Commit commit) { - this.commit = commit; - } - - @Override - public byte[] value() { - return commit.operation().value().orElse(null); - } - - @Override - public void discard() { - commit.close(); - } - } - - private void notifyListeners(DocumentPath path, DocumentTreeUpdateResult result) { - if (result.status() != Status.OK) { - return; - } - DocumentTreeEvent event = - new DocumentTreeEvent<>(path, - result.created() ? Type.CREATED : result.newValue() == null ? Type.DELETED : Type.UPDATED, - Optional.ofNullable(result.newValue()), - Optional.ofNullable(result.oldValue())); - - listeners.values() - .stream() - .filter(l -> event.path().isDescendentOf(l.leastCommonAncestorPath())) - .forEach(listener -> listener.publish(AtomixDocumentTree.CHANGE_SUBJECT, Arrays.asList(event))); - } - - @Override - public void register(ServerSession session) { - } - - @Override - public void unregister(ServerSession session) { - closeListener(session.id()); - } - - @Override - public void expire(ServerSession session) { - closeListener(session.id()); - } - - @Override - public void close(ServerSession session) { - closeListener(session.id()); - } - - private void closeListener(Long sessionId) { - SessionListenCommits listenCommits = listeners.remove(sessionId); - if (listenCommits != null) { - listenCommits.close(); - } - } - - private class SessionListenCommits { - private final List> commits = Lists.newArrayList(); - private DocumentPath leastCommonAncestorPath; - - public void add(Commit commit) { - commits.add(commit); - recomputeLeastCommonAncestor(); - } - - public void remove(Commit commit) { - // Remove the first listen commit with path matching path in unlisten commit - Iterator> iterator = commits.iterator(); - while (iterator.hasNext()) { - Commit listenCommit = iterator.next(); - if (listenCommit.operation().path().equals(commit.operation().path())) { - iterator.remove(); - listenCommit.close(); - } - } - recomputeLeastCommonAncestor(); - } - - public DocumentPath leastCommonAncestorPath() { - return leastCommonAncestorPath; - } - - public void publish(String topic, M message) { - commits.stream().findAny().ifPresent(commit -> commit.session().publish(topic, message)); - } - - public void close() { - commits.forEach(Commit::close); - commits.clear(); - leastCommonAncestorPath = null; - } - - private void recomputeLeastCommonAncestor() { - this.leastCommonAncestorPath = DocumentPath.leastCommonAncestor(commits.stream() - .map(c -> c.operation().path()) - .collect(Collectors.toList())); - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixIdGenerator.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixIdGenerator.java index 971b60cf04..7d487f4b5d 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixIdGenerator.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixIdGenerator.java @@ -18,36 +18,34 @@ package org.onosproject.store.primitives.resources.impl; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicLong; -import io.atomix.variables.DistributedLong; +import org.onosproject.store.service.AsyncAtomicCounter; import org.onosproject.store.service.AsyncAtomicIdGenerator; /** * {@code AsyncAtomicIdGenerator} implementation backed by Atomix - * {@link DistributedLong}. + * {@link AsyncAtomicCounter}. */ public class AtomixIdGenerator implements AsyncAtomicIdGenerator { private static final long DEFAULT_BATCH_SIZE = 1000; - private final String name; - private final DistributedLong distLong; + private final AsyncAtomicCounter counter; private final long batchSize; private CompletableFuture reserveFuture; private long base; private final AtomicLong delta = new AtomicLong(); - public AtomixIdGenerator(String name, DistributedLong distLong) { - this(name, distLong, DEFAULT_BATCH_SIZE); + public AtomixIdGenerator(AsyncAtomicCounter counter) { + this(counter, DEFAULT_BATCH_SIZE); } - AtomixIdGenerator(String name, DistributedLong distLong, long batchSize) { - this.name = name; - this.distLong = distLong; + AtomixIdGenerator(AsyncAtomicCounter counter, long batchSize) { + this.counter = counter; this.batchSize = batchSize; } @Override public String name() { - return name; + return counter.name(); } @Override @@ -64,9 +62,9 @@ public class AtomixIdGenerator implements AsyncAtomicIdGenerator { private CompletableFuture reserve() { if (reserveFuture == null || reserveFuture.isDone()) { - reserveFuture = distLong.getAndAdd(batchSize); + reserveFuture = counter.getAndAdd(batchSize); } else { - reserveFuture = reserveFuture.thenCompose(v -> distLong.getAndAdd(batchSize)); + reserveFuture = reserveFuture.thenCompose(v -> counter.getAndAdd(batchSize)); } reserveFuture = reserveFuture.thenApply(base -> { this.base = base; diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElector.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElector.java index 3e4509154a..825fa9877d 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElector.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElector.java @@ -15,73 +15,65 @@ */ package org.onosproject.store.primitives.resources.impl; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.AbstractResource; -import io.atomix.resource.ResourceTypeInfo; - -import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import java.util.function.Function; -import org.onosproject.cluster.Leadership; -import org.onosproject.cluster.NodeId; -import org.onosproject.event.Change; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Anoint; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.GetAllLeaderships; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.GetElectedTopics; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.GetLeadership; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Listen; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Promote; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Run; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Unlisten; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Withdraw; -import org.onosproject.store.service.AsyncLeaderElector; - -import com.google.common.collect.ImmutableSet; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Sets; +import io.atomix.protocols.raft.proxy.RaftProxy; +import org.onlab.util.KryoNamespace; +import org.onosproject.cluster.Leadership; +import org.onosproject.cluster.NodeId; +import org.onosproject.event.Change; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Anoint; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GetElectedTopics; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GetLeadership; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Promote; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Run; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Withdraw; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.AsyncLeaderElector; +import org.onosproject.store.service.Serializer; + +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorEvents.CHANGE; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.ADD_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.ANOINT; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.EVICT; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_ALL_LEADERSHIPS; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_ELECTED_TOPICS; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_LEADERSHIP; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.PROMOTE; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.REMOVE_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.RUN; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.WITHDRAW; /** * Distributed resource providing the {@link AsyncLeaderElector} primitive. */ -@ResourceTypeInfo(id = -152, factory = AtomixLeaderElectorFactory.class) -public class AtomixLeaderElector extends AbstractResource - implements AsyncLeaderElector { - private final Set> statusChangeListeners = - Sets.newCopyOnWriteArraySet(); - private final Set>> leadershipChangeListeners = - Sets.newCopyOnWriteArraySet(); +public class AtomixLeaderElector extends AbstractRaftPrimitive implements AsyncLeaderElector { + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.API) + .register(AtomixLeaderElectorOperations.NAMESPACE) + .register(AtomixLeaderElectorEvents.NAMESPACE) + .build()); + + private final Set>> leadershipChangeListeners = Sets.newCopyOnWriteArraySet(); private final Consumer> cacheUpdater; private final Consumer statusListener; - public static final String CHANGE_SUBJECT = "leadershipChangeEvents"; private final LoadingCache> cache; - Function mapper = state -> { - switch (state) { - case CONNECTED: - return Status.ACTIVE; - case SUSPENDED: - return Status.SUSPENDED; - case CLOSED: - return Status.INACTIVE; - default: - throw new IllegalStateException("Unknown state " + state); - } - }; - - public AtomixLeaderElector(CopycatClient client, Properties properties) { - super(client, properties); + public AtomixLeaderElector(RaftProxy proxy) { + super(proxy); cache = CacheBuilder.newBuilder() .maximumSize(1000) - .build(CacheLoader.from(topic -> this.client.submit(new GetLeadership(topic)))); + .build(CacheLoader.from(topic -> proxy.invoke( + GET_LEADERSHIP, SERIALIZER::encode, new GetLeadership(topic), SERIALIZER::decode))); cacheUpdater = change -> { Leadership leadership = change.newValue(); @@ -93,7 +85,13 @@ public class AtomixLeaderElector extends AbstractResource } }; addStatusChangeListener(statusListener); - client.onStateChange(this::handleStateChange); + + proxy.addStateChangeListener(state -> { + if (state == RaftProxy.State.CONNECTED && isListening()) { + proxy.invoke(ADD_LISTENER); + } + }); + proxy.addEventListener(CHANGE, SERIALIZER::decode, this::handleEvent); } @Override @@ -102,24 +100,6 @@ public class AtomixLeaderElector extends AbstractResource return removeChangeListener(cacheUpdater); } - @Override - public String name() { - return null; - } - - @Override - public CompletableFuture open() { - return super.open().thenApply(result -> { - client.onStateChange(state -> { - if (state == CopycatClient.State.CONNECTED && isListening()) { - client.submit(new Listen()); - } - }); - client.onEvent(CHANGE_SUBJECT, this::handleEvent); - return result; - }); - } - public CompletableFuture setupCache() { return addChangeListener(cacheUpdater).thenApply(v -> this); } @@ -130,27 +110,32 @@ public class AtomixLeaderElector extends AbstractResource @Override public CompletableFuture run(String topic, NodeId nodeId) { - return client.submit(new Run(topic, nodeId)).whenComplete((r, e) -> cache.invalidate(topic)); + return proxy.invoke(RUN, SERIALIZER::encode, new Run(topic, nodeId), SERIALIZER::decode) + .whenComplete((r, e) -> cache.invalidate(topic)); } @Override public CompletableFuture withdraw(String topic) { - return client.submit(new Withdraw(topic)).whenComplete((r, e) -> cache.invalidate(topic)); + return proxy.invoke(WITHDRAW, SERIALIZER::encode, new Withdraw(topic)) + .whenComplete((r, e) -> cache.invalidate(topic)); } @Override public CompletableFuture anoint(String topic, NodeId nodeId) { - return client.submit(new Anoint(topic, nodeId)).whenComplete((r, e) -> cache.invalidate(topic)); + return proxy.invoke(ANOINT, SERIALIZER::encode, new Anoint(topic, nodeId), SERIALIZER::decode) + .whenComplete((r, e) -> cache.invalidate(topic)); } @Override public CompletableFuture promote(String topic, NodeId nodeId) { - return client.submit(new Promote(topic, nodeId)).whenComplete((r, e) -> cache.invalidate(topic)); + return proxy.invoke( + PROMOTE, SERIALIZER::encode, new Promote(topic, nodeId), SERIALIZER::decode) + .whenComplete((r, e) -> cache.invalidate(topic)); } @Override public CompletableFuture evict(NodeId nodeId) { - return client.submit(new AtomixLeaderElectorCommands.Evict(nodeId)); + return proxy.invoke(EVICT, SERIALIZER::encode, new AtomixLeaderElectorOperations.Evict(nodeId)); } @Override @@ -165,17 +150,17 @@ public class AtomixLeaderElector extends AbstractResource @Override public CompletableFuture> getLeaderships() { - return client.submit(new GetAllLeaderships()); + return proxy.invoke(GET_ALL_LEADERSHIPS, SERIALIZER::decode); } public CompletableFuture> getElectedTopics(NodeId nodeId) { - return client.submit(new GetElectedTopics(nodeId)); + return proxy.invoke(GET_ELECTED_TOPICS, SERIALIZER::encode, new GetElectedTopics(nodeId), SERIALIZER::decode); } @Override public synchronized CompletableFuture addChangeListener(Consumer> consumer) { if (leadershipChangeListeners.isEmpty()) { - return client.submit(new Listen()).thenRun(() -> leadershipChangeListeners.add(consumer)); + return proxy.invoke(ADD_LISTENER).thenRun(() -> leadershipChangeListeners.add(consumer)); } else { leadershipChangeListeners.add(consumer); return CompletableFuture.completedFuture(null); @@ -185,31 +170,12 @@ public class AtomixLeaderElector extends AbstractResource @Override public synchronized CompletableFuture removeChangeListener(Consumer> consumer) { if (leadershipChangeListeners.remove(consumer) && leadershipChangeListeners.isEmpty()) { - return client.submit(new Unlisten()).thenApply(v -> null); + return proxy.invoke(REMOVE_LISTENER).thenApply(v -> null); } return CompletableFuture.completedFuture(null); } - @Override - public void addStatusChangeListener(Consumer listener) { - statusChangeListeners.add(listener); - } - - @Override - public void removeStatusChangeListener(Consumer listener) { - statusChangeListeners.remove(listener); - } - - @Override - public Collection> statusChangeListeners() { - return ImmutableSet.copyOf(statusChangeListeners); - } - private boolean isListening() { return !leadershipChangeListeners.isEmpty(); } - - private void handleStateChange(CopycatClient.State state) { - statusChangeListeners().forEach(listener -> listener.accept(mapper.apply(state))); - } -} +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorCommands.java deleted file mode 100644 index 87e6bcc15e..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorCommands.java +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.resources.impl; - -import java.util.Map; -import java.util.Set; - -import org.onosproject.cluster.Leadership; -import org.onosproject.cluster.NodeId; - - -import com.google.common.base.MoreObjects; -import com.google.common.base.Strings; - -import io.atomix.catalyst.buffer.BufferInput; -import io.atomix.catalyst.buffer.BufferOutput; -import io.atomix.catalyst.serializer.CatalystSerializable; -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.catalyst.serializer.Serializer; -import io.atomix.catalyst.serializer.SerializerRegistry; -import io.atomix.catalyst.util.Assert; -import io.atomix.copycat.Command; -import io.atomix.copycat.Query; - -/** - * {@link AtomixLeaderElector} resource state machine operations. - */ -public final class AtomixLeaderElectorCommands { - - private AtomixLeaderElectorCommands() { - } - - /** - * Abstract election query. - */ - @SuppressWarnings("serial") - public abstract static class ElectionQuery implements Query, CatalystSerializable { - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - } - - /** - * Abstract election topic query. - */ - @SuppressWarnings("serial") - public abstract static class TopicQuery extends ElectionQuery implements CatalystSerializable { - String topic; - - public TopicQuery() { - } - - public TopicQuery(String topic) { - this.topic = Assert.notNull(topic, "topic"); - } - - /** - * Returns the topic. - * @return topic - */ - public String topic() { - return topic; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - serializer.writeObject(topic, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - topic = serializer.readObject(buffer); - } - } - - /** - * Abstract election command. - */ - @SuppressWarnings("serial") - public abstract static class ElectionCommand implements Command, CatalystSerializable { - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - } - - /** - * Listen command. - */ - @SuppressWarnings("serial") - public static class Listen extends ElectionCommand { - @Override - public CompactionMode compaction() { - return CompactionMode.QUORUM; - } - } - - /** - * Unlisten command. - */ - @SuppressWarnings("serial") - public static class Unlisten extends ElectionCommand { - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - } - - /** - * GetLeader query. - */ - @SuppressWarnings("serial") - public static class GetLeadership extends TopicQuery { - - public GetLeadership() { - } - - public GetLeadership(String topic) { - super(topic); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("topic", topic) - .toString(); - } - } - - /** - * GetAllLeaders query. - */ - @SuppressWarnings("serial") - public static class GetAllLeaderships extends ElectionQuery> { - } - - /** - * GetElectedTopics query. - */ - @SuppressWarnings("serial") - public static class GetElectedTopics extends ElectionQuery> { - private NodeId nodeId; - - public GetElectedTopics() { - } - - public GetElectedTopics(NodeId nodeId) { - this.nodeId = Assert.argNot(nodeId, nodeId == null, "nodeId cannot be null"); - } - - /** - * Returns the nodeId to check. - * - * @return The nodeId to check. - */ - public NodeId nodeId() { - return nodeId; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("nodeId", nodeId) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - super.writeObject(buffer, serializer); - serializer.writeObject(nodeId, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - super.readObject(buffer, serializer); - nodeId = serializer.readObject(buffer); - } - } - - /** - * Enter and run for leadership. - */ - @SuppressWarnings("serial") - public static class Run extends ElectionCommand { - private String topic; - private NodeId nodeId; - - public Run() { - } - - public Run(String topic, NodeId nodeId) { - this.topic = Assert.argNot(topic, Strings.isNullOrEmpty(topic), "topic cannot be null or empty"); - this.nodeId = Assert.argNot(nodeId, nodeId == null, "nodeId cannot be null"); - } - - /** - * Returns the topic. - * - * @return topic - */ - public String topic() { - return topic; - } - - /** - * Returns the nodeId. - * - * @return the nodeId - */ - public NodeId nodeId() { - return nodeId; - } - - @Override - public CompactionMode compaction() { - return CompactionMode.SNAPSHOT; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("topic", topic) - .add("nodeId", nodeId) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - buffer.writeString(topic); - buffer.writeString(nodeId.toString()); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - topic = buffer.readString(); - nodeId = new NodeId(buffer.readString()); - } - } - - /** - * Withdraw from a leadership contest. - */ - @SuppressWarnings("serial") - public static class Withdraw extends ElectionCommand { - private String topic; - - public Withdraw() { - } - - public Withdraw(String topic) { - this.topic = Assert.argNot(topic, Strings.isNullOrEmpty(topic), "topic cannot be null or empty"); - } - - /** - * Returns the topic. - * - * @return The topic - */ - public String topic() { - return topic; - } - - @Override - public CompactionMode compaction() { - return CompactionMode.SNAPSHOT; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("topic", topic) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - buffer.writeString(topic); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - topic = buffer.readString(); - } - } - - /** - * Command for administratively changing the leadership state for a node. - */ - @SuppressWarnings("serial") - public abstract static class ElectionChangeCommand extends ElectionCommand { - private String topic; - private NodeId nodeId; - - ElectionChangeCommand() { - topic = null; - nodeId = null; - } - - public ElectionChangeCommand(String topic, NodeId nodeId) { - this.topic = topic; - this.nodeId = nodeId; - } - - /** - * Returns the topic. - * - * @return The topic - */ - public String topic() { - return topic; - } - - /** - * Returns the nodeId to make leader. - * - * @return The nodeId - */ - public NodeId nodeId() { - return nodeId; - } - - @Override - public CompactionMode compaction() { - return CompactionMode.SNAPSHOT; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("topic", topic) - .add("nodeId", nodeId) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - buffer.writeString(topic); - buffer.writeString(nodeId.toString()); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - topic = buffer.readString(); - nodeId = new NodeId(buffer.readString()); - } - } - - /** - * Command for administratively anoint a node as leader. - */ - @SuppressWarnings("serial") - public static class Anoint extends ElectionChangeCommand { - - private Anoint() { - } - - public Anoint(String topic, NodeId nodeId) { - super(topic, nodeId); - } - } - - /** - * Command for administratively promote a node as top candidate. - */ - @SuppressWarnings("serial") - public static class Promote extends ElectionChangeCommand { - - private Promote() { - } - - public Promote(String topic, NodeId nodeId) { - super(topic, nodeId); - } - } - - /** - * Command for administratively evicting a node from all leadership topics. - */ - @SuppressWarnings("serial") - public static class Evict extends ElectionCommand { - private NodeId nodeId; - - public Evict() { - } - - public Evict(NodeId nodeId) { - this.nodeId = nodeId; - } - - /** - * Returns the node identifier. - * - * @return The nodeId - */ - public NodeId nodeId() { - return nodeId; - } - - @Override - public CompactionMode compaction() { - return CompactionMode.SNAPSHOT; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("nodeId", nodeId) - .toString(); - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - buffer.writeString(nodeId.toString()); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - nodeId = new NodeId(buffer.readString()); - } - } - - /** - * Map command type resolver. - */ - public static class TypeResolver implements SerializableTypeResolver { - @Override - public void resolve(SerializerRegistry registry) { - registry.register(Run.class, -861); - registry.register(Withdraw.class, -862); - registry.register(Anoint.class, -863); - registry.register(GetAllLeaderships.class, -864); - registry.register(GetElectedTopics.class, -865); - registry.register(GetLeadership.class, -866); - registry.register(Listen.class, -867); - registry.register(Unlisten.class, -868); - registry.register(Promote.class, -869); - registry.register(Evict.class, -870); - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorEvents.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorEvents.java new file mode 100644 index 0000000000..b6ba8a5595 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorEvents.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.event.EventType; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.serializers.KryoNamespaces; + +/** + * Atomix leader elector events. + */ +public enum AtomixLeaderElectorEvents implements EventType { + CHANGE("change"); + + private final String id; + + AtomixLeaderElectorEvents(String id) { + this.id = id; + } + + @Override + public String id() { + return id; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 50) + .build("AtomixLeaderElectorEvents"); +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorFactory.java deleted file mode 100644 index 22f23b5106..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.resources.impl; - -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.ResourceFactory; -import io.atomix.resource.ResourceStateMachine; - -import java.util.Properties; - -/** - * {@link AtomixLeaderElector} resource factory. - * - */ -public class AtomixLeaderElectorFactory implements ResourceFactory { - - @Override - public SerializableTypeResolver createSerializableTypeResolver() { - return new AtomixLeaderElectorCommands.TypeResolver(); - } - - @Override - public ResourceStateMachine createStateMachine(Properties config) { - return new AtomixLeaderElectorState(config); - } - - @Override - public AtomixLeaderElector createInstance(CopycatClient client, Properties options) { - return new AtomixLeaderElector(client, options); - } -} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorOperations.java new file mode 100644 index 0000000000..17b8c239e0 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorOperations.java @@ -0,0 +1,333 @@ +/* + * Copyright 2016-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import com.google.common.base.MoreObjects; +import io.atomix.protocols.raft.operation.OperationId; +import io.atomix.protocols.raft.operation.OperationType; +import org.onlab.util.KryoNamespace; +import org.onosproject.cluster.NodeId; +import org.onosproject.store.serializers.KryoNamespaces; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * {@link AtomixLeaderElector} resource state machine operations. + */ +public enum AtomixLeaderElectorOperations implements OperationId { + ADD_LISTENER("addListener", OperationType.COMMAND), + REMOVE_LISTENER("removeListener", OperationType.COMMAND), + RUN("run", OperationType.COMMAND), + WITHDRAW("withdraw", OperationType.COMMAND), + ANOINT("anoint", OperationType.COMMAND), + PROMOTE("promote", OperationType.COMMAND), + EVICT("evict", OperationType.COMMAND), + GET_LEADERSHIP("getLeadership", OperationType.QUERY), + GET_ALL_LEADERSHIPS("getAllLeaderships", OperationType.QUERY), + GET_ELECTED_TOPICS("getElectedTopics", OperationType.QUERY); + + private final String id; + private final OperationType type; + + AtomixLeaderElectorOperations(String id, OperationType type) { + this.id = id; + this.type = type; + } + + @Override + public String id() { + return id; + } + + @Override + public OperationType type() { + return type; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .register(KryoNamespaces.API) + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID) + .register(Run.class) + .register(Withdraw.class) + .register(Anoint.class) + .register(Promote.class) + .register(Evict.class) + .register(GetLeadership.class) + .register(GetElectedTopics.class) + .build("AtomixLeaderElectorOperations"); + + /** + * Abstract election query. + */ + @SuppressWarnings("serial") + public abstract static class ElectionOperation { + } + + /** + * Abstract election topic query. + */ + @SuppressWarnings("serial") + public abstract static class TopicOperation extends ElectionOperation { + String topic; + + public TopicOperation() { + } + + public TopicOperation(String topic) { + this.topic = checkNotNull(topic); + } + + /** + * Returns the topic. + * @return topic + */ + public String topic() { + return topic; + } + } + + /** + * GetLeader query. + */ + @SuppressWarnings("serial") + public static class GetLeadership extends TopicOperation { + + public GetLeadership() { + } + + public GetLeadership(String topic) { + super(topic); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("topic", topic) + .toString(); + } + } + + /** + * GetElectedTopics query. + */ + @SuppressWarnings("serial") + public static class GetElectedTopics extends ElectionOperation { + private NodeId nodeId; + + public GetElectedTopics() { + } + + public GetElectedTopics(NodeId nodeId) { + checkArgument(nodeId != null, "nodeId cannot be null"); + this.nodeId = nodeId; + } + + /** + * Returns the nodeId to check. + * + * @return The nodeId to check. + */ + public NodeId nodeId() { + return nodeId; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("nodeId", nodeId) + .toString(); + } + } + + /** + * Enter and run for leadership. + */ + @SuppressWarnings("serial") + public static class Run extends ElectionOperation { + private String topic; + private NodeId nodeId; + + public Run() { + } + + public Run(String topic, NodeId nodeId) { + this.topic = topic; + this.nodeId = nodeId; + } + + /** + * Returns the topic. + * + * @return topic + */ + public String topic() { + return topic; + } + + /** + * Returns the nodeId. + * + * @return the nodeId + */ + public NodeId nodeId() { + return nodeId; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("topic", topic) + .add("nodeId", nodeId) + .toString(); + } + } + + /** + * Withdraw from a leadership contest. + */ + @SuppressWarnings("serial") + public static class Withdraw extends ElectionOperation { + private String topic; + + public Withdraw() { + } + + public Withdraw(String topic) { + this.topic = topic; + } + + /** + * Returns the topic. + * + * @return The topic + */ + public String topic() { + return topic; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("topic", topic) + .toString(); + } + } + + /** + * Command for administratively changing the leadership state for a node. + */ + @SuppressWarnings("serial") + public abstract static class ElectionChangeOperation extends ElectionOperation { + private String topic; + private NodeId nodeId; + + ElectionChangeOperation() { + topic = null; + nodeId = null; + } + + public ElectionChangeOperation(String topic, NodeId nodeId) { + this.topic = topic; + this.nodeId = nodeId; + } + + /** + * Returns the topic. + * + * @return The topic + */ + public String topic() { + return topic; + } + + /** + * Returns the nodeId to make leader. + * + * @return The nodeId + */ + public NodeId nodeId() { + return nodeId; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("topic", topic) + .add("nodeId", nodeId) + .toString(); + } + } + + /** + * Command for administratively anoint a node as leader. + */ + @SuppressWarnings("serial") + public static class Anoint extends ElectionChangeOperation { + + private Anoint() { + } + + public Anoint(String topic, NodeId nodeId) { + super(topic, nodeId); + } + } + + /** + * Command for administratively promote a node as top candidate. + */ + @SuppressWarnings("serial") + public static class Promote extends ElectionChangeOperation { + + private Promote() { + } + + public Promote(String topic, NodeId nodeId) { + super(topic, nodeId); + } + } + + /** + * Command for administratively evicting a node from all leadership topics. + */ + @SuppressWarnings("serial") + public static class Evict extends ElectionOperation { + private NodeId nodeId; + + public Evict() { + } + + public Evict(NodeId nodeId) { + this.nodeId = nodeId; + } + + /** + * Returns the node identifier. + * + * @return The nodeId + */ + public NodeId nodeId() { + return nodeId; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("nodeId", nodeId) + .toString(); + } + } +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorService.java similarity index 63% rename from core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorState.java rename to core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorService.java index 369c191bcf..c65d920829 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorState.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorService.java @@ -15,18 +15,6 @@ */ package org.onosproject.store.primitives.resources.impl; -import static org.slf4j.LoggerFactory.getLogger; - -import com.google.common.collect.ImmutableSet; -import io.atomix.copycat.server.session.ServerSession; -import io.atomix.copycat.server.Commit; -import io.atomix.copycat.server.Snapshottable; -import io.atomix.copycat.server.StateMachineExecutor; -import io.atomix.copycat.server.session.SessionListener; -import io.atomix.copycat.server.storage.snapshot.SnapshotReader; -import io.atomix.copycat.server.storage.snapshot.SnapshotWriter; -import io.atomix.resource.ResourceStateMachine; - import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; @@ -34,69 +22,101 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; import java.util.stream.Collectors; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import io.atomix.protocols.raft.service.AbstractRaftService; +import io.atomix.protocols.raft.service.Commit; +import io.atomix.protocols.raft.service.RaftServiceExecutor; +import io.atomix.protocols.raft.session.RaftSession; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import org.onlab.util.KryoNamespace; import org.onosproject.cluster.Leader; import org.onosproject.cluster.Leadership; import org.onosproject.cluster.NodeId; import org.onosproject.event.Change; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Anoint; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Evict; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.GetAllLeaderships; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.GetElectedTopics; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.GetLeadership; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Listen; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Promote; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Run; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Unlisten; -import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorCommands.Withdraw; -import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Anoint; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Evict; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GetElectedTopics; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GetLeadership; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Promote; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Run; +import org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.Withdraw; import org.onosproject.store.service.Serializer; -import org.slf4j.Logger; -import com.google.common.base.MoreObjects; -import com.google.common.base.Objects; -import com.google.common.base.Throwables; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorEvents.CHANGE; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.ADD_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.ANOINT; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.EVICT; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_ALL_LEADERSHIPS; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_ELECTED_TOPICS; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_LEADERSHIP; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.PROMOTE; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.REMOVE_LISTENER; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.RUN; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.WITHDRAW; /** * State machine for {@link AtomixLeaderElector} resource. */ -public class AtomixLeaderElectorState extends ResourceStateMachine - implements SessionListener, Snapshottable { +public class AtomixLeaderElectorService extends AbstractRaftService { + + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(AtomixLeaderElectorOperations.NAMESPACE) + .register(AtomixLeaderElectorEvents.NAMESPACE) + .register(ElectionState.class) + .register(Registration.class) + .register(new LinkedHashMap<>().keySet().getClass()) + .build()); - private final Logger log = getLogger(getClass()); private Map termCounters = new HashMap<>(); private Map elections = new HashMap<>(); - private final Map> listeners = new LinkedHashMap<>(); - private final Serializer serializer = Serializer.using(Arrays.asList(KryoNamespaces.API), - ElectionState.class, - Registration.class); + private Map listeners = new LinkedHashMap<>(); - public AtomixLeaderElectorState(Properties properties) { - super(properties); + @Override + public void snapshot(SnapshotWriter writer) { + writer.writeObject(Sets.newHashSet(listeners.keySet()), SERIALIZER::encode); + writer.writeObject(termCounters, SERIALIZER::encode); + writer.writeObject(elections, SERIALIZER::encode); + getLogger().debug("Took state machine snapshot"); } @Override - protected void configure(StateMachineExecutor executor) { + public void install(SnapshotReader reader) { + listeners = new LinkedHashMap<>(); + for (Long sessionId : reader.>readObject(SERIALIZER::decode)) { + listeners.put(sessionId, getSessions().getSession(sessionId)); + } + termCounters = reader.readObject(SERIALIZER::decode); + elections = reader.readObject(SERIALIZER::decode); + getLogger().debug("Reinstated state machine from snapshot"); + } + + @Override + protected void configure(RaftServiceExecutor executor) { // Notification - executor.register(Listen.class, this::listen); - executor.register(Unlisten.class, this::unlisten); + executor.register(ADD_LISTENER, this::listen); + executor.register(REMOVE_LISTENER, this::unlisten); // Commands - executor.register(Run.class, this::run); - executor.register(Withdraw.class, this::withdraw); - executor.register(Anoint.class, this::anoint); - executor.register(Promote.class, this::promote); - executor.register(Evict.class, this::evict); + executor.register(RUN, SERIALIZER::decode, this::run, SERIALIZER::encode); + executor.register(WITHDRAW, SERIALIZER::decode, this::withdraw); + executor.register(ANOINT, SERIALIZER::decode, this::anoint, SERIALIZER::encode); + executor.register(PROMOTE, SERIALIZER::decode, this::promote, SERIALIZER::encode); + executor.register(EVICT, SERIALIZER::decode, this::evict); // Queries - executor.register(GetLeadership.class, this::leadership); - executor.register(GetAllLeaderships.class, this::allLeaderships); - executor.register(GetElectedTopics.class, this::electedTopics); + executor.register(GET_LEADERSHIP, SERIALIZER::decode, this::getLeadership, SERIALIZER::encode); + executor.register(GET_ALL_LEADERSHIPS, this::allLeaderships, SERIALIZER::encode); + executor.register(GET_ELECTED_TOPICS, SERIALIZER::decode, this::electedTopics, SERIALIZER::encode); } private void notifyLeadershipChange(Leadership previousLeadership, Leadership newLeadership) { @@ -107,16 +127,7 @@ public class AtomixLeaderElectorState extends ResourceStateMachine if (changes.isEmpty()) { return; } - listeners.values() - .forEach(listener -> listener.session() - .publish(AtomixLeaderElector.CHANGE_SUBJECT, changes)); - } - - @Override - public void delete() { - // Close and clear Listeners - listeners.values().forEach(Commit::close); - listeners.clear(); + listeners.values().forEach(session -> session.publish(CHANGE, SERIALIZER::encode, changes)); } /** @@ -124,22 +135,8 @@ public class AtomixLeaderElectorState extends ResourceStateMachine * * @param commit listen commit */ - public void listen(Commit commit) { - Long sessionId = commit.session().id(); - if (listeners.putIfAbsent(commit.session().id(), commit) != null) { - commit.close(); - } - commit.session() - .onStateChange( - state -> { - if (state == ServerSession.State.CLOSED - || state == ServerSession.State.EXPIRED) { - Commit listener = listeners.remove(sessionId); - if (listener != null) { - listener.close(); - } - } - }); + public void listen(Commit commit) { + listeners.put(commit.session().sessionId().id(), commit.session()); } /** @@ -147,27 +144,20 @@ public class AtomixLeaderElectorState extends ResourceStateMachine * * @param commit unlisten commit */ - public void unlisten(Commit commit) { - try { - Commit listener = listeners.remove(commit.session().id()); - if (listener != null) { - listener.close(); - } - } finally { - commit.close(); - } + public void unlisten(Commit commit) { + listeners.remove(commit.session().sessionId().id()); } /** - * Applies an {@link AtomixLeaderElectorCommands.Run} commit. + * Applies an {@link AtomixLeaderElectorOperations.Run} commit. * @param commit commit entry * @return topic leader. If no previous leader existed this is the node that just entered the race. */ public Leadership run(Commit commit) { try { - String topic = commit.operation().topic(); + String topic = commit.value().topic(); Leadership oldLeadership = leadership(topic); - Registration registration = new Registration(commit.operation().nodeId(), commit.session().id()); + Registration registration = new Registration(commit.value().nodeId(), commit.session().sessionId().id()); elections.compute(topic, (k, v) -> { if (v == null) { return new ElectionState(registration, termCounter(topic)::incrementAndGet); @@ -186,44 +176,40 @@ public class AtomixLeaderElectorState extends ResourceStateMachine } return newLeadership; } catch (Exception e) { - log.error("State machine operation failed", e); + getLogger().error("State machine operation failed", e); throw Throwables.propagate(e); - } finally { - commit.close(); } } /** - * Applies an {@link AtomixLeaderElectorCommands.Withdraw} commit. + * Applies an {@link AtomixLeaderElectorOperations.Withdraw} commit. * @param commit withdraw commit */ public void withdraw(Commit commit) { try { - String topic = commit.operation().topic(); + String topic = commit.value().topic(); Leadership oldLeadership = leadership(topic); elections.computeIfPresent(topic, (k, v) -> v.cleanup(commit.session(), - termCounter(topic)::incrementAndGet)); + termCounter(topic)::incrementAndGet)); Leadership newLeadership = leadership(topic); if (!Objects.equal(oldLeadership, newLeadership)) { notifyLeadershipChange(oldLeadership, newLeadership); } } catch (Exception e) { - log.error("State machine operation failed", e); + getLogger().error("State machine operation failed", e); throw Throwables.propagate(e); - } finally { - commit.close(); } } /** - * Applies an {@link AtomixLeaderElectorCommands.Anoint} commit. + * Applies an {@link AtomixLeaderElectorOperations.Anoint} commit. * @param commit anoint commit * @return {@code true} if changes were made and the transfer occurred; {@code false} if it did not. */ public boolean anoint(Commit commit) { try { - String topic = commit.operation().topic(); - NodeId nodeId = commit.operation().nodeId(); + String topic = commit.value().topic(); + NodeId nodeId = commit.value().nodeId(); Leadership oldLeadership = leadership(topic); ElectionState electionState = elections.computeIfPresent(topic, (k, v) -> v.transferLeadership(nodeId, termCounter(topic))); @@ -233,24 +219,22 @@ public class AtomixLeaderElectorState extends ResourceStateMachine } return (electionState != null && electionState.leader() != null && - commit.operation().nodeId().equals(electionState.leader().nodeId())); + commit.value().nodeId().equals(electionState.leader().nodeId())); } catch (Exception e) { - log.error("State machine operation failed", e); + getLogger().error("State machine operation failed", e); throw Throwables.propagate(e); - } finally { - commit.close(); } } /** - * Applies an {@link AtomixLeaderElectorCommands.Promote} commit. + * Applies an {@link AtomixLeaderElectorOperations.Promote} commit. * @param commit promote commit * @return {@code true} if changes desired end state is achieved. */ public boolean promote(Commit commit) { try { - String topic = commit.operation().topic(); - NodeId nodeId = commit.operation().nodeId(); + String topic = commit.value().topic(); + NodeId nodeId = commit.value().nodeId(); Leadership oldLeadership = leadership(topic); if (oldLeadership == null || !oldLeadership.candidates().contains(nodeId)) { return false; @@ -262,21 +246,19 @@ public class AtomixLeaderElectorState extends ResourceStateMachine } return true; } catch (Exception e) { - log.error("State machine operation failed", e); + getLogger().error("State machine operation failed", e); throw Throwables.propagate(e); - } finally { - commit.close(); } } /** - * Applies an {@link AtomixLeaderElectorCommands.Evict} commit. + * Applies an {@link AtomixLeaderElectorOperations.Evict} commit. * @param commit evict commit */ public void evict(Commit commit) { try { List> changes = Lists.newArrayList(); - NodeId nodeId = commit.operation().nodeId(); + NodeId nodeId = commit.value().nodeId(); Set topics = Maps.filterValues(elections, e -> e.candidates().contains(nodeId)).keySet(); topics.forEach(topic -> { Leadership oldLeadership = leadership(topic); @@ -288,65 +270,57 @@ public class AtomixLeaderElectorState extends ResourceStateMachine }); notifyLeadershipChanges(changes); } catch (Exception e) { - log.error("State machine operation failed", e); + getLogger().error("State machine operation failed", e); throw Throwables.propagate(e); - } finally { - commit.close(); } } /** - * Applies an {@link AtomixLeaderElectorCommands.GetLeadership} commit. + * Applies an {@link AtomixLeaderElectorOperations.GetLeadership} commit. * @param commit GetLeadership commit * @return leader */ - public Leadership leadership(Commit commit) { - String topic = commit.operation().topic(); + public Leadership getLeadership(Commit commit) { + String topic = commit.value().topic(); try { return leadership(topic); } catch (Exception e) { - log.error("State machine operation failed", e); + getLogger().error("State machine operation failed", e); throw Throwables.propagate(e); - } finally { - commit.close(); } } /** - * Applies an {@link AtomixLeaderElectorCommands.GetElectedTopics} commit. + * Applies an {@link AtomixLeaderElectorOperations.GetElectedTopics} commit. * @param commit commit entry * @return set of topics for which the node is the leader */ public Set electedTopics(Commit commit) { try { - NodeId nodeId = commit.operation().nodeId(); + NodeId nodeId = commit.value().nodeId(); return ImmutableSet.copyOf(Maps.filterEntries(elections, e -> { Leader leader = leadership(e.getKey()).leader(); return leader != null && leader.nodeId().equals(nodeId); }).keySet()); } catch (Exception e) { - log.error("State machine operation failed", e); + getLogger().error("State machine operation failed", e); throw Throwables.propagate(e); - } finally { - commit.close(); } } /** - * Applies an {@link AtomixLeaderElectorCommands.GetAllLeaderships} commit. + * Applies an {@link AtomixLeaderElectorOperations#GET_ALL_LEADERSHIPS} commit. * @param commit GetAllLeaderships commit * @return topic to leader mapping */ - public Map allLeaderships(Commit commit) { + public Map allLeaderships(Commit commit) { Map result = new HashMap<>(); try { result.putAll(Maps.transformEntries(elections, (k, v) -> leadership(k))); return result; } catch (Exception e) { - log.error("State machine operation failed", e); + getLogger().error("State machine operation failed", e); throw Throwables.propagate(e); - } finally { - commit.close(); } } @@ -366,11 +340,8 @@ public class AtomixLeaderElectorState extends ResourceStateMachine return electionState == null ? new LinkedList<>() : electionState.candidates(); } - private void onSessionEnd(ServerSession session) { - Commit listener = listeners.remove(session.id()); - if (listener != null) { - listener.close(); - } + private void onSessionEnd(RaftSession session) { + listeners.remove(session.sessionId().id()); Set topics = elections.keySet(); List> changes = Lists.newArrayList(); topics.forEach(topic -> { @@ -440,15 +411,15 @@ public class AtomixLeaderElectorState extends ResourceStateMachine this.termStartTime = termStartTime; } - public ElectionState cleanup(ServerSession session, Supplier termCounter) { + public ElectionState cleanup(RaftSession session, Supplier termCounter) { Optional registration = - registrations.stream().filter(r -> r.sessionId() == session.id()).findFirst(); + registrations.stream().filter(r -> r.sessionId() == session.sessionId().id()).findFirst(); if (registration.isPresent()) { List updatedRegistrations = registrations.stream() - .filter(r -> r.sessionId() != session.id()) - .collect(Collectors.toList()); - if (leader.sessionId() == session.id()) { + .filter(r -> r.sessionId() != session.sessionId().id()) + .collect(Collectors.toList()); + if (leader.sessionId() == session.sessionId().id()) { if (!updatedRegistrations.isEmpty()) { return new ElectionState(updatedRegistrations, updatedRegistrations.get(0), @@ -471,8 +442,8 @@ public class AtomixLeaderElectorState extends ResourceStateMachine if (registration.isPresent()) { List updatedRegistrations = registrations.stream() - .filter(r -> !r.nodeId().equals(nodeId)) - .collect(Collectors.toList()); + .filter(r -> !r.nodeId().equals(nodeId)) + .collect(Collectors.toList()); if (leader.nodeId().equals(nodeId)) { if (!updatedRegistrations.isEmpty()) { return new ElectionState(updatedRegistrations, @@ -522,14 +493,14 @@ public class AtomixLeaderElectorState extends ResourceStateMachine public ElectionState transferLeadership(NodeId nodeId, AtomicLong termCounter) { Registration newLeader = registrations.stream() - .filter(r -> r.nodeId().equals(nodeId)) - .findFirst() - .orElse(null); + .filter(r -> r.nodeId().equals(nodeId)) + .findFirst() + .orElse(null); if (newLeader != null) { return new ElectionState(registrations, - newLeader, - termCounter.incrementAndGet(), - System.currentTimeMillis()); + newLeader, + termCounter.incrementAndGet(), + System.currentTimeMillis()); } else { return this; } @@ -537,66 +508,33 @@ public class AtomixLeaderElectorState extends ResourceStateMachine public ElectionState promote(NodeId nodeId) { Registration registration = registrations.stream() - .filter(r -> r.nodeId().equals(nodeId)) - .findFirst() - .orElse(null); + .filter(r -> r.nodeId().equals(nodeId)) + .findFirst() + .orElse(null); List updatedRegistrations = Lists.newArrayList(); updatedRegistrations.add(registration); registrations.stream() - .filter(r -> !r.nodeId().equals(nodeId)) - .forEach(updatedRegistrations::add); + .filter(r -> !r.nodeId().equals(nodeId)) + .forEach(updatedRegistrations::add); return new ElectionState(updatedRegistrations, - leader, - term, - termStartTime); + leader, + term, + termStartTime); } } @Override - public void register(ServerSession session) { - } - - @Override - public void unregister(ServerSession session) { + public void onExpire(RaftSession session) { onSessionEnd(session); } @Override - public void expire(ServerSession session) { + public void onClose(RaftSession session) { onSessionEnd(session); } - @Override - public void close(ServerSession session) { - onSessionEnd(session); - } - - @Override - public void snapshot(SnapshotWriter writer) { - byte[] encodedTermCounters = serializer.encode(termCounters); - writer.writeInt(encodedTermCounters.length); - writer.write(encodedTermCounters); - byte[] encodedElections = serializer.encode(elections); - writer.writeInt(encodedElections.length); - writer.write(encodedElections); - log.debug("Took state machine snapshot"); - } - - @Override - public void install(SnapshotReader reader) { - int encodedTermCountersSize = reader.readInt(); - byte[] encodedTermCounters = new byte[encodedTermCountersSize]; - reader.read(encodedTermCounters); - termCounters = serializer.decode(encodedTermCounters); - int encodedElectionsSize = reader.readInt(); - byte[] encodedElections = new byte[encodedElectionsSize]; - reader.read(encodedElections); - elections = serializer.decode(encodedElections); - log.debug("Reinstated state machine from snapshot"); - } - private AtomicLong termCounter(String topic) { return termCounters.computeIfAbsent(topic, k -> new AtomicLong(0)); } -} +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixSerializerAdapter.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixSerializerAdapter.java new file mode 100644 index 0000000000..7eafdb68c2 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixSerializerAdapter.java @@ -0,0 +1,39 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import org.onosproject.store.service.Serializer; + +/** + * ONOS to Atomix serializer adapter. + */ +public class AtomixSerializerAdapter implements io.atomix.serializer.Serializer { + private final Serializer serializer; + + public AtomixSerializerAdapter(Serializer serializer) { + this.serializer = serializer; + } + + @Override + public byte[] encode(T object) { + return serializer.encode(object); + } + + @Override + public T decode(byte[] bytes) { + return serializer.decode(bytes); + } +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueue.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueue.java index 569a597ff6..e21ec8caad 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueue.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueue.java @@ -15,16 +15,8 @@ */ package org.onosproject.store.primitives.resources.impl; -import static java.util.concurrent.Executors.newSingleThreadExecutor; -import static org.onlab.util.Tools.groupedThreads; -import static org.slf4j.LoggerFactory.getLogger; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.AbstractResource; -import io.atomix.resource.ResourceTypeInfo; - import java.util.Collection; import java.util.List; -import java.util.Properties; import java.util.Timer; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; @@ -34,63 +26,64 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import com.google.common.collect.ImmutableList; +import io.atomix.protocols.raft.proxy.RaftProxy; import org.onlab.util.AbstractAccumulator; import org.onlab.util.Accumulator; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Add; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Clear; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Complete; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Register; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Stats; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Take; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Unregister; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.Add; +import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.Complete; +import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.Take; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.Serializer; import org.onosproject.store.service.Task; import org.onosproject.store.service.WorkQueue; import org.onosproject.store.service.WorkQueueStats; import org.slf4j.Logger; -import com.google.common.collect.ImmutableList; +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueEvents.TASK_AVAILABLE; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.ADD; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.CLEAR; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.COMPLETE; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.REGISTER; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.STATS; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.TAKE; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.UNREGISTER; +import static org.slf4j.LoggerFactory.getLogger; /** * Distributed resource providing the {@link WorkQueue} primitive. */ -@ResourceTypeInfo(id = -154, factory = AtomixWorkQueueFactory.class) -public class AtomixWorkQueue extends AbstractResource - implements WorkQueue { +public class AtomixWorkQueue extends AbstractRaftPrimitive implements WorkQueue { + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixWorkQueueOperations.NAMESPACE) + .register(AtomixWorkQueueEvents.NAMESPACE) + .build()); private final Logger log = getLogger(getClass()); - public static final String TASK_AVAILABLE = "task-available"; private final ExecutorService executor = newSingleThreadExecutor(groupedThreads("AtomixWorkQueue", "%d", log)); private final AtomicReference taskProcessor = new AtomicReference<>(); private final Timer timer = new Timer("atomix-work-queue-completer"); private final AtomicBoolean isRegistered = new AtomicBoolean(false); - protected AtomixWorkQueue(CopycatClient client, Properties options) { - super(client, options); - } - - @Override - public String name() { - return null; + public AtomixWorkQueue(RaftProxy proxy) { + super(proxy); + proxy.addStateChangeListener(state -> { + if (state == RaftProxy.State.CONNECTED && isRegistered.get()) { + proxy.invoke(REGISTER); + } + }); + proxy.addEventListener(TASK_AVAILABLE, this::resumeWork); } @Override public CompletableFuture destroy() { executor.shutdown(); timer.cancel(); - return client.submit(new Clear()); - } - - @Override - public CompletableFuture open() { - return super.open().thenApply(result -> { - client.onStateChange(state -> { - if (state == CopycatClient.State.CONNECTED && isRegistered.get()) { - client.submit(new Register()); - } - }); - client.onEvent(TASK_AVAILABLE, this::resumeWork); - return result; - }); + return proxy.invoke(CLEAR); } @Override @@ -98,7 +91,7 @@ public class AtomixWorkQueue extends AbstractResource if (items.isEmpty()) { return CompletableFuture.completedFuture(null); } - return client.submit(new Add(items)); + return proxy.invoke(ADD, SERIALIZER::encode, new Add(items)); } @Override @@ -106,7 +99,7 @@ public class AtomixWorkQueue extends AbstractResource if (maxTasks <= 0) { return CompletableFuture.completedFuture(ImmutableList.of()); } - return client.submit(new Take(maxTasks)); + return proxy.invoke(TAKE, SERIALIZER::encode, new Take(maxTasks), SERIALIZER::decode); } @Override @@ -114,21 +107,21 @@ public class AtomixWorkQueue extends AbstractResource if (taskIds.isEmpty()) { return CompletableFuture.completedFuture(null); } - return client.submit(new Complete(taskIds)); + return proxy.invoke(COMPLETE, SERIALIZER::encode, new Complete(taskIds)); } @Override public CompletableFuture registerTaskProcessor(Consumer callback, - int parallelism, - Executor executor) { + int parallelism, + Executor executor) { Accumulator completedTaskAccumulator = new CompletedTaskAccumulator(timer, 50, 50); // TODO: make configurable taskProcessor.set(new TaskProcessor(callback, - parallelism, - executor, - completedTaskAccumulator)); + parallelism, + executor, + completedTaskAccumulator)); return register().thenCompose(v -> take(parallelism)) - .thenAccept(taskProcessor.get()); + .thenAccept(taskProcessor.get()); } @Override @@ -138,7 +131,7 @@ public class AtomixWorkQueue extends AbstractResource @Override public CompletableFuture stats() { - return client.submit(new Stats()); + return proxy.invoke(STATS, SERIALIZER::decode); } private void resumeWork() { @@ -147,15 +140,15 @@ public class AtomixWorkQueue extends AbstractResource return; } this.take(activeProcessor.headRoom()) - .whenCompleteAsync((tasks, e) -> activeProcessor.accept(tasks), executor); + .whenCompleteAsync((tasks, e) -> activeProcessor.accept(tasks), executor); } private CompletableFuture register() { - return client.submit(new Register()).thenRun(() -> isRegistered.set(true)); + return proxy.invoke(REGISTER).thenRun(() -> isRegistered.set(true)); } private CompletableFuture unregister() { - return client.submit(new Unregister()).thenRun(() -> isRegistered.set(false)); + return proxy.invoke(UNREGISTER).thenRun(() -> isRegistered.set(false)); } // TaskId accumulator for paced triggering of task completion calls. @@ -178,9 +171,9 @@ public class AtomixWorkQueue extends AbstractResource private final Accumulator taskCompleter; public TaskProcessor(Consumer backingConsumer, - int parallelism, - Executor executor, - Accumulator taskCompleter) { + int parallelism, + Executor executor, + Accumulator taskCompleter) { this.backingConsumer = backingConsumer; this.headRoom = new AtomicInteger(parallelism); this.executor = executor; @@ -198,17 +191,17 @@ public class AtomixWorkQueue extends AbstractResource } headRoom.addAndGet(-1 * tasks.size()); tasks.forEach(task -> - executor.execute(() -> { - try { - backingConsumer.accept(task.payload()); - taskCompleter.add(task.taskId()); - } catch (Exception e) { - log.debug("Task execution failed", e); - } finally { - headRoom.incrementAndGet(); - resumeWork(); - } - })); + executor.execute(() -> { + try { + backingConsumer.accept(task.payload()); + taskCompleter.add(task.taskId()); + } catch (Exception e) { + log.debug("Task execution failed", e); + } finally { + headRoom.incrementAndGet(); + resumeWork(); + } + })); } } -} +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueCommands.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueCommands.java deleted file mode 100644 index 977470dbbd..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueCommands.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.resources.impl; - -import io.atomix.catalyst.buffer.BufferInput; -import io.atomix.catalyst.buffer.BufferOutput; -import io.atomix.catalyst.serializer.CatalystSerializable; -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.catalyst.serializer.Serializer; -import io.atomix.catalyst.serializer.SerializerRegistry; -import io.atomix.copycat.Command; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import org.onosproject.store.service.Task; -import org.onosproject.store.service.WorkQueueStats; - -import com.google.common.base.MoreObjects; - -/** - * {@link AtomixWorkQueue} resource state machine operations. - */ -public final class AtomixWorkQueueCommands { - - private AtomixWorkQueueCommands() { - } - - /** - * Command to add a collection of tasks to the queue. - */ - @SuppressWarnings("serial") - public static class Add implements Command, CatalystSerializable { - - private Collection items; - - private Add() { - } - - public Add(Collection items) { - this.items = items; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - buffer.writeInt(items.size()); - items.forEach(task -> serializer.writeObject(task, buffer)); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - items = IntStream.range(0, buffer.readInt()) - .mapToObj(i -> serializer.readObject(buffer)) - .collect(Collectors.toCollection(ArrayList::new)); - } - - public Collection items() { - return items; - } - - @Override - public CompactionMode compaction() { - return CompactionMode.QUORUM; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("items", items) - .toString(); - } - } - - /** - * Command to take a task from the queue. - */ - @SuppressWarnings("serial") - public static class Take implements Command>>, CatalystSerializable { - - private int maxTasks; - - private Take() { - } - - public Take(int maxTasks) { - this.maxTasks = maxTasks; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - buffer.writeInt(maxTasks); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - maxTasks = buffer.readInt(); - } - - public int maxTasks() { - return maxTasks; - } - - @Override - public CompactionMode compaction() { - return CompactionMode.QUORUM; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("maxTasks", maxTasks) - .toString(); - } - } - - @SuppressWarnings("serial") - public static class Stats implements Command, CatalystSerializable { - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .toString(); - } - } - - - - @SuppressWarnings("serial") - public static class Register implements Command, CatalystSerializable { - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - - @Override - public CompactionMode compaction() { - return CompactionMode.QUORUM; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .toString(); - } - } - - @SuppressWarnings("serial") - public static class Unregister implements Command, CatalystSerializable { - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .toString(); - } - } - - @SuppressWarnings("serial") - public static class Complete implements Command, CatalystSerializable { - private Collection taskIds; - - private Complete() { - } - - public Complete(Collection taskIds) { - this.taskIds = taskIds; - } - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - serializer.writeObject(taskIds, buffer); - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - taskIds = serializer.readObject(buffer); - } - - public Collection taskIds() { - return taskIds; - } - - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("taskIds", taskIds) - .toString(); - } - } - - @SuppressWarnings("serial") - public static class Clear implements Command, CatalystSerializable { - - @Override - public void writeObject(BufferOutput buffer, Serializer serializer) { - } - - @Override - public void readObject(BufferInput buffer, Serializer serializer) { - } - - @Override - public CompactionMode compaction() { - return CompactionMode.TOMBSTONE; - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .toString(); - } - } - - /** - * Work queue command type resolver. - */ - public static class TypeResolver implements SerializableTypeResolver { - @Override - public void resolve(SerializerRegistry registry) { - registry.register(Register.class, -960); - registry.register(Unregister.class, -961); - registry.register(Take.class, -962); - registry.register(Add.class, -963); - registry.register(Complete.class, -964); - registry.register(Stats.class, -965); - registry.register(Clear.class, -966); - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueEvents.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueEvents.java new file mode 100644 index 0000000000..50b7366e24 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueEvents.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.event.EventType; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.serializers.KryoNamespaces; + +/** + * Atomix work queue events. + */ +public enum AtomixWorkQueueEvents implements EventType { + TASK_AVAILABLE("taskAvailable"); + + private final String id; + + AtomixWorkQueueEvents(String id) { + this.id = id; + } + + @Override + public String id() { + return id; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID + 50) + .build("AtomixWorkQueueEvents"); +} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueFactory.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueFactory.java deleted file mode 100644 index 0c61b2e50c..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueFactory.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.resources.impl; - -import io.atomix.catalyst.serializer.SerializableTypeResolver; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.resource.ResourceFactory; -import io.atomix.resource.ResourceStateMachine; - -import java.util.Properties; - -/** - * {@link AtomixWorkQueue} resource factory. - */ -public class AtomixWorkQueueFactory implements ResourceFactory { - - @Override - public SerializableTypeResolver createSerializableTypeResolver() { - return new AtomixWorkQueueCommands.TypeResolver(); - } - - @Override - public ResourceStateMachine createStateMachine(Properties config) { - return new AtomixWorkQueueState(config); - } - - @Override - public AtomixWorkQueue createInstance(CopycatClient client, Properties properties) { - return new AtomixWorkQueue(client, properties); - } -} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueOperations.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueOperations.java new file mode 100644 index 0000000000..a7a0df1e16 --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueOperations.java @@ -0,0 +1,148 @@ +/* + * Copyright 2016-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import java.util.Collection; + +import com.google.common.base.MoreObjects; +import io.atomix.protocols.raft.operation.OperationId; +import io.atomix.protocols.raft.operation.OperationType; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.Task; +import org.onosproject.store.service.WorkQueueStats; + +/** + * {@link AtomixWorkQueue} resource state machine operations. + */ +public enum AtomixWorkQueueOperations implements OperationId { + STATS("stats", OperationType.QUERY), + REGISTER("register", OperationType.COMMAND), + UNREGISTER("unregister", OperationType.COMMAND), + ADD("add", OperationType.COMMAND), + TAKE("take", OperationType.COMMAND), + COMPLETE("complete", OperationType.COMMAND), + CLEAR("clear", OperationType.COMMAND); + + private final String id; + private final OperationType type; + + AtomixWorkQueueOperations(String id, OperationType type) { + this.id = id; + this.type = type; + } + + @Override + public String id() { + return id; + } + + @Override + public OperationType type() { + return type; + } + + public static final KryoNamespace NAMESPACE = KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID) + .register(Add.class) + .register(Take.class) + .register(Complete.class) + .register(Task.class) + .register(WorkQueueStats.class) + .build("AtomixWorkQueueOperations"); + + /** + * Work queue operation. + */ + public abstract static class WorkQueueOperation { + } + + /** + * Command to add a collection of tasks to the queue. + */ + @SuppressWarnings("serial") + public static class Add extends WorkQueueOperation { + private Collection items; + + private Add() { + } + + public Add(Collection items) { + this.items = items; + } + + public Collection items() { + return items; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("items", items) + .toString(); + } + } + + /** + * Command to take a task from the queue. + */ + @SuppressWarnings("serial") + public static class Take extends WorkQueueOperation { + private int maxTasks; + + private Take() { + } + + public Take(int maxTasks) { + this.maxTasks = maxTasks; + } + + public int maxTasks() { + return maxTasks; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("maxTasks", maxTasks) + .toString(); + } + } + + @SuppressWarnings("serial") + public static class Complete extends WorkQueueOperation { + private Collection taskIds; + + private Complete() { + } + + public Complete(Collection taskIds) { + this.taskIds = taskIds; + } + + public Collection taskIds() { + return taskIds; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("taskIds", taskIds) + .toString(); + } + } +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueService.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueService.java new file mode 100644 index 0000000000..6458ec8dad --- /dev/null +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueService.java @@ -0,0 +1,243 @@ +/* + * Copyright 2016-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; +import com.google.common.collect.Queues; +import com.google.common.collect.Sets; +import io.atomix.protocols.raft.service.AbstractRaftService; +import io.atomix.protocols.raft.service.Commit; +import io.atomix.protocols.raft.service.RaftServiceExecutor; +import io.atomix.protocols.raft.session.RaftSession; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import org.onlab.util.KryoNamespace; +import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.Add; +import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.Complete; +import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.Take; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.Task; +import org.onosproject.store.service.WorkQueueStats; + +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueEvents.TASK_AVAILABLE; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.ADD; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.CLEAR; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.COMPLETE; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.REGISTER; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.STATS; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.TAKE; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.UNREGISTER; + +/** + * State machine for {@link AtomixWorkQueue} resource. + */ +public class AtomixWorkQueueService extends AbstractRaftService { + + private static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(KryoNamespaces.BASIC) + .register(AtomixWorkQueueOperations.NAMESPACE) + .register(AtomixWorkQueueEvents.NAMESPACE) + .register(TaskAssignment.class) + .register(new HashMap().keySet().getClass()) + .register(ArrayDeque.class) + .build()); + + private final AtomicLong totalCompleted = new AtomicLong(0); + + private Queue> unassignedTasks = Queues.newArrayDeque(); + private Map assignments = Maps.newHashMap(); + private Map registeredWorkers = Maps.newHashMap(); + + @Override + public void snapshot(SnapshotWriter writer) { + writer.writeObject(Sets.newHashSet(registeredWorkers.keySet()), SERIALIZER::encode); + writer.writeObject(assignments, SERIALIZER::encode); + writer.writeObject(unassignedTasks, SERIALIZER::encode); + writer.writeLong(totalCompleted.get()); + } + + @Override + public void install(SnapshotReader reader) { + registeredWorkers = Maps.newHashMap(); + for (Long sessionId : reader.>readObject(SERIALIZER::decode)) { + registeredWorkers.put(sessionId, getSessions().getSession(sessionId)); + } + assignments = reader.readObject(SERIALIZER::decode); + unassignedTasks = reader.readObject(SERIALIZER::decode); + totalCompleted.set(reader.readLong()); + } + + @Override + protected void configure(RaftServiceExecutor executor) { + executor.register(STATS, this::stats, SERIALIZER::encode); + executor.register(REGISTER, this::register); + executor.register(UNREGISTER, this::unregister); + executor.register(ADD, SERIALIZER::decode, this::add); + executor.register(TAKE, SERIALIZER::decode, this::take, SERIALIZER::encode); + executor.register(COMPLETE, SERIALIZER::decode, this::complete); + executor.register(CLEAR, this::clear); + } + + protected WorkQueueStats stats(Commit commit) { + return WorkQueueStats.builder() + .withTotalCompleted(totalCompleted.get()) + .withTotalPending(unassignedTasks.size()) + .withTotalInProgress(assignments.size()) + .build(); + } + + protected void clear(Commit commit) { + unassignedTasks.clear(); + assignments.clear(); + registeredWorkers.clear(); + totalCompleted.set(0); + } + + protected void register(Commit commit) { + registeredWorkers.put(commit.session().sessionId().id(), commit.session()); + } + + protected void unregister(Commit commit) { + registeredWorkers.remove(commit.session().sessionId().id()); + } + + protected void add(Commit commit) { + Collection items = commit.value().items(); + + AtomicInteger itemIndex = new AtomicInteger(0); + items.forEach(item -> { + String taskId = String.format("%d:%d:%d", commit.session().sessionId().id(), + commit.index(), + itemIndex.getAndIncrement()); + unassignedTasks.add(new Task<>(taskId, item)); + }); + + // Send an event to all sessions that have expressed interest in task processing + // and are not actively processing a task. + registeredWorkers.values().forEach(session -> session.publish(TASK_AVAILABLE)); + // FIXME: This generates a lot of event traffic. + } + + protected Collection> take(Commit commit) { + try { + if (unassignedTasks.isEmpty()) { + return ImmutableList.of(); + } + long sessionId = commit.session().sessionId().id(); + int maxTasks = commit.value().maxTasks(); + return IntStream.range(0, Math.min(maxTasks, unassignedTasks.size())) + .mapToObj(i -> { + Task task = unassignedTasks.poll(); + String taskId = task.taskId(); + TaskAssignment assignment = new TaskAssignment(sessionId, task); + + // bookkeeping + assignments.put(taskId, assignment); + + return task; + }) + .collect(Collectors.toCollection(ArrayList::new)); + } catch (Exception e) { + getLogger().warn("State machine update failed", e); + throw Throwables.propagate(e); + } + } + + protected void complete(Commit commit) { + long sessionId = commit.session().sessionId().id(); + try { + commit.value().taskIds().forEach(taskId -> { + TaskAssignment assignment = assignments.get(taskId); + if (assignment != null && assignment.sessionId() == sessionId) { + assignments.remove(taskId); + // bookkeeping + totalCompleted.incrementAndGet(); + } + }); + } catch (Exception e) { + getLogger().warn("State machine update failed", e); + throw Throwables.propagate(e); + } + } + + @Override + public void onExpire(RaftSession session) { + evictWorker(session.sessionId().id()); + } + + @Override + public void onClose(RaftSession session) { + evictWorker(session.sessionId().id()); + } + + private void evictWorker(long sessionId) { + registeredWorkers.remove(sessionId); + + // TODO: Maintain an index of tasks by session for efficient access. + Iterator> iter = assignments.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + TaskAssignment assignment = entry.getValue(); + if (assignment.sessionId() == sessionId) { + unassignedTasks.add(assignment.task()); + iter.remove(); + } + } + } + + private static class TaskAssignment { + private final long sessionId; + private final Task task; + + public TaskAssignment(long sessionId, Task task) { + this.sessionId = sessionId; + this.task = task; + } + + public long sessionId() { + return sessionId; + } + + public Task task() { + return task; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("sessionId", sessionId) + .add("task", task) + .toString(); + } + } +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueState.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueState.java deleted file mode 100644 index 82f28e8862..0000000000 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueState.java +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Copyright 2016-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.resources.impl; - -import static org.slf4j.LoggerFactory.getLogger; -import io.atomix.copycat.server.Commit; -import io.atomix.copycat.server.Snapshottable; -import io.atomix.copycat.server.StateMachineExecutor; -import io.atomix.copycat.server.session.ServerSession; -import io.atomix.copycat.server.session.SessionListener; -import io.atomix.copycat.server.storage.snapshot.SnapshotReader; -import io.atomix.copycat.server.storage.snapshot.SnapshotWriter; -import io.atomix.resource.ResourceStateMachine; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Properties; -import java.util.Queue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import org.onlab.util.CountDownCompleter; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Add; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Clear; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Complete; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Register; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Stats; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Take; -import org.onosproject.store.primitives.resources.impl.AtomixWorkQueueCommands.Unregister; -import org.onosproject.store.service.Task; -import org.onosproject.store.service.WorkQueueStats; -import org.slf4j.Logger; - -import com.google.common.base.MoreObjects; -import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; -import com.google.common.collect.Queues; -import com.google.common.util.concurrent.AtomicLongMap; - -/** - * State machine for {@link AtomixWorkQueue} resource. - */ -public class AtomixWorkQueueState extends ResourceStateMachine implements SessionListener, Snapshottable { - - private final Logger log = getLogger(getClass()); - - private final AtomicLong totalCompleted = new AtomicLong(0); - - private final Queue unassignedTasks = Queues.newArrayDeque(); - private final Map assignments = Maps.newHashMap(); - private final Map> registeredWorkers = Maps.newHashMap(); - private final AtomicLongMap activeTasksPerSession = AtomicLongMap.create(); - - protected AtomixWorkQueueState(Properties config) { - super(config); - } - - @Override - protected void configure(StateMachineExecutor executor) { - executor.register(Stats.class, this::stats); - executor.register(Register.class, (Consumer>) this::register); - executor.register(Unregister.class, (Consumer>) this::unregister); - executor.register(Add.class, (Consumer>) this::add); - executor.register(Take.class, this::take); - executor.register(Complete.class, (Consumer>) this::complete); - executor.register(Clear.class, (Consumer>) this::clear); - } - - protected WorkQueueStats stats(Commit commit) { - try { - return WorkQueueStats.builder() - .withTotalCompleted(totalCompleted.get()) - .withTotalPending(unassignedTasks.size()) - .withTotalInProgress(assignments.size()) - .build(); - } finally { - commit.close(); - } - } - - protected void clear(Commit commit) { - try { - unassignedTasks.forEach(TaskHolder::complete); - unassignedTasks.clear(); - assignments.values().forEach(TaskAssignment::markComplete); - assignments.clear(); - registeredWorkers.values().forEach(Commit::close); - registeredWorkers.clear(); - activeTasksPerSession.clear(); - totalCompleted.set(0); - } finally { - commit.close(); - } - } - - protected void register(Commit commit) { - long sessionId = commit.session().id(); - if (registeredWorkers.putIfAbsent(sessionId, commit) != null) { - commit.close(); - } - } - - protected void unregister(Commit commit) { - try { - Commit registerCommit = registeredWorkers.remove(commit.session().id()); - if (registerCommit != null) { - registerCommit.close(); - } - } finally { - commit.close(); - } - } - - protected void add(Commit commit) { - Collection items = commit.operation().items(); - - // Create a CountDownCompleter that will close the commit when all tasks - // submitted as part of it are completed. - CountDownCompleter> referenceTracker = - new CountDownCompleter<>(commit, items.size(), Commit::close); - - AtomicInteger itemIndex = new AtomicInteger(0); - items.forEach(item -> { - String taskId = String.format("%d:%d:%d", commit.session().id(), - commit.index(), - itemIndex.getAndIncrement()); - unassignedTasks.add(new TaskHolder(new Task<>(taskId, item), referenceTracker)); - }); - - // Send an event to all sessions that have expressed interest in task processing - // and are not actively processing a task. - registeredWorkers.values() - .stream() - .map(Commit::session) - .forEach(session -> session.publish(AtomixWorkQueue.TASK_AVAILABLE)); - // FIXME: This generates a lot of event traffic. - } - - protected Collection> take(Commit commit) { - try { - if (unassignedTasks.isEmpty()) { - return ImmutableList.of(); - } - long sessionId = commit.session().id(); - int maxTasks = commit.operation().maxTasks(); - return IntStream.range(0, Math.min(maxTasks, unassignedTasks.size())) - .mapToObj(i -> { - TaskHolder holder = unassignedTasks.poll(); - String taskId = holder.task().taskId(); - TaskAssignment assignment = new TaskAssignment(sessionId, holder); - - // bookkeeping - assignments.put(taskId, assignment); - activeTasksPerSession.incrementAndGet(sessionId); - - return holder.task(); - }) - .collect(Collectors.toCollection(ArrayList::new)); - } catch (Exception e) { - log.warn("State machine update failed", e); - throw Throwables.propagate(e); - } finally { - commit.close(); - } - } - - protected void complete(Commit commit) { - long sessionId = commit.session().id(); - try { - commit.operation().taskIds().forEach(taskId -> { - TaskAssignment assignment = assignments.get(taskId); - if (assignment != null && assignment.sessionId() == sessionId) { - assignments.remove(taskId).markComplete(); - // bookkeeping - totalCompleted.incrementAndGet(); - activeTasksPerSession.decrementAndGet(sessionId); - } - }); - } catch (Exception e) { - log.warn("State machine update failed", e); - throw Throwables.propagate(e); - } finally { - commit.close(); - } - } - - @Override - public void register(ServerSession session) { - } - - @Override - public void unregister(ServerSession session) { - evictWorker(session.id()); - } - - @Override - public void expire(ServerSession session) { - evictWorker(session.id()); - } - - @Override - public void close(ServerSession session) { - evictWorker(session.id()); - } - - @Override - public void snapshot(SnapshotWriter writer) { - writer.writeLong(totalCompleted.get()); - } - - @Override - public void install(SnapshotReader reader) { - totalCompleted.set(reader.readLong()); - } - - private void evictWorker(long sessionId) { - Commit commit = registeredWorkers.remove(sessionId); - if (commit != null) { - commit.close(); - } - - // TODO: Maintain an index of tasks by session for efficient access. - Iterator> iter = assignments.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - TaskAssignment assignment = entry.getValue(); - if (assignment.sessionId() == sessionId) { - unassignedTasks.add(assignment.taskHolder()); - iter.remove(); - } - } - - // Bookkeeping - activeTasksPerSession.remove(sessionId); - activeTasksPerSession.removeAllZeros(); - } - - private class TaskHolder { - - private final Task task; - private final CountDownCompleter> referenceTracker; - - public TaskHolder(Task delegate, CountDownCompleter> referenceTracker) { - this.task = delegate; - this.referenceTracker = referenceTracker; - } - - public Task task() { - return task; - } - - public void complete() { - referenceTracker.countDown(); - } - } - - private class TaskAssignment { - private final long sessionId; - private final TaskHolder taskHolder; - - public TaskAssignment(long sessionId, TaskHolder taskHolder) { - this.sessionId = sessionId; - this.taskHolder = taskHolder; - } - - public long sessionId() { - return sessionId; - } - - public TaskHolder taskHolder() { - return taskHolder; - } - - public void markComplete() { - taskHolder.complete(); - } - - @Override - public String toString() { - return MoreObjects.toStringHelper(getClass()) - .add("sessionId", sessionId) - .add("taskHolder", taskHolder) - .toString(); - } - } -} diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTree.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTree.java index a351ad531e..ba248ba249 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTree.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTree.java @@ -41,7 +41,7 @@ import com.google.common.collect.Maps; public class DefaultDocumentTree implements DocumentTree { private static final DocumentPath ROOT_PATH = DocumentPath.from("root"); - private final DefaultDocumentTreeNode root; + final DefaultDocumentTreeNode root; private final Supplier versionSupplier; public DefaultDocumentTree() { @@ -55,6 +55,11 @@ public class DefaultDocumentTree implements DocumentTree { this.versionSupplier = versionSupplier; } + DefaultDocumentTree(Supplier versionSupplier, DefaultDocumentTreeNode root) { + this.root = root; + this.versionSupplier = versionSupplier; + } + @Override public DocumentPath root() { return ROOT_PATH; @@ -195,4 +200,4 @@ public class DefaultDocumentTree implements DocumentTree { throw new IllegalDocumentModificationException(); } } -} +} \ No newline at end of file diff --git a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTreeNode.java b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTreeNode.java index 4b4852bd89..65687799d0 100644 --- a/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTreeNode.java +++ b/core/store/primitives/src/main/java/org/onosproject/store/primitives/resources/impl/DefaultDocumentTreeNode.java @@ -41,9 +41,9 @@ public class DefaultDocumentTreeNode implements DocumentTreeNode { private final DocumentTreeNode parent; public DefaultDocumentTreeNode(DocumentPath key, - V value, - long version, - DocumentTreeNode parent) { + V value, + long version, + DocumentTreeNode parent) { this.key = checkNotNull(key); this.value = new Versioned<>(value, version); this.parent = parent; @@ -137,9 +137,9 @@ public class DefaultDocumentTreeNode implements DocumentTreeNode { public String toString() { MoreObjects.ToStringHelper helper = MoreObjects.toStringHelper(getClass()) - .add("parent", this.parent) - .add("key", this.key) - .add("value", this.value); + .add("parent", this.parent) + .add("key", this.key) + .add("value", this.value); for (DocumentTreeNode child : children.values()) { helper = helper.add("child", "\n" + child.path().pathElements() .get(child.path().pathElements().size() - 1) + @@ -147,4 +147,4 @@ public class DefaultDocumentTreeNode implements DocumentTreeNode { } return helper.toString(); } -} +} \ No newline at end of file diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/CopycatTransportTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/CopycatTransportTest.java deleted file mode 100644 index d62f4af0ba..0000000000 --- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/impl/CopycatTransportTest.java +++ /dev/null @@ -1,433 +0,0 @@ -/* - * Copyright 2017-present Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.store.primitives.impl; - -import java.time.Duration; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; - -import com.google.common.collect.Lists; -import io.atomix.catalyst.concurrent.SingleThreadContext; -import io.atomix.catalyst.concurrent.ThreadContext; -import io.atomix.catalyst.transport.Address; -import io.atomix.catalyst.transport.Client; -import io.atomix.catalyst.transport.Server; -import io.atomix.catalyst.transport.Transport; -import io.atomix.copycat.protocol.ConnectRequest; -import io.atomix.copycat.protocol.ConnectResponse; -import io.atomix.copycat.protocol.Response; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.onlab.packet.IpAddress; -import org.onlab.util.Tools; -import org.onosproject.cluster.PartitionId; -import org.onosproject.store.cluster.messaging.Endpoint; -import org.onosproject.store.cluster.messaging.MessagingException; -import org.onosproject.store.cluster.messaging.MessagingService; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.onlab.junit.TestTools.findAvailablePort; - -/** - * Copycat transport test. - */ -public class CopycatTransportTest { - - private static final String IP_STRING = "127.0.0.1"; - - private Endpoint endpoint1 = new Endpoint(IpAddress.valueOf(IP_STRING), 5001); - private Endpoint endpoint2 = new Endpoint(IpAddress.valueOf(IP_STRING), 5002); - - private TestMessagingService clientService; - private TestMessagingService serverService; - - private Transport clientTransport; - private ThreadContext clientContext; - - private Transport serverTransport; - private ThreadContext serverContext; - - @Before - public void setUp() throws Exception { - Map services = new ConcurrentHashMap<>(); - - endpoint1 = new Endpoint(IpAddress.valueOf("127.0.0.1"), findAvailablePort(5001)); - clientService = new TestMessagingService(endpoint1, services); - clientTransport = new CopycatTransport(PartitionId.from(1), clientService); - clientContext = new SingleThreadContext("client-test-%d", CatalystSerializers.getSerializer()); - - endpoint2 = new Endpoint(IpAddress.valueOf("127.0.0.1"), findAvailablePort(5003)); - serverService = new TestMessagingService(endpoint2, services); - serverTransport = new CopycatTransport(PartitionId.from(1), serverService); - serverContext = new SingleThreadContext("server-test-%d", CatalystSerializers.getSerializer()); - } - - @After - public void tearDown() throws Exception { - if (clientContext != null) { - clientContext.close(); - } - if (serverContext != null) { - serverContext.close(); - } - } - - /** - * Tests sending a message from the client side of a Copycat connection to the server side. - */ - @Test - public void testCopycatClientConnectionSend() throws Exception { - Client client = clientTransport.client(); - Server server = serverTransport.server(); - - CountDownLatch latch = new CountDownLatch(4); - CountDownLatch listenLatch = new CountDownLatch(1); - CountDownLatch handlerLatch = new CountDownLatch(1); - serverContext.executor().execute(() -> { - server.listen(new Address(IP_STRING, endpoint2.port()), connection -> { - serverContext.checkThread(); - latch.countDown(); - connection.handler(ConnectRequest.class, request -> { - serverContext.checkThread(); - latch.countDown(); - return CompletableFuture.completedFuture(ConnectResponse.builder() - .withStatus(Response.Status.OK) - .withLeader(new Address(IP_STRING, endpoint2.port())) - .withMembers(Lists.newArrayList(new Address(IP_STRING, endpoint2.port()))) - .build()); - }); - handlerLatch.countDown(); - }).thenRun(listenLatch::countDown); - }); - - listenLatch.await(5, TimeUnit.SECONDS); - - clientContext.executor().execute(() -> { - client.connect(new Address(IP_STRING, endpoint2.port())).thenAccept(connection -> { - clientContext.checkThread(); - latch.countDown(); - try { - handlerLatch.await(5, TimeUnit.SECONDS); - } catch (InterruptedException e) { - fail(); - } - connection.sendAndReceive(ConnectRequest.builder() - .withClientId(UUID.randomUUID().toString()) - .build()) - .thenAccept(response -> { - clientContext.checkThread(); - assertNotNull(response); - assertEquals(Response.Status.OK, response.status()); - latch.countDown(); - }); - }); - }); - - latch.await(5, TimeUnit.SECONDS); - assertEquals(0, latch.getCount()); - } - - /** - * Tests sending a message from the server side of a Copycat connection to the client side. - */ - @Test - public void testCopycatServerConnectionSend() throws Exception { - Client client = clientTransport.client(); - Server server = serverTransport.server(); - - CountDownLatch latch = new CountDownLatch(4); - CountDownLatch listenLatch = new CountDownLatch(1); - serverContext.executor().execute(() -> { - server.listen(new Address(IP_STRING, endpoint2.port()), connection -> { - serverContext.checkThread(); - latch.countDown(); - serverContext.schedule(Duration.ofMillis(100), () -> { - connection.sendAndReceive(ConnectRequest.builder() - .withClientId("foo") - .build()) - .thenAccept(response -> { - serverContext.checkThread(); - assertEquals(Response.Status.OK, response.status()); - latch.countDown(); - }); - }); - }).thenRun(listenLatch::countDown); - }); - - listenLatch.await(5, TimeUnit.SECONDS); - - clientContext.executor().execute(() -> { - client.connect(new Address(IP_STRING, endpoint2.port())).thenAccept(connection -> { - clientContext.checkThread(); - latch.countDown(); - connection.handler(ConnectRequest.class, request -> { - clientContext.checkThread(); - latch.countDown(); - assertEquals("foo", request.client()); - return CompletableFuture.completedFuture(ConnectResponse.builder() - .withStatus(Response.Status.OK) - .withLeader(new Address(IP_STRING, endpoint2.port())) - .withMembers(Lists.newArrayList(new Address(IP_STRING, endpoint2.port()))) - .build()); - }); - }); - }); - - latch.await(5, TimeUnit.SECONDS); - assertEquals(0, latch.getCount()); - } - - /** - * Tests closing the server side of a Copycat connection. - */ - @Test - public void testCopycatClientConnectionClose() throws Exception { - Client client = clientTransport.client(); - Server server = serverTransport.server(); - - CountDownLatch latch = new CountDownLatch(5); - CountDownLatch listenLatch = new CountDownLatch(1); - serverContext.executor().execute(() -> { - server.listen(new Address(IP_STRING, endpoint2.port()), connection -> { - serverContext.checkThread(); - latch.countDown(); - connection.onClose(c -> { - serverContext.checkThread(); - latch.countDown(); - }); - }).thenRun(listenLatch::countDown); - }); - - listenLatch.await(5, TimeUnit.SECONDS); - - clientContext.executor().execute(() -> { - client.connect(new Address(IP_STRING, endpoint2.port())).thenAccept(connection -> { - clientContext.checkThread(); - latch.countDown(); - connection.onClose(c -> { - clientContext.checkThread(); - latch.countDown(); - }); - clientContext.schedule(Duration.ofMillis(100), () -> { - connection.close().whenComplete((result, error) -> { - clientContext.checkThread(); - latch.countDown(); - }); - }); - }); - }); - - latch.await(5, TimeUnit.SECONDS); - assertEquals(0, latch.getCount()); - } - - /** - * Tests that a client connection is closed on exception. - */ - @Test - public void testCopycatClientConnectionCloseOnException() throws Exception { - Client client = clientTransport.client(); - Server server = serverTransport.server(); - - CountDownLatch listenLatch = new CountDownLatch(1); - CountDownLatch closeLatch = new CountDownLatch(1); - CountDownLatch latch = new CountDownLatch(1); - serverContext.executor().execute(() -> { - server.listen(new Address(IP_STRING, endpoint2.port()), connection -> { - serverContext.checkThread(); - }).thenRun(listenLatch::countDown); - }); - - listenLatch.await(5, TimeUnit.SECONDS); - - clientContext.executor().execute(() -> { - client.connect(new Address(IP_STRING, endpoint2.port())).thenAccept(connection -> { - clientContext.checkThread(); - serverService.handlers.clear(); - connection.onClose(c -> latch.countDown()); - connection.sendAndReceive(ConnectRequest.builder() - .withClientId(UUID.randomUUID().toString()) - .build()) - .thenAccept(response -> fail()); - }); - }); - - latch.await(5, TimeUnit.SECONDS); - assertEquals(0, latch.getCount()); - } - - /** - * Tests closing the server side of a Copycat connection. - */ - @Test - public void testCopycatServerConnectionClose() throws Exception { - Client client = clientTransport.client(); - Server server = serverTransport.server(); - - CountDownLatch latch = new CountDownLatch(5); - CountDownLatch listenLatch = new CountDownLatch(1); - serverContext.executor().execute(() -> { - server.listen(new Address(IP_STRING, endpoint2.port()), connection -> { - serverContext.checkThread(); - latch.countDown(); - connection.onClose(c -> { - latch.countDown(); - }); - serverContext.schedule(Duration.ofMillis(100), () -> { - connection.close().whenComplete((result, error) -> { - serverContext.checkThread(); - latch.countDown(); - }); - }); - }).thenRun(listenLatch::countDown); - }); - - listenLatch.await(5, TimeUnit.SECONDS); - - clientContext.executor().execute(() -> { - client.connect(new Address(IP_STRING, endpoint2.port())).thenAccept(connection -> { - clientContext.checkThread(); - latch.countDown(); - connection.onClose(c -> { - latch.countDown(); - }); - }); - }); - - latch.await(5, TimeUnit.SECONDS); - assertEquals(0, latch.getCount()); - } - - /** - * Tests that a server connection is closed on exception. - */ - @Test - public void testCopycatServerConnectionCloseOnException() throws Exception { - Client client = clientTransport.client(); - Server server = serverTransport.server(); - - CountDownLatch latch = new CountDownLatch(1); - CountDownLatch listenLatch = new CountDownLatch(1); - CountDownLatch connectLatch = new CountDownLatch(1); - serverContext.executor().execute(() -> { - server.listen(new Address(IP_STRING, endpoint2.port()), connection -> { - serverContext.checkThread(); - serverContext.executor().execute(() -> { - try { - connectLatch.await(5, TimeUnit.SECONDS); - } catch (InterruptedException e) { - fail(); - } - clientService.handlers.clear(); - connection.onClose(c -> latch.countDown()); - connection.sendAndReceive(ConnectRequest.builder() - .withClientId("foo") - .build()) - .thenAccept(response -> fail()); - }); - }).thenRun(listenLatch::countDown); - }); - - listenLatch.await(5, TimeUnit.SECONDS); - - clientContext.executor().execute(() -> { - client.connect(new Address(IP_STRING, endpoint2.port())).thenAccept(connection -> { - clientContext.checkThread(); - connectLatch.countDown(); - }); - }); - - latch.await(5, TimeUnit.SECONDS); - assertEquals(0, latch.getCount()); - } - - /** - * Custom implementation of {@code MessagingService} used for testing. Really, this should - * be mocked but suffices for now. - */ - public static final class TestMessagingService implements MessagingService { - private final Endpoint endpoint; - private final Map services; - private final Map>> handlers = - new ConcurrentHashMap<>(); - - TestMessagingService(Endpoint endpoint, Map services) { - this.endpoint = endpoint; - this.services = services; - services.put(endpoint, this); - } - - private CompletableFuture handle(Endpoint ep, String type, byte[] message, Executor executor) { - BiFunction> handler = handlers.get(type); - if (handler == null) { - return Tools.exceptionalFuture(new MessagingException.NoRemoteHandler()); - } - return handler.apply(ep, message).thenApplyAsync(r -> r, executor); - } - - @Override - public CompletableFuture sendAsync(Endpoint ep, String type, byte[] payload) { - // Unused for testing - return null; - } - - @Override - public CompletableFuture sendAndReceive(Endpoint ep, String type, byte[] payload) { - // Unused for testing - return null; - } - - @Override - public CompletableFuture sendAndReceive(Endpoint ep, String type, byte[] payload, Executor executor) { - TestMessagingService service = services.get(ep); - if (service == null) { - return Tools.exceptionalFuture(new IllegalStateException()); - } - return service.handle(endpoint, type, payload, executor); - } - - @Override - public void registerHandler(String type, BiConsumer handler, Executor executor) { - // Unused for testing - } - - @Override - public void registerHandler(String type, BiFunction handler, Executor executor) { - // Unused for testing - } - - @Override - public void registerHandler(String type, BiFunction> handler) { - handlers.put(type, handler); - } - - @Override - public void unregisterHandler(String type) { - handlers.remove(type); - } - } - -} \ No newline at end of file diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapServiceTest.java new file mode 100644 index 0000000000..6d7007a67f --- /dev/null +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapServiceTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.service.ServiceId; +import io.atomix.protocols.raft.service.impl.DefaultCommit; +import io.atomix.protocols.raft.session.impl.RaftSessionContext; +import io.atomix.protocols.raft.storage.RaftStorage; +import io.atomix.protocols.raft.storage.snapshot.Snapshot; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotStore; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import io.atomix.storage.StorageLevel; +import io.atomix.time.WallClockTimestamp; +import org.junit.Test; + +import static org.easymock.EasyMock.mock; +import static org.junit.Assert.assertEquals; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixAtomicCounterMapOperations.PUT; + +/** + * Atomic counter map service test. + */ +public class AtomixAtomicCounterMapServiceTest { + @Test + public void testSnapshot() throws Exception { + SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder() + .withPrefix("test") + .withStorageLevel(StorageLevel.MEMORY) + .build()); + Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp()); + + AtomixAtomicCounterMapService service = new AtomixAtomicCounterMapService(); + service.put(new DefaultCommit<>( + 2, + PUT, + new AtomixAtomicCounterMapOperations.Put("foo", 1), + mock(RaftSessionContext.class), + System.currentTimeMillis())); + + try (SnapshotWriter writer = snapshot.openWriter()) { + service.snapshot(writer); + } + + snapshot.complete(); + + service = new AtomixAtomicCounterMapService(); + try (SnapshotReader reader = snapshot.openReader()) { + service.install(reader); + } + + long value = service.get(new DefaultCommit<>( + 2, + GET, + new AtomixAtomicCounterMapOperations.Get("foo"), + mock(RaftSessionContext.class), + System.currentTimeMillis())); + assertEquals(1, value); + } +} diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapTest.java index 63db59218d..ee44b2917a 100644 --- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapTest.java +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixAtomicCounterMapTest.java @@ -15,9 +15,8 @@ */ package org.onosproject.store.primitives.resources.impl; -import io.atomix.resource.ResourceType; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import io.atomix.protocols.raft.proxy.RaftProxy; +import io.atomix.protocols.raft.service.RaftService; import org.junit.Test; import static org.junit.Assert.assertFalse; @@ -26,21 +25,16 @@ import static org.junit.Assert.assertTrue; /** * Unit test for {@code AtomixCounterMap}. */ -public class AtomixAtomicCounterMapTest extends AtomixTestBase { +public class AtomixAtomicCounterMapTest extends AtomixTestBase { - @BeforeClass - public static void preTestSetup() throws Throwable { - createCopycatServers(3); - } - - @AfterClass - public static void postTestCleanup() throws Exception { - clearTests(); + @Override + protected RaftService createService() { + return new AtomixAtomicCounterMapService(); } @Override - protected ResourceType resourceType() { - return new ResourceType(AtomixAtomicCounterMap.class); + protected AtomixAtomicCounterMap createPrimitive(RaftProxy proxy) { + return new AtomixAtomicCounterMap(proxy); } /** @@ -48,8 +42,7 @@ public class AtomixAtomicCounterMapTest extends AtomixTestBase { */ @Test public void testBasicCounterMapOperations() throws Throwable { - AtomixAtomicCounterMap map = createAtomixClient().getResource("testBasicCounterMapOperationMap", - AtomixAtomicCounterMap.class).join(); + AtomixAtomicCounterMap map = newPrimitive("testBasicCounterMapOperationMap"); map.isEmpty().thenAccept(isEmpty -> { assertTrue(isEmpty); diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapServiceTest.java new file mode 100644 index 0000000000..098c1933ad --- /dev/null +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapServiceTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.service.ServiceId; +import io.atomix.protocols.raft.service.impl.DefaultCommit; +import io.atomix.protocols.raft.session.impl.RaftSessionContext; +import io.atomix.protocols.raft.storage.RaftStorage; +import io.atomix.protocols.raft.storage.snapshot.Snapshot; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotStore; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import io.atomix.storage.StorageLevel; +import io.atomix.time.WallClockTimestamp; +import org.junit.Test; +import org.onlab.util.Match; +import org.onosproject.store.service.Versioned; + +import static org.easymock.EasyMock.mock; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertNotNull; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentMapOperations.UPDATE_AND_GET; + +/** + * Consistent map service test. + */ +public class AtomixConsistentMapServiceTest { + @Test + @SuppressWarnings("unchecked") + public void testSnapshot() throws Exception { + SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder() + .withPrefix("test") + .withStorageLevel(StorageLevel.MEMORY) + .build()); + Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp()); + + AtomixConsistentMapService service = new AtomixConsistentMapService(); + service.updateAndGet(new DefaultCommit<>( + 2, + UPDATE_AND_GET, + new AtomixConsistentMapOperations.UpdateAndGet("foo", "Hello world!".getBytes(), Match.ANY, Match.ANY), + mock(RaftSessionContext.class), + System.currentTimeMillis())); + + try (SnapshotWriter writer = snapshot.openWriter()) { + service.snapshot(writer); + } + + snapshot.complete(); + + service = new AtomixConsistentMapService(); + try (SnapshotReader reader = snapshot.openReader()) { + service.install(reader); + } + + Versioned value = service.get(new DefaultCommit<>( + 2, + GET, + new AtomixConsistentMapOperations.Get("foo"), + mock(RaftSessionContext.class), + System.currentTimeMillis())); + assertNotNull(value); + assertArrayEquals("Hello world!".getBytes(), value.value()); + } +} diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapTest.java index 284b57b116..e858b3f32d 100644 --- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapTest.java +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentMapTest.java @@ -15,11 +15,18 @@ */ package org.onosproject.store.primitives.resources.impl; +import java.util.Arrays; +import java.util.ConcurrentModificationException; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CompletionException; +import java.util.stream.Collectors; + import com.google.common.base.Throwables; import com.google.common.collect.Sets; -import io.atomix.resource.ResourceType; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import io.atomix.protocols.raft.proxy.RaftProxy; +import io.atomix.protocols.raft.service.RaftService; import org.junit.Test; import org.onlab.util.Tools; import org.onosproject.store.primitives.MapUpdate; @@ -30,14 +37,6 @@ import org.onosproject.store.service.TransactionLog; import org.onosproject.store.service.Version; import org.onosproject.store.service.Versioned; -import java.util.Arrays; -import java.util.ConcurrentModificationException; -import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CompletionException; -import java.util.stream.Collectors; - import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -51,20 +50,16 @@ import static org.junit.Assert.fail; /** * Unit tests for {@link AtomixConsistentMap}. */ -public class AtomixConsistentMapTest extends AtomixTestBase { +public class AtomixConsistentMapTest extends AtomixTestBase { - @BeforeClass - public static void preTestSetup() throws Throwable { - createCopycatServers(3); - } - - @AfterClass - public static void postTestCleanup() throws Exception { - clearTests(); - } @Override - protected ResourceType resourceType() { - return new ResourceType(AtomixConsistentMap.class); + protected RaftService createService() { + return new AtomixConsistentMapService(); + } + + @Override + protected AtomixConsistentMap createPrimitive(RaftProxy proxy) { + return new AtomixConsistentMap(proxy); } /** @@ -119,8 +114,7 @@ public class AtomixConsistentMapTest extends AtomixTestBase { final byte[] rawFooValue = Tools.getBytesUtf8("Hello foo!"); final byte[] rawBarValue = Tools.getBytesUtf8("Hello bar!"); - AtomixConsistentMap map = createAtomixClient().getResource("testBasicMapOperationMap", - AtomixConsistentMap.class).join(); + AtomixConsistentMap map = newPrimitive("testBasicMapOperationMap"); map.isEmpty().thenAccept(result -> { assertTrue(result); @@ -249,8 +243,7 @@ public class AtomixConsistentMapTest extends AtomixTestBase { final byte[] value2 = Tools.getBytesUtf8("value2"); final byte[] value3 = Tools.getBytesUtf8("value3"); - AtomixConsistentMap map = createAtomixClient().getResource("testMapComputeOperationsMap", - AtomixConsistentMap.class).join(); + AtomixConsistentMap map = newPrimitive("testMapComputeOperationsMap"); map.computeIfAbsent("foo", k -> value1).thenAccept(result -> { assertTrue(Arrays.equals(Versioned.valueOrElse(result, null), value1)); @@ -287,8 +280,7 @@ public class AtomixConsistentMapTest extends AtomixTestBase { final byte[] value2 = Tools.getBytesUtf8("value2"); final byte[] value3 = Tools.getBytesUtf8("value3"); - AtomixConsistentMap map = createAtomixClient().getResource("testMapListenerMap", - AtomixConsistentMap.class).join(); + AtomixConsistentMap map = newPrimitive("testMapListenerMap"); TestMapEventListener listener = new TestMapEventListener(); // add listener; insert new value into map and verify an INSERT event is received. @@ -343,8 +335,7 @@ public class AtomixConsistentMapTest extends AtomixTestBase { } protected void transactionPrepareTests() throws Throwable { - AtomixConsistentMap map = createAtomixClient().getResource("testPrepareTestsMap", - AtomixConsistentMap.class).join(); + AtomixConsistentMap map = newPrimitive("testPrepareTestsMap"); TransactionId transactionId1 = TransactionId.from("tx1"); TransactionId transactionId2 = TransactionId.from("tx2"); @@ -420,8 +411,7 @@ public class AtomixConsistentMapTest extends AtomixTestBase { final byte[] value1 = Tools.getBytesUtf8("value1"); final byte[] value2 = Tools.getBytesUtf8("value2"); - AtomixConsistentMap map = createAtomixClient().getResource("testCommitTestsMap", - AtomixConsistentMap.class).join(); + AtomixConsistentMap map = newPrimitive("testCommitTestsMap"); TestMapEventListener listener = new TestMapEventListener(); map.addListener(listener).join(); @@ -521,8 +511,7 @@ public class AtomixConsistentMapTest extends AtomixTestBase { final byte[] value1 = Tools.getBytesUtf8("value1"); final byte[] value2 = Tools.getBytesUtf8("value2"); - AtomixConsistentMap map = createAtomixClient().getResource("testTransactionRollbackTestsMap", - AtomixConsistentMap.class).join(); + AtomixConsistentMap map = newPrimitive("testTransactionRollbackTestsMap"); TestMapEventListener listener = new TestMapEventListener(); map.addListener(listener).join(); diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapServiceTest.java new file mode 100644 index 0000000000..836c08c6dd --- /dev/null +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapServiceTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import java.util.Arrays; +import java.util.Collection; + +import io.atomix.protocols.raft.service.ServiceId; +import io.atomix.protocols.raft.service.impl.DefaultCommit; +import io.atomix.protocols.raft.session.impl.RaftSessionContext; +import io.atomix.protocols.raft.storage.RaftStorage; +import io.atomix.protocols.raft.storage.snapshot.Snapshot; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotStore; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import io.atomix.storage.StorageLevel; +import io.atomix.time.WallClockTimestamp; +import org.junit.Test; +import org.onlab.util.Match; +import org.onosproject.store.service.Versioned; + +import static org.easymock.EasyMock.mock; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentSetMultimapOperations.PUT; + +/** + * Consistent set multimap service test. + */ +public class AtomixConsistentSetMultimapServiceTest { + @Test + @SuppressWarnings("unchecked") + public void testSnapshot() throws Exception { + SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder() + .withPrefix("test") + .withStorageLevel(StorageLevel.MEMORY) + .build()); + Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp()); + + AtomixConsistentSetMultimapService service = new AtomixConsistentSetMultimapService(); + service.put(new DefaultCommit<>( + 2, + PUT, + new AtomixConsistentSetMultimapOperations.Put( + "foo", Arrays.asList("Hello world!".getBytes()), Match.ANY), + mock(RaftSessionContext.class), + System.currentTimeMillis())); + + try (SnapshotWriter writer = snapshot.openWriter()) { + service.snapshot(writer); + } + + snapshot.complete(); + + service = new AtomixConsistentSetMultimapService(); + try (SnapshotReader reader = snapshot.openReader()) { + service.install(reader); + } + + Versioned> value = service.get(new DefaultCommit<>( + 2, + GET, + new AtomixConsistentSetMultimapOperations.Get("foo"), + mock(RaftSessionContext.class), + System.currentTimeMillis())); + assertNotNull(value); + assertEquals(1, value.value().size()); + assertArrayEquals("Hello world!".getBytes(), value.value().iterator().next()); + } +} diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapTest.java index b1ec1f8f93..2d0191209d 100644 --- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapTest.java +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentSetMultimapTest.java @@ -16,22 +16,21 @@ package org.onosproject.store.primitives.resources.impl; -import com.google.common.collect.Lists; -import com.google.common.collect.Multiset; -import com.google.common.collect.TreeMultiset; -import io.atomix.resource.ResourceType; -import org.apache.commons.collections.keyvalue.DefaultMapEntry; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.onlab.util.Tools; - import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; +import com.google.common.collect.Lists; +import com.google.common.collect.Multiset; +import com.google.common.collect.TreeMultiset; +import io.atomix.protocols.raft.proxy.RaftProxy; +import io.atomix.protocols.raft.service.RaftService; +import org.apache.commons.collections.keyvalue.DefaultMapEntry; +import org.junit.Test; +import org.onlab.util.Tools; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -39,7 +38,7 @@ import static org.junit.Assert.assertTrue; /** * Tests the {@link AtomixConsistentSetMultimap}. */ -public class AtomixConsistentSetMultimapTest extends AtomixTestBase { +public class AtomixConsistentSetMultimapTest extends AtomixTestBase { private final String keyOne = "hello"; private final String keyTwo = "goodbye"; private final String keyThree = "foo"; @@ -55,19 +54,14 @@ public class AtomixConsistentSetMultimapTest extends AtomixTestBase { valueThree, valueFour); - @BeforeClass - public static void preTestSetup() throws Throwable { - createCopycatServers(3); - } - - @AfterClass - public static void postTestCleanup() throws Exception { - clearTests(); + @Override + protected RaftService createService() { + return new AtomixConsistentSetMultimapService(); } @Override - protected ResourceType resourceType() { - return new ResourceType(AtomixConsistentSetMultimap.class); + protected AtomixConsistentSetMultimap createPrimitive(RaftProxy proxy) { + return new AtomixConsistentSetMultimap(proxy); } /** @@ -154,9 +148,10 @@ public class AtomixConsistentSetMultimapTest extends AtomixTestBase { }); }); + final String[] removedKey = new String[1]; + //Test behavior after removals allValues.forEach(value -> { - final String[] removedKey = new String[1]; allKeys.forEach(key -> { map.remove(key, value) .thenAccept(result -> assertTrue(result)).join(); @@ -164,11 +159,12 @@ public class AtomixConsistentSetMultimapTest extends AtomixTestBase { .thenAccept(result -> assertFalse(result)).join(); removedKey[0] = key; }); - //Check that contains key works properly for removed keys - map.containsKey(removedKey[0]) - .thenAccept(result -> assertFalse(result)); }); + //Check that contains key works properly for removed keys + map.containsKey(removedKey[0]) + .thenAccept(result -> assertFalse(result)); + //Check that contains value works correctly for removed values allValues.forEach(value -> { map.containsValue(value) @@ -403,9 +399,7 @@ public class AtomixConsistentSetMultimapTest extends AtomixTestBase { private AtomixConsistentSetMultimap createResource(String mapName) { try { - AtomixConsistentSetMultimap map = createAtomixClient(). - getResource(mapName, AtomixConsistentSetMultimap.class) - .join(); + AtomixConsistentSetMultimap map = newPrimitive(mapName); return map; } catch (Throwable e) { throw new RuntimeException(e.toString()); diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapServiceTest.java new file mode 100644 index 0000000000..04698e0b1f --- /dev/null +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapServiceTest.java @@ -0,0 +1,80 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.service.ServiceId; +import io.atomix.protocols.raft.service.impl.DefaultCommit; +import io.atomix.protocols.raft.session.impl.RaftSessionContext; +import io.atomix.protocols.raft.storage.RaftStorage; +import io.atomix.protocols.raft.storage.snapshot.Snapshot; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotStore; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import io.atomix.storage.StorageLevel; +import io.atomix.time.WallClockTimestamp; +import org.junit.Test; +import org.onlab.util.Match; +import org.onosproject.store.service.Versioned; + +import static org.easymock.EasyMock.mock; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertNotNull; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixConsistentTreeMapOperations.UPDATE_AND_GET; + +/** + * Consistent tree map service test. + */ +public class AtomixConsistentTreeMapServiceTest { + @Test + @SuppressWarnings("unchecked") + public void testSnapshot() throws Exception { + SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder() + .withPrefix("test") + .withStorageLevel(StorageLevel.MEMORY) + .build()); + Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp()); + + AtomixConsistentTreeMapService service = new AtomixConsistentTreeMapService(); + service.updateAndGet(new DefaultCommit<>( + 2, + UPDATE_AND_GET, + new AtomixConsistentTreeMapOperations.UpdateAndGet( + "foo", "Hello world!".getBytes(), Match.ANY, Match.ANY), + mock(RaftSessionContext.class), + System.currentTimeMillis())); + + try (SnapshotWriter writer = snapshot.openWriter()) { + service.snapshot(writer); + } + + snapshot.complete(); + + service = new AtomixConsistentTreeMapService(); + try (SnapshotReader reader = snapshot.openReader()) { + service.install(reader); + } + + Versioned value = service.get(new DefaultCommit<>( + 2, + GET, + new AtomixConsistentTreeMapOperations.Get("foo"), + mock(RaftSessionContext.class), + System.currentTimeMillis())); + assertNotNull(value); + assertArrayEquals("Hello world!".getBytes(), value.value()); + } +} diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapTest.java index bbaaf57aa2..f1de625ca7 100644 --- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapTest.java +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixConsistentTreeMapTest.java @@ -15,16 +15,6 @@ */ package org.onosproject.store.primitives.resources.impl; -import com.google.common.base.Throwables; -import com.google.common.collect.Lists; -import io.atomix.resource.ResourceType; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.onlab.util.Tools; -import org.onosproject.store.service.MapEvent; -import org.onosproject.store.service.MapEventListener; - import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -33,6 +23,15 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.stream.Collectors; +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import io.atomix.protocols.raft.proxy.RaftProxy; +import io.atomix.protocols.raft.service.RaftService; +import org.junit.Test; +import org.onlab.util.Tools; +import org.onosproject.store.service.MapEvent; +import org.onosproject.store.service.MapEventListener; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -44,7 +43,7 @@ import static org.junit.Assert.assertTrue; /** * Unit tests for {@link AtomixConsistentTreeMap}. */ -public class AtomixConsistentTreeMapTest extends AtomixTestBase { +public class AtomixConsistentTreeMapTest extends AtomixTestBase { private final String keyFour = "hello"; private final String keyThree = "goodbye"; private final String keyTwo = "foo"; @@ -60,19 +59,15 @@ public class AtomixConsistentTreeMapTest extends AtomixTestBase { valueTwo, valueThree, valueFour); - @BeforeClass - public static void preTestSetup() throws Throwable { - createCopycatServers(3); - } - @AfterClass - public static void postTestCleanup() throws Throwable { - clearTests(); + @Override + protected RaftService createService() { + return new AtomixConsistentTreeMapService(); } @Override - protected ResourceType resourceType() { - return new ResourceType(AtomixConsistentTreeMap.class); + protected AtomixConsistentTreeMap createPrimitive(RaftProxy proxy) { + return new AtomixConsistentTreeMap(proxy); } /** @@ -359,7 +354,9 @@ public class AtomixConsistentTreeMapTest extends AtomixTestBase { map.ceilingKey(keyOne).thenAccept(result -> assertNull(result)) .join(); map.higherKey(keyOne).thenAccept(result -> assertNull(result)).join(); - map.delete().join(); + + // TODO: delete() is not supported + //map.delete().join(); allKeys.forEach(key -> map.put( key, allValues.get(allKeys.indexOf(key))) @@ -481,15 +478,14 @@ public class AtomixConsistentTreeMapTest extends AtomixTestBase { map.higherKey(keyFour).thenAccept( result -> assertNull(result)) .join(); - map.delete().join(); + // TODO: delete() is not supported + //map.delete().join(); } private AtomixConsistentTreeMap createResource(String mapName) { try { - AtomixConsistentTreeMap map = createAtomixClient(). - getResource(mapName, AtomixConsistentTreeMap.class) - .join(); + AtomixConsistentTreeMap map = newPrimitive(mapName); return map; } catch (Throwable e) { throw new RuntimeException(e.toString()); diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterServiceTest.java new file mode 100644 index 0000000000..707aa917b9 --- /dev/null +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterServiceTest.java @@ -0,0 +1,74 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.service.ServiceId; +import io.atomix.protocols.raft.service.impl.DefaultCommit; +import io.atomix.protocols.raft.session.impl.RaftSessionContext; +import io.atomix.protocols.raft.storage.RaftStorage; +import io.atomix.protocols.raft.storage.snapshot.Snapshot; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotStore; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import io.atomix.storage.StorageLevel; +import io.atomix.time.WallClockTimestamp; +import org.junit.Test; + +import static org.easymock.EasyMock.mock; +import static org.junit.Assert.assertEquals; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixCounterOperations.SET; + +/** + * Counter service test. + */ +public class AtomixCounterServiceTest { + @Test + public void testSnapshot() throws Exception { + SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder() + .withPrefix("test") + .withStorageLevel(StorageLevel.MEMORY) + .build()); + Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp()); + + AtomixCounterService service = new AtomixCounterService(); + service.set(new DefaultCommit<>( + 2, + SET, + new AtomixCounterOperations.Set(1L), + mock(RaftSessionContext.class), + System.currentTimeMillis())); + + try (SnapshotWriter writer = snapshot.openWriter()) { + service.snapshot(writer); + } + + snapshot.complete(); + + service = new AtomixCounterService(); + try (SnapshotReader reader = snapshot.openReader()) { + service.install(reader); + } + + long value = service.get(new DefaultCommit<>( + 2, + GET, + null, + mock(RaftSessionContext.class), + System.currentTimeMillis())); + assertEquals(1, value); + } +} diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLongTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterTest.java similarity index 69% rename from core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLongTest.java rename to core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterTest.java index eb835cdb1d..2e334d6bf9 100644 --- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLongTest.java +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixCounterTest.java @@ -15,35 +15,26 @@ */ package org.onosproject.store.primitives.resources.impl; -import io.atomix.Atomix; -import io.atomix.resource.ResourceType; -import io.atomix.variables.DistributedLong; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import io.atomix.protocols.raft.proxy.RaftProxy; +import io.atomix.protocols.raft.service.RaftService; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -/**git s +/** * Unit tests for {@link AtomixCounter}. */ -public class AtomixLongTest extends AtomixTestBase { - - @BeforeClass - public static void preTestSetup() throws Throwable { - createCopycatServers(3); - } - - @AfterClass - public static void postTestCleanup() throws Exception { - clearTests(); +public class AtomixCounterTest extends AtomixTestBase { + @Override + protected RaftService createService() { + return new AtomixCounterService(); } @Override - protected ResourceType resourceType() { - return new ResourceType(DistributedLong.class); + protected AtomixCounter createPrimitive(RaftProxy proxy) { + return new AtomixCounter(proxy); } @Test @@ -52,9 +43,7 @@ public class AtomixLongTest extends AtomixTestBase { } protected void basicOperationsTest() throws Throwable { - Atomix atomix = createAtomixClient(); - AtomixCounter along = new AtomixCounter("test-long-basic-operations", - atomix.getLong("test-long").join()); + AtomixCounter along = newPrimitive("test-counter-basic-operations"); assertEquals(0, along.get().join().longValue()); assertEquals(1, along.incrementAndGet().join().longValue()); along.set(100).join(); diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeServiceTest.java new file mode 100644 index 0000000000..8e1ae91d62 --- /dev/null +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeServiceTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import java.util.Optional; + +import io.atomix.protocols.raft.service.ServiceId; +import io.atomix.protocols.raft.service.impl.DefaultCommit; +import io.atomix.protocols.raft.session.impl.RaftSessionContext; +import io.atomix.protocols.raft.storage.RaftStorage; +import io.atomix.protocols.raft.storage.snapshot.Snapshot; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotStore; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import io.atomix.storage.StorageLevel; +import io.atomix.time.WallClockTimestamp; +import org.junit.Test; +import org.onlab.util.Match; +import org.onosproject.store.service.DocumentPath; +import org.onosproject.store.service.Versioned; + +import static org.easymock.EasyMock.mock; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertNotNull; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.GET; +import static org.onosproject.store.primitives.resources.impl.AtomixDocumentTreeOperations.UPDATE; + +/** + * Document tree service test. + */ +public class AtomixDocumentTreeServiceTest { + @Test + public void testSnapshot() throws Exception { + SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder() + .withPrefix("test") + .withStorageLevel(StorageLevel.MEMORY) + .build()); + Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp()); + + AtomixDocumentTreeService service = new AtomixDocumentTreeService(); + service.update(new DefaultCommit<>( + 2, + UPDATE, + new AtomixDocumentTreeOperations.Update( + DocumentPath.from("root|foo"), + Optional.of("Hello world!".getBytes()), + Match.any(), + Match.ifNull()), + mock(RaftSessionContext.class), + System.currentTimeMillis())); + + try (SnapshotWriter writer = snapshot.openWriter()) { + service.snapshot(writer); + } + + snapshot.complete(); + + service = new AtomixDocumentTreeService(); + try (SnapshotReader reader = snapshot.openReader()) { + service.install(reader); + } + + Versioned value = service.get(new DefaultCommit<>( + 2, + GET, + new AtomixDocumentTreeOperations.Get(DocumentPath.from("root|foo")), + mock(RaftSessionContext.class), + System.currentTimeMillis())); + assertNotNull(value); + assertArrayEquals("Hello world!".getBytes(), value.value()); + } +} diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeTest.java index dbf8c665f3..ec81c410bf 100644 --- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeTest.java +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixDocumentTreeTest.java @@ -16,22 +16,14 @@ package org.onosproject.store.primitives.resources.impl; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import io.atomix.AtomixClient; -import io.atomix.resource.ResourceType; - import java.util.Map; import java.util.UUID; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import com.google.common.base.Throwables; +import io.atomix.protocols.raft.proxy.RaftProxy; +import io.atomix.protocols.raft.service.RaftService; import org.junit.Test; import org.onosproject.store.service.DocumentPath; import org.onosproject.store.service.DocumentTreeEvent; @@ -40,32 +32,34 @@ import org.onosproject.store.service.IllegalDocumentModificationException; import org.onosproject.store.service.NoSuchDocumentPathException; import org.onosproject.store.service.Versioned; -import com.google.common.base.Throwables; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; /** * Unit tests for {@link AtomixDocumentTree}. */ -public class AtomixDocumentTreeTest extends AtomixTestBase { - @BeforeClass - public static void preTestSetup() throws Throwable { - createCopycatServers(3); +public class AtomixDocumentTreeTest extends AtomixTestBase { + + @Override + protected RaftService createService() { + return new AtomixDocumentTreeService(); } - @AfterClass - public static void postTestCleanup() throws Exception { - clearTests(); - } @Override - protected ResourceType resourceType() { - return new ResourceType(AtomixDocumentTree.class); + protected AtomixDocumentTree createPrimitive(RaftProxy proxy) { + return new AtomixDocumentTree(proxy); } + /** * Tests queries (get and getChildren). */ @Test public void testQueries() throws Throwable { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); Versioned root = tree.get(path("root")).join(); assertEquals(1, root.version()); assertNull(root.value()); @@ -76,8 +70,7 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { */ @Test public void testCreate() throws Throwable { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); tree.create(path("root.a"), "a".getBytes()).join(); tree.create(path("root.a.b"), "ab".getBytes()).join(); tree.create(path("root.a.c"), "ac".getBytes()).join(); @@ -100,8 +93,7 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { */ @Test public void testRecursiveCreate() throws Throwable { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); tree.createRecursive(path("root.a.b.c"), "abc".getBytes()).join(); Versioned a = tree.get(path("root.a")).join(); assertArrayEquals(null, a.value()); @@ -118,8 +110,7 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { */ @Test public void testSet() throws Throwable { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); tree.create(path("root.a"), "a".getBytes()).join(); tree.create(path("root.a.b"), "ab".getBytes()).join(); tree.create(path("root.a.c"), "ac".getBytes()).join(); @@ -146,8 +137,7 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { */ @Test public void testReplaceVersion() throws Throwable { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); tree.create(path("root.a"), "a".getBytes()).join(); tree.create(path("root.a.b"), "ab".getBytes()).join(); tree.create(path("root.a.c"), "ac".getBytes()).join(); @@ -168,8 +158,7 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { */ @Test public void testReplaceValue() throws Throwable { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); tree.create(path("root.a"), "a".getBytes()).join(); tree.create(path("root.a.b"), "ab".getBytes()).join(); tree.create(path("root.a.c"), "ac".getBytes()).join(); @@ -190,8 +179,7 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { */ @Test public void testRemove() throws Throwable { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); tree.create(path("root.a"), "a".getBytes()).join(); tree.create(path("root.a.b"), "ab".getBytes()).join(); tree.create(path("root.a.c"), "ac".getBytes()).join(); @@ -219,8 +207,7 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { */ @Test public void testRemoveFailures() throws Throwable { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); tree.create(path("root.a"), "a".getBytes()).join(); tree.create(path("root.a.b"), "ab".getBytes()).join(); tree.create(path("root.a.c"), "ac".getBytes()).join(); @@ -252,8 +239,7 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { */ @Test public void testCreateFailures() throws Throwable { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); try { tree.create(path("root.a.c"), "ac".getBytes()).join(); fail(); @@ -267,8 +253,7 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { */ @Test public void testSetFailures() throws Throwable { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); try { tree.set(path("root.a.c"), "ac".getBytes()).join(); fail(); @@ -282,8 +267,7 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { */ @Test public void testGetChildren() throws Throwable { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); tree.create(path("root.a"), "a".getBytes()).join(); tree.create(path("root.a.b"), "ab".getBytes()).join(); tree.create(path("root.a.c"), "ac".getBytes()).join(); @@ -309,8 +293,7 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { */ @Test public void testClear() { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); tree.create(path("root.a"), "a".getBytes()).join(); tree.create(path("root.a.b"), "ab".getBytes()).join(); tree.create(path("root.a.c"), "ac".getBytes()).join(); @@ -324,8 +307,7 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { */ @Test public void testNotifications() throws Exception { - AtomixDocumentTree tree = createAtomixClient().getResource(UUID.randomUUID().toString(), - AtomixDocumentTree.class).join(); + AtomixDocumentTree tree = newPrimitive(UUID.randomUUID().toString()); TestEventListener listener = new TestEventListener(); // add listener; create a node in the tree and verify an CREATED event is received. @@ -359,12 +341,9 @@ public class AtomixDocumentTreeTest extends AtomixTestBase { @Test public void testFilteredNotifications() throws Throwable { - AtomixClient client1 = createAtomixClient(); - AtomixClient client2 = createAtomixClient(); - String treeName = UUID.randomUUID().toString(); - AtomixDocumentTree tree1 = client1.getResource(treeName, AtomixDocumentTree.class).join(); - AtomixDocumentTree tree2 = client2.getResource(treeName, AtomixDocumentTree.class).join(); + AtomixDocumentTree tree1 = newPrimitive(treeName); + AtomixDocumentTree tree2 = newPrimitive(treeName); TestEventListener listener1a = new TestEventListener(3); TestEventListener listener1ab = new TestEventListener(2); diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixIdGeneratorTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixIdGeneratorTest.java index 1fbc464754..2a9ca02632 100644 --- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixIdGeneratorTest.java +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixIdGeneratorTest.java @@ -17,10 +17,8 @@ package org.onosproject.store.primitives.resources.impl; import java.util.concurrent.CompletableFuture; -import io.atomix.resource.ResourceType; -import io.atomix.variables.DistributedLong; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import io.atomix.protocols.raft.proxy.RaftProxy; +import io.atomix.protocols.raft.service.RaftService; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -28,21 +26,16 @@ import static org.junit.Assert.assertEquals; /** * Unit test for {@code AtomixIdGenerator}. */ -public class AtomixIdGeneratorTest extends AtomixTestBase { +public class AtomixIdGeneratorTest extends AtomixTestBase { - @BeforeClass - public static void preTestSetup() throws Throwable { - createCopycatServers(3); - } - - @AfterClass - public static void postTestCleanup() throws Exception { - clearTests(); + @Override + protected RaftService createService() { + return new AtomixCounterService(); } @Override - protected ResourceType resourceType() { - return new ResourceType(DistributedLong.class); + protected AtomixCounter createPrimitive(RaftProxy proxy) { + return new AtomixCounter(proxy); } /** @@ -50,10 +43,8 @@ public class AtomixIdGeneratorTest extends AtomixTestBase { */ @Test public void testNextId() throws Throwable { - AtomixIdGenerator idGenerator1 = new AtomixIdGenerator("testNextId", - createAtomixClient().getLong("testNextId").join()); - AtomixIdGenerator idGenerator2 = new AtomixIdGenerator("testNextId", - createAtomixClient().getLong("testNextId").join()); + AtomixIdGenerator idGenerator1 = new AtomixIdGenerator(newPrimitive("testNextId")); + AtomixIdGenerator idGenerator2 = new AtomixIdGenerator(newPrimitive("testNextId")); CompletableFuture future11 = idGenerator1.nextId(); CompletableFuture future12 = idGenerator1.nextId(); @@ -82,10 +73,8 @@ public class AtomixIdGeneratorTest extends AtomixTestBase { */ @Test public void testNextIdBatchRollover() throws Throwable { - AtomixIdGenerator idGenerator1 = new AtomixIdGenerator("testNextIdBatchRollover", - createAtomixClient().getLong("testNextIdBatchRollover").join(), 2); - AtomixIdGenerator idGenerator2 = new AtomixIdGenerator("testNextIdBatchRollover", - createAtomixClient().getLong("testNextIdBatchRollover").join(), 2); + AtomixIdGenerator idGenerator1 = new AtomixIdGenerator(newPrimitive("testNextIdBatchRollover"), 2); + AtomixIdGenerator idGenerator2 = new AtomixIdGenerator(newPrimitive("testNextIdBatchRollover"), 2); CompletableFuture future11 = idGenerator1.nextId(); CompletableFuture future12 = idGenerator1.nextId(); diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorServiceTest.java new file mode 100644 index 0000000000..d3b6343b73 --- /dev/null +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorServiceTest.java @@ -0,0 +1,113 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import io.atomix.protocols.raft.ReadConsistency; +import io.atomix.protocols.raft.cluster.MemberId; +import io.atomix.protocols.raft.impl.RaftServerContext; +import io.atomix.protocols.raft.protocol.RaftServerProtocol; +import io.atomix.protocols.raft.service.ServiceId; +import io.atomix.protocols.raft.service.ServiceType; +import io.atomix.protocols.raft.service.impl.DefaultCommit; +import io.atomix.protocols.raft.service.impl.DefaultServiceContext; +import io.atomix.protocols.raft.session.SessionId; +import io.atomix.protocols.raft.session.impl.RaftSessionContext; +import io.atomix.protocols.raft.storage.RaftStorage; +import io.atomix.protocols.raft.storage.snapshot.Snapshot; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotStore; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import io.atomix.storage.StorageLevel; +import io.atomix.time.WallClockTimestamp; +import io.atomix.utils.concurrent.ThreadContext; +import org.junit.Test; +import org.onosproject.cluster.Leadership; +import org.onosproject.cluster.NodeId; + +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.mock; +import static org.easymock.EasyMock.replay; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.GET_LEADERSHIP; +import static org.onosproject.store.primitives.resources.impl.AtomixLeaderElectorOperations.RUN; +import static org.onosproject.store.service.DistributedPrimitive.Type.LEADER_ELECTOR; + +/** + * Leader elector service test. + */ +public class AtomixLeaderElectorServiceTest { + @Test + public void testSnapshot() throws Exception { + SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder() + .withPrefix("test") + .withStorageLevel(StorageLevel.MEMORY) + .build()); + Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp()); + + DefaultServiceContext context = mock(DefaultServiceContext.class); + expect(context.serviceType()).andReturn(ServiceType.from(LEADER_ELECTOR.name())).anyTimes(); + expect(context.serviceName()).andReturn("test").anyTimes(); + expect(context.serviceId()).andReturn(ServiceId.from(1)).anyTimes(); + expect(context.executor()).andReturn(mock(ThreadContext.class)).anyTimes(); + + RaftServerContext server = mock(RaftServerContext.class); + expect(server.getProtocol()).andReturn(mock(RaftServerProtocol.class)); + + replay(context, server); + + AtomixLeaderElectorService service = new AtomixLeaderElectorService(); + service.init(context); + + NodeId nodeId = NodeId.nodeId("1"); + service.run(new DefaultCommit<>( + 2, + RUN, + new AtomixLeaderElectorOperations.Run("test", nodeId), + new RaftSessionContext( + SessionId.from(1), + MemberId.from("1"), + "test", + ServiceType.from(LEADER_ELECTOR.name()), + ReadConsistency.LINEARIZABLE, + 5000, + context, + server), + System.currentTimeMillis())); + + try (SnapshotWriter writer = snapshot.openWriter()) { + service.snapshot(writer); + } + + snapshot.complete(); + + service = new AtomixLeaderElectorService(); + service.init(context); + + try (SnapshotReader reader = snapshot.openReader()) { + service.install(reader); + } + + Leadership value = service.getLeadership(new DefaultCommit<>( + 2, + GET_LEADERSHIP, + new AtomixLeaderElectorOperations.GetLeadership("test"), + mock(RaftSessionContext.class), + System.currentTimeMillis())); + assertNotNull(value); + assertEquals(value.leader().nodeId(), nodeId); + } +} diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorTest.java index 00df1fcb5d..4bd6d7fdc7 100644 --- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorTest.java +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixLeaderElectorTest.java @@ -15,21 +15,18 @@ */ package org.onosproject.store.primitives.resources.impl; -import io.atomix.Atomix; -import io.atomix.AtomixClient; -import io.atomix.resource.ResourceType; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.onosproject.cluster.Leadership; -import org.onosproject.cluster.NodeId; -import org.onosproject.event.Change; - import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; +import io.atomix.protocols.raft.proxy.RaftProxy; +import io.atomix.protocols.raft.service.RaftService; +import org.junit.Test; +import org.onosproject.cluster.Leadership; +import org.onosproject.cluster.NodeId; +import org.onosproject.event.Change; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -37,25 +34,20 @@ import static org.junit.Assert.assertTrue; /** * Unit tests for {@link AtomixLeaderElector}. */ -public class AtomixLeaderElectorTest extends AtomixTestBase { +public class AtomixLeaderElectorTest extends AtomixTestBase { NodeId node1 = new NodeId("node1"); NodeId node2 = new NodeId("node2"); NodeId node3 = new NodeId("node3"); - @BeforeClass - public static void preTestSetup() throws Throwable { - createCopycatServers(3); - } - - @AfterClass - public static void postTestCleanup() throws Exception { - clearTests(); + @Override + protected RaftService createService() { + return new AtomixLeaderElectorService(); } @Override - protected ResourceType resourceType() { - return new ResourceType(AtomixLeaderElector.class); + protected AtomixLeaderElector createPrimitive(RaftProxy proxy) { + return new AtomixLeaderElector(proxy); } @Test @@ -64,18 +56,15 @@ public class AtomixLeaderElectorTest extends AtomixTestBase { } private void leaderElectorRunTests() throws Throwable { - Atomix client1 = createAtomixClient(); - AtomixLeaderElector elector1 = client1.getResource("test-elector-run", - AtomixLeaderElector.class).join(); + AtomixLeaderElector elector1 = newPrimitive("test-elector-run"); elector1.run("foo", node1).thenAccept(result -> { assertEquals(node1, result.leaderNodeId()); assertEquals(1, result.leader().term()); assertEquals(1, result.candidates().size()); assertEquals(node1, result.candidates().get(0)); }).join(); - Atomix client2 = createAtomixClient(); - AtomixLeaderElector elector2 = client2.getResource("test-elector-run", - AtomixLeaderElector.class).join(); + + AtomixLeaderElector elector2 = newPrimitive("test-elector-run"); elector2.run("foo", node2).thenAccept(result -> { assertEquals(node1, result.leaderNodeId()); assertEquals(1, result.leader().term()); @@ -91,13 +80,9 @@ public class AtomixLeaderElectorTest extends AtomixTestBase { } private void leaderElectorWithdrawTests() throws Throwable { - Atomix client1 = createAtomixClient(); - AtomixLeaderElector elector1 = client1.getResource("test-elector-withdraw", - AtomixLeaderElector.class).join(); + AtomixLeaderElector elector1 = newPrimitive("test-elector-withdraw"); elector1.run("foo", node1).join(); - Atomix client2 = createAtomixClient(); - AtomixLeaderElector elector2 = client2.getResource("test-elector-withdraw", - AtomixLeaderElector.class).join(); + AtomixLeaderElector elector2 = newPrimitive("test-elector-withdraw"); elector2.run("foo", node2).join(); LeaderEventListener listener1 = new LeaderEventListener(); @@ -121,6 +106,14 @@ public class AtomixLeaderElectorTest extends AtomixTestBase { assertEquals(1, result.newValue().candidates().size()); assertEquals(node2, result.newValue().candidates().get(0)); }).join(); + + Leadership leadership1 = elector1.getLeadership("foo").join(); + assertEquals(node2, leadership1.leader().nodeId()); + assertEquals(1, leadership1.candidates().size()); + + Leadership leadership2 = elector2.getLeadership("foo").join(); + assertEquals(node2, leadership2.leader().nodeId()); + assertEquals(1, leadership2.candidates().size()); } @Test @@ -129,15 +122,9 @@ public class AtomixLeaderElectorTest extends AtomixTestBase { } private void leaderElectorAnointTests() throws Throwable { - Atomix client1 = createAtomixClient(); - AtomixLeaderElector elector1 = client1.getResource("test-elector-anoint", - AtomixLeaderElector.class).join(); - Atomix client2 = createAtomixClient(); - AtomixLeaderElector elector2 = client2.getResource("test-elector-anoint", - AtomixLeaderElector.class).join(); - Atomix client3 = createAtomixClient(); - AtomixLeaderElector elector3 = client3.getResource("test-elector-anoint", - AtomixLeaderElector.class).join(); + AtomixLeaderElector elector1 = newPrimitive("test-elector-anoint"); + AtomixLeaderElector elector2 = newPrimitive("test-elector-anoint"); + AtomixLeaderElector elector3 = newPrimitive("test-elector-anoint"); elector1.run("foo", node1).join(); elector2.run("foo", node2).join(); @@ -185,15 +172,9 @@ public class AtomixLeaderElectorTest extends AtomixTestBase { } private void leaderElectorPromoteTests() throws Throwable { - AtomixClient client1 = createAtomixClient(); - AtomixLeaderElector elector1 = client1.getResource("test-elector-promote", - AtomixLeaderElector.class).join(); - AtomixClient client2 = createAtomixClient(); - AtomixLeaderElector elector2 = client2.getResource("test-elector-promote", - AtomixLeaderElector.class).join(); - AtomixClient client3 = createAtomixClient(); - AtomixLeaderElector elector3 = client3.getResource("test-elector-promote", - AtomixLeaderElector.class).join(); + AtomixLeaderElector elector1 = newPrimitive("test-elector-promote"); + AtomixLeaderElector elector2 = newPrimitive("test-elector-promote"); + AtomixLeaderElector elector3 = newPrimitive("test-elector-promote"); elector1.run("foo", node1).join(); elector2.run("foo", node2).join(); @@ -245,17 +226,13 @@ public class AtomixLeaderElectorTest extends AtomixTestBase { } private void leaderElectorLeaderSessionCloseTests() throws Throwable { - AtomixClient client1 = createAtomixClient(); - AtomixLeaderElector elector1 = client1.getResource("test-elector-leader-session-close", - AtomixLeaderElector.class).join(); + AtomixLeaderElector elector1 = newPrimitive("test-elector-leader-session-close"); elector1.run("foo", node1).join(); - Atomix client2 = createAtomixClient(); - AtomixLeaderElector elector2 = client2.getResource("test-elector-leader-session-close", - AtomixLeaderElector.class).join(); + AtomixLeaderElector elector2 = newPrimitive("test-elector-leader-session-close"); LeaderEventListener listener = new LeaderEventListener(); elector2.run("foo", node2).join(); elector2.addChangeListener(listener).join(); - client1.close(); + elector1.proxy.close(); listener.nextEvent().thenAccept(result -> { assertEquals(node2, result.newValue().leaderNodeId()); assertEquals(1, result.newValue().candidates().size()); @@ -269,17 +246,13 @@ public class AtomixLeaderElectorTest extends AtomixTestBase { } private void leaderElectorNonLeaderSessionCloseTests() throws Throwable { - Atomix client1 = createAtomixClient(); - AtomixLeaderElector elector1 = client1.getResource("test-elector-non-leader-session-close", - AtomixLeaderElector.class).join(); + AtomixLeaderElector elector1 = newPrimitive("test-elector-non-leader-session-close"); elector1.run("foo", node1).join(); - AtomixClient client2 = createAtomixClient(); - AtomixLeaderElector elector2 = client2.getResource("test-elector-non-leader-session-close", - AtomixLeaderElector.class).join(); + AtomixLeaderElector elector2 = newPrimitive("test-elector-non-leader-session-close"); LeaderEventListener listener = new LeaderEventListener(); elector2.run("foo", node2).join(); elector1.addChangeListener(listener).join(); - client2.close().join(); + elector2.proxy.close().join(); listener.nextEvent().thenAccept(result -> { assertEquals(node1, result.newValue().leaderNodeId()); assertEquals(1, result.newValue().candidates().size()); @@ -293,12 +266,8 @@ public class AtomixLeaderElectorTest extends AtomixTestBase { } private void leaderElectorQueryTests() throws Throwable { - Atomix client1 = createAtomixClient(); - Atomix client2 = createAtomixClient(); - AtomixLeaderElector elector1 = client1.getResource("test-elector-query", - AtomixLeaderElector.class).join(); - AtomixLeaderElector elector2 = client2.getResource("test-elector-query", - AtomixLeaderElector.class).join(); + AtomixLeaderElector elector1 = newPrimitive("test-elector-query"); + AtomixLeaderElector elector2 = newPrimitive("test-elector-query"); elector1.run("foo", node1).join(); elector2.run("foo", node2).join(); elector2.run("bar", node2).join(); diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixTestBase.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixTestBase.java index f7a5007351..7073bb501a 100644 --- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixTestBase.java +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixTestBase.java @@ -15,133 +15,490 @@ */ package org.onosproject.store.primitives.resources.impl; -import com.google.common.util.concurrent.Uninterruptibles; - -import io.atomix.AtomixClient; -import io.atomix.catalyst.serializer.Serializer; -import io.atomix.catalyst.transport.Address; -import io.atomix.catalyst.transport.local.LocalServerRegistry; -import io.atomix.catalyst.transport.netty.NettyTransport; -import io.atomix.copycat.client.CopycatClient; -import io.atomix.copycat.server.CopycatServer; -import io.atomix.copycat.server.storage.Storage; -import io.atomix.copycat.server.storage.StorageLevel; -import io.atomix.manager.internal.ResourceManagerState; -import io.atomix.resource.ResourceType; -import org.onlab.junit.TestTools; -import org.onosproject.store.primitives.impl.CatalystSerializers; - +import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.time.Instant; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import com.google.common.collect.Lists; +import io.atomix.protocols.raft.RaftClient; +import io.atomix.protocols.raft.RaftError; +import io.atomix.protocols.raft.RaftServer; +import io.atomix.protocols.raft.ReadConsistency; +import io.atomix.protocols.raft.cluster.MemberId; +import io.atomix.protocols.raft.cluster.RaftMember; +import io.atomix.protocols.raft.cluster.impl.DefaultRaftMember; +import io.atomix.protocols.raft.event.RaftEvent; +import io.atomix.protocols.raft.event.impl.DefaultEventType; +import io.atomix.protocols.raft.operation.OperationType; +import io.atomix.protocols.raft.operation.RaftOperation; +import io.atomix.protocols.raft.operation.impl.DefaultOperationId; +import io.atomix.protocols.raft.protocol.AppendRequest; +import io.atomix.protocols.raft.protocol.AppendResponse; +import io.atomix.protocols.raft.protocol.CloseSessionRequest; +import io.atomix.protocols.raft.protocol.CloseSessionResponse; +import io.atomix.protocols.raft.protocol.CommandRequest; +import io.atomix.protocols.raft.protocol.CommandResponse; +import io.atomix.protocols.raft.protocol.ConfigureRequest; +import io.atomix.protocols.raft.protocol.ConfigureResponse; +import io.atomix.protocols.raft.protocol.InstallRequest; +import io.atomix.protocols.raft.protocol.InstallResponse; +import io.atomix.protocols.raft.protocol.JoinRequest; +import io.atomix.protocols.raft.protocol.JoinResponse; +import io.atomix.protocols.raft.protocol.KeepAliveRequest; +import io.atomix.protocols.raft.protocol.KeepAliveResponse; +import io.atomix.protocols.raft.protocol.LeaveRequest; +import io.atomix.protocols.raft.protocol.LeaveResponse; +import io.atomix.protocols.raft.protocol.MetadataRequest; +import io.atomix.protocols.raft.protocol.MetadataResponse; +import io.atomix.protocols.raft.protocol.OpenSessionRequest; +import io.atomix.protocols.raft.protocol.OpenSessionResponse; +import io.atomix.protocols.raft.protocol.PollRequest; +import io.atomix.protocols.raft.protocol.PollResponse; +import io.atomix.protocols.raft.protocol.PublishRequest; +import io.atomix.protocols.raft.protocol.QueryRequest; +import io.atomix.protocols.raft.protocol.QueryResponse; +import io.atomix.protocols.raft.protocol.RaftResponse; +import io.atomix.protocols.raft.protocol.ReconfigureRequest; +import io.atomix.protocols.raft.protocol.ReconfigureResponse; +import io.atomix.protocols.raft.protocol.ResetRequest; +import io.atomix.protocols.raft.protocol.VoteRequest; +import io.atomix.protocols.raft.protocol.VoteResponse; +import io.atomix.protocols.raft.proxy.CommunicationStrategy; +import io.atomix.protocols.raft.proxy.RaftProxy; +import io.atomix.protocols.raft.service.RaftService; +import io.atomix.protocols.raft.session.SessionId; +import io.atomix.protocols.raft.storage.RaftStorage; +import io.atomix.protocols.raft.storage.log.entry.CloseSessionEntry; +import io.atomix.protocols.raft.storage.log.entry.CommandEntry; +import io.atomix.protocols.raft.storage.log.entry.ConfigurationEntry; +import io.atomix.protocols.raft.storage.log.entry.InitializeEntry; +import io.atomix.protocols.raft.storage.log.entry.KeepAliveEntry; +import io.atomix.protocols.raft.storage.log.entry.MetadataEntry; +import io.atomix.protocols.raft.storage.log.entry.OpenSessionEntry; +import io.atomix.protocols.raft.storage.log.entry.QueryEntry; +import io.atomix.protocols.raft.storage.system.Configuration; +import io.atomix.storage.StorageLevel; +import org.junit.After; +import org.junit.Before; +import org.onlab.util.KryoNamespace; +import org.onosproject.cluster.NodeId; +import org.onosproject.cluster.PartitionId; +import org.onosproject.store.primitives.impl.RaftClientCommunicator; +import org.onosproject.store.primitives.impl.RaftServerCommunicator; +import org.onosproject.store.service.Serializer; /** * Base class for various Atomix tests. + * + * @param the Raft primitive type being tested */ -public abstract class AtomixTestBase { - protected static LocalServerRegistry registry = new LocalServerRegistry(); - protected static List

members = new ArrayList<>(); - protected static List copycatClients = new ArrayList<>(); - protected static List copycatServers = new ArrayList<>(); - protected static List atomixClients = new ArrayList<>(); - protected static List atomixServers = new ArrayList<>(); - protected static Serializer serializer = CatalystSerializers.getSerializer(); - protected static AtomicInteger port = new AtomicInteger(49200); +public abstract class AtomixTestBase { + + private static final Serializer PROTOCOL_SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(OpenSessionRequest.class) + .register(OpenSessionResponse.class) + .register(CloseSessionRequest.class) + .register(CloseSessionResponse.class) + .register(KeepAliveRequest.class) + .register(KeepAliveResponse.class) + .register(QueryRequest.class) + .register(QueryResponse.class) + .register(CommandRequest.class) + .register(CommandResponse.class) + .register(MetadataRequest.class) + .register(MetadataResponse.class) + .register(JoinRequest.class) + .register(JoinResponse.class) + .register(LeaveRequest.class) + .register(LeaveResponse.class) + .register(ConfigureRequest.class) + .register(ConfigureResponse.class) + .register(ReconfigureRequest.class) + .register(ReconfigureResponse.class) + .register(InstallRequest.class) + .register(InstallResponse.class) + .register(PollRequest.class) + .register(PollResponse.class) + .register(VoteRequest.class) + .register(VoteResponse.class) + .register(AppendRequest.class) + .register(AppendResponse.class) + .register(PublishRequest.class) + .register(ResetRequest.class) + .register(RaftResponse.Status.class) + .register(RaftError.class) + .register(RaftError.Type.class) + .register(ReadConsistency.class) + .register(byte[].class) + .register(long[].class) + .register(CloseSessionEntry.class) + .register(CommandEntry.class) + .register(ConfigurationEntry.class) + .register(InitializeEntry.class) + .register(KeepAliveEntry.class) + .register(MetadataEntry.class) + .register(OpenSessionEntry.class) + .register(QueryEntry.class) + .register(RaftOperation.class) + .register(RaftEvent.class) + .register(DefaultEventType.class) + .register(DefaultOperationId.class) + .register(OperationType.class) + .register(ReadConsistency.class) + .register(ArrayList.class) + .register(LinkedList.class) + .register(Collections.emptyList().getClass()) + .register(HashSet.class) + .register(DefaultRaftMember.class) + .register(MemberId.class) + .register(SessionId.class) + .register(RaftMember.Type.class) + .register(RaftMember.Status.class) + .register(Instant.class) + .register(Configuration.class) + .register(AtomixAtomicCounterMapOperations.class) + .register(AtomixConsistentMapEvents.class) + .register(AtomixConsistentMapOperations.class) + .register(AtomixConsistentSetMultimapOperations.class) + .register(AtomixConsistentSetMultimapEvents.class) + .register(AtomixConsistentTreeMapEvents.class) + .register(AtomixConsistentTreeMapOperations.class) + .register(AtomixCounterOperations.class) + .register(AtomixDocumentTreeEvents.class) + .register(AtomixDocumentTreeOperations.class) + .register(AtomixLeaderElectorEvents.class) + .register(AtomixLeaderElectorOperations.class) + .register(AtomixWorkQueueEvents.class) + .register(AtomixWorkQueueOperations.class) + .build()); + + private static final Serializer STORAGE_SERIALIZER = Serializer.using(KryoNamespace.newBuilder() + .register(CloseSessionEntry.class) + .register(CommandEntry.class) + .register(ConfigurationEntry.class) + .register(InitializeEntry.class) + .register(KeepAliveEntry.class) + .register(MetadataEntry.class) + .register(OpenSessionEntry.class) + .register(QueryEntry.class) + .register(RaftOperation.class) + .register(ReadConsistency.class) + .register(AtomixAtomicCounterMapOperations.class) + .register(AtomixConsistentMapOperations.class) + .register(AtomixConsistentSetMultimapOperations.class) + .register(AtomixConsistentTreeMapOperations.class) + .register(AtomixCounterOperations.class) + .register(AtomixDocumentTreeOperations.class) + .register(AtomixLeaderElectorOperations.class) + .register(AtomixWorkQueueOperations.class) + .register(ArrayList.class) + .register(HashSet.class) + .register(DefaultRaftMember.class) + .register(MemberId.class) + .register(RaftMember.Type.class) + .register(RaftMember.Status.class) + .register(Instant.class) + .register(Configuration.class) + .register(byte[].class) + .register(long[].class) + .build()); + + protected TestClusterCommunicationServiceFactory communicationServiceFactory; + protected List members = Lists.newCopyOnWriteArrayList(); + protected List clients = Lists.newCopyOnWriteArrayList(); + protected List servers = Lists.newCopyOnWriteArrayList(); + protected int nextId; /** - * Creates a new resource state machine. + * Creates the primitive service. * - * @return A new resource state machine. + * @return the primitive service */ - protected abstract ResourceType resourceType(); + protected abstract RaftService createService(); + + /** + * Creates a new primitive. + * + * @param name the primitive name + * @return the primitive instance + */ + protected T newPrimitive(String name) { + RaftClient client = createClient(); + RaftProxy proxy = client.newProxyBuilder() + .withName(name) + .withServiceType("test") + .withReadConsistency(readConsistency()) + .withCommunicationStrategy(communicationStrategy()) + .build() + .open() + .join(); + return createPrimitive(proxy); + } + + /** + * Creates a new primitive instance. + * + * @param proxy the primitive proxy + * @return the primitive instance + */ + protected abstract T createPrimitive(RaftProxy proxy); + + /** + * Returns the proxy read consistency. + * + * @return the primitive read consistency + */ + protected ReadConsistency readConsistency() { + return ReadConsistency.LINEARIZABLE; + } + + /** + * Returns the proxy communication strategy. + * + * @return the primitive communication strategy + */ + protected CommunicationStrategy communicationStrategy() { + return CommunicationStrategy.LEADER; + } + + @Before + public void prepare() { + members.clear(); + clients.clear(); + servers.clear(); + communicationServiceFactory = new TestClusterCommunicationServiceFactory(); + createServers(3); + } + + @After + public void cleanup() { + shutdown(); + } + + /** + * Shuts down clients and servers. + */ + private void shutdown() { + clients.forEach(c -> { + try { + c.close().get(10, TimeUnit.SECONDS); + } catch (Exception e) { + } + }); + + servers.forEach(s -> { + try { + if (s.isRunning()) { + s.shutdown().get(10, TimeUnit.SECONDS); + } + } catch (Exception e) { + } + }); + + Path directory = Paths.get("target/primitives/"); + if (Files.exists(directory)) { + try { + Files.walkFileTree(directory, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + } + } + } + + /** + * Returns the next unique member identifier. + * + * @return The next unique member identifier. + */ + private MemberId nextMemberId() { + return MemberId.from(String.valueOf(++nextId)); + } /** * Returns the next server address. * + * @param type The startup member type. * @return The next server address. */ - private static Address nextAddress() { - Address address = new Address("127.0.0.1", - TestTools.findAvailablePort(port.getAndIncrement())); - members.add(address); - return address; + private RaftMember nextMember(RaftMember.Type type) { + return new TestMember(nextMemberId(), type); } /** - * Creates a set of Copycat servers. + * Creates a set of Raft servers. */ - protected static List createCopycatServers(int nodes) - throws Throwable { - List servers = new ArrayList<>(); - - List
members = new ArrayList<>(); + protected List createServers(int nodes) { + List servers = new ArrayList<>(); for (int i = 0; i < nodes; i++) { - Address address = nextAddress(); - members.add(address); - CopycatServer server = createCopycatServer(address); - if (members.size() <= 1) { - server.bootstrap().join(); - } else { - server.join(members).join(); - } + members.add(nextMember(RaftMember.Type.ACTIVE)); + } + + CountDownLatch latch = new CountDownLatch(nodes); + for (int i = 0; i < nodes; i++) { + RaftServer server = createServer(members.get(i)); + server.bootstrap(members.stream().map(RaftMember::memberId).collect(Collectors.toList())) + .thenRun(latch::countDown); servers.add(server); } + try { + latch.await(30000, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return servers; } /** - * Creates a Copycat server. + * Creates a Raft server. */ - protected static CopycatServer createCopycatServer(Address address) { - CopycatServer server = CopycatServer.builder(address) - .withTransport(NettyTransport.builder().withThreads(1).build()) - .withStorage(Storage.builder() - .withStorageLevel(StorageLevel.MEMORY) - .build()) - .withStateMachine(ResourceManagerState::new) - .withSerializer(serializer.clone()) - .build(); - copycatServers.add(server); + private RaftServer createServer(RaftMember member) { + RaftServer.Builder builder = RaftServer.newBuilder(member.memberId()) + .withType(member.getType()) + .withProtocol(new RaftServerCommunicator( + PartitionId.from(1), + PROTOCOL_SERIALIZER, + communicationServiceFactory.newCommunicationService(NodeId.nodeId(member.memberId().id())))) + .withStorage(RaftStorage.newBuilder() + .withStorageLevel(StorageLevel.MEMORY) + .withDirectory(new File(String.format("target/primitives/%s", member.memberId()))) + .withSerializer(new AtomixSerializerAdapter(STORAGE_SERIALIZER)) + .withMaxSegmentSize(1024 * 1024) + .build()) + .addService("test", this::createService); + + RaftServer server = builder.build(); + servers.add(server); return server; } - public static void clearTests() throws Exception { - registry = new LocalServerRegistry(); - members = new ArrayList<>(); - - CompletableFuture closeClients = - CompletableFuture.allOf(atomixClients.stream() - .map(AtomixClient::close) - .toArray(CompletableFuture[]::new)); - closeClients.join(); - - CompletableFuture closeServers = - CompletableFuture.allOf(copycatServers.stream() - .map(CopycatServer::shutdown) - .toArray(CompletableFuture[]::new)); - closeServers.join(); - - atomixClients.clear(); - copycatServers.clear(); - } - - /** - * Creates a Atomix client. + * Creates a Raft client. */ - protected AtomixClient createAtomixClient() { - CountDownLatch latch = new CountDownLatch(1); - AtomixClient client = AtomixClient.builder() - .withTransport(NettyTransport.builder().withThreads(1).build()) - .withSerializer(serializer.clone()) + private RaftClient createClient() { + MemberId memberId = nextMemberId(); + RaftClient client = RaftClient.newBuilder() + .withMemberId(memberId) + .withProtocol(new RaftClientCommunicator( + PartitionId.from(1), + PROTOCOL_SERIALIZER, + communicationServiceFactory.newCommunicationService(NodeId.nodeId(memberId.id())))) .build(); - client.connect(members).thenRun(latch::countDown); - atomixClients.add(client); - Uninterruptibles.awaitUninterruptibly(latch); + + client.connect(members.stream().map(RaftMember::memberId).collect(Collectors.toList())).join(); + clients.add(client); return client; } + + /** + * Test member. + */ + public static class TestMember implements RaftMember { + private final MemberId memberId; + private final Type type; + + public TestMember(MemberId memberId, Type type) { + this.memberId = memberId; + this.type = type; + } + + @Override + public MemberId memberId() { + return memberId; + } + + @Override + public int hash() { + return memberId.hashCode(); + } + + @Override + public Type getType() { + return type; + } + + @Override + public void addTypeChangeListener(Consumer listener) { + + } + + @Override + public void removeTypeChangeListener(Consumer listener) { + + } + + @Override + public Status getStatus() { + return Status.AVAILABLE; + } + + @Override + public Instant getLastUpdated() { + return Instant.now(); + } + + @Override + public void addStatusChangeListener(Consumer listener) { + + } + + @Override + public void removeStatusChangeListener(Consumer listener) { + + } + + @Override + public CompletableFuture promote() { + return null; + } + + @Override + public CompletableFuture promote(Type type) { + return null; + } + + @Override + public CompletableFuture demote() { + return null; + } + + @Override + public CompletableFuture demote(Type type) { + return null; + } + + @Override + public CompletableFuture remove() { + return null; + } + } } diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueServiceTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueServiceTest.java new file mode 100644 index 0000000000..44ebd52ef1 --- /dev/null +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueServiceTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import java.util.Arrays; +import java.util.Collection; + +import io.atomix.protocols.raft.ReadConsistency; +import io.atomix.protocols.raft.cluster.MemberId; +import io.atomix.protocols.raft.impl.RaftServerContext; +import io.atomix.protocols.raft.protocol.RaftServerProtocol; +import io.atomix.protocols.raft.service.ServiceId; +import io.atomix.protocols.raft.service.ServiceType; +import io.atomix.protocols.raft.service.impl.DefaultCommit; +import io.atomix.protocols.raft.service.impl.DefaultServiceContext; +import io.atomix.protocols.raft.session.SessionId; +import io.atomix.protocols.raft.session.impl.RaftSessionContext; +import io.atomix.protocols.raft.storage.RaftStorage; +import io.atomix.protocols.raft.storage.snapshot.Snapshot; +import io.atomix.protocols.raft.storage.snapshot.SnapshotReader; +import io.atomix.protocols.raft.storage.snapshot.SnapshotStore; +import io.atomix.protocols.raft.storage.snapshot.SnapshotWriter; +import io.atomix.storage.StorageLevel; +import io.atomix.time.WallClockTimestamp; +import io.atomix.utils.concurrent.ThreadContext; +import org.junit.Test; +import org.onosproject.store.service.Task; + +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.mock; +import static org.easymock.EasyMock.replay; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.ADD; +import static org.onosproject.store.primitives.resources.impl.AtomixWorkQueueOperations.TAKE; +import static org.onosproject.store.service.DistributedPrimitive.Type.WORK_QUEUE; + +/** + * Work queue service test. + */ +public class AtomixWorkQueueServiceTest { + @Test + public void testSnapshot() throws Exception { + SnapshotStore store = new SnapshotStore(RaftStorage.newBuilder() + .withPrefix("test") + .withStorageLevel(StorageLevel.MEMORY) + .build()); + Snapshot snapshot = store.newSnapshot(ServiceId.from(1), 2, new WallClockTimestamp()); + + DefaultServiceContext context = mock(DefaultServiceContext.class); + expect(context.serviceType()).andReturn(ServiceType.from(WORK_QUEUE.name())).anyTimes(); + expect(context.serviceName()).andReturn("test").anyTimes(); + expect(context.serviceId()).andReturn(ServiceId.from(1)).anyTimes(); + expect(context.executor()).andReturn(mock(ThreadContext.class)).anyTimes(); + + RaftServerContext server = mock(RaftServerContext.class); + expect(server.getProtocol()).andReturn(mock(RaftServerProtocol.class)); + + replay(context, server); + + RaftSessionContext session = new RaftSessionContext( + SessionId.from(1), + MemberId.from("1"), + "test", + ServiceType.from(WORK_QUEUE.name()), + ReadConsistency.LINEARIZABLE, + 5000, + context, + server); + + AtomixWorkQueueService service = new AtomixWorkQueueService(); + service.init(context); + + service.add(new DefaultCommit<>( + 2, + ADD, + new AtomixWorkQueueOperations.Add(Arrays.asList("Hello world!".getBytes())), + session, + System.currentTimeMillis())); + + try (SnapshotWriter writer = snapshot.openWriter()) { + service.snapshot(writer); + } + + snapshot.complete(); + + service = new AtomixWorkQueueService(); + service.init(context); + + try (SnapshotReader reader = snapshot.openReader()) { + service.install(reader); + } + + Collection> value = service.take(new DefaultCommit<>( + 2, + TAKE, + new AtomixWorkQueueOperations.Take(1), + session, + System.currentTimeMillis())); + assertNotNull(value); + assertEquals(1, value.size()); + assertArrayEquals("Hello world!".getBytes(), value.iterator().next().payload()); + } +} diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueTest.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueTest.java index 3675373c7f..5357b04d44 100644 --- a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueTest.java +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/AtomixWorkQueueTest.java @@ -15,13 +15,6 @@ */ package org.onosproject.store.primitives.resources.impl; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import io.atomix.Atomix; -import io.atomix.AtomixClient; -import io.atomix.resource.ResourceType; - import java.time.Duration; import java.util.Arrays; import java.util.UUID; @@ -30,48 +23,43 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import com.google.common.util.concurrent.Uninterruptibles; +import io.atomix.protocols.raft.proxy.RaftProxy; +import io.atomix.protocols.raft.service.RaftService; import org.junit.Test; import org.onlab.util.Tools; import org.onosproject.store.service.Task; import org.onosproject.store.service.WorkQueueStats; -import com.google.common.util.concurrent.Uninterruptibles; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; /** * Unit tests for {@link AtomixWorkQueue}. */ -public class AtomixWorkQueueTest extends AtomixTestBase { - +public class AtomixWorkQueueTest extends AtomixTestBase { private static final Duration DEFAULT_PROCESSING_TIME = Duration.ofMillis(100); private static final byte[] DEFAULT_PAYLOAD = "hello world".getBytes(); - @BeforeClass - public static void preTestSetup() throws Throwable { - createCopycatServers(1); - } - - @AfterClass - public static void postTestCleanup() throws Exception { - clearTests(); + @Override + protected RaftService createService() { + return new AtomixWorkQueueService(); } @Override - protected ResourceType resourceType() { - return new ResourceType(AtomixWorkQueue.class); + protected AtomixWorkQueue createPrimitive(RaftProxy proxy) { + return new AtomixWorkQueue(proxy); } @Test public void testAdd() throws Throwable { String queueName = UUID.randomUUID().toString(); - Atomix atomix1 = createAtomixClient(); - AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue1 = newPrimitive(queueName); byte[] item = DEFAULT_PAYLOAD; queue1.addOne(item).join(); - Atomix atomix2 = createAtomixClient(); - AtomixWorkQueue queue2 = atomix2.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue2 = newPrimitive(queueName); byte[] task2 = DEFAULT_PAYLOAD; queue2.addOne(task2).join(); @@ -84,8 +72,7 @@ public class AtomixWorkQueueTest extends AtomixTestBase { @Test public void testAddMultiple() throws Throwable { String queueName = UUID.randomUUID().toString(); - Atomix atomix1 = createAtomixClient(); - AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue1 = newPrimitive(queueName); byte[] item1 = DEFAULT_PAYLOAD; byte[] item2 = DEFAULT_PAYLOAD; queue1.addMultiple(Arrays.asList(item1, item2)).join(); @@ -99,13 +86,11 @@ public class AtomixWorkQueueTest extends AtomixTestBase { @Test public void testTakeAndComplete() throws Throwable { String queueName = UUID.randomUUID().toString(); - Atomix atomix1 = createAtomixClient(); - AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue1 = newPrimitive(queueName); byte[] item1 = DEFAULT_PAYLOAD; queue1.addOne(item1).join(); - Atomix atomix2 = createAtomixClient(); - AtomixWorkQueue queue2 = atomix2.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue2 = newPrimitive(queueName); Task removedTask = queue2.take().join(); WorkQueueStats stats = queue2.stats().join(); @@ -128,13 +113,11 @@ public class AtomixWorkQueueTest extends AtomixTestBase { @Test public void testUnexpectedClientClose() throws Throwable { String queueName = UUID.randomUUID().toString(); - Atomix atomix1 = createAtomixClient(); - AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue1 = newPrimitive(queueName); byte[] item1 = DEFAULT_PAYLOAD; queue1.addOne(item1).join(); - AtomixClient atomix2 = createAtomixClient(); - AtomixWorkQueue queue2 = atomix2.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue2 = newPrimitive(queueName); queue2.take().join(); WorkQueueStats stats = queue1.stats().join(); @@ -142,7 +125,7 @@ public class AtomixWorkQueueTest extends AtomixTestBase { assertEquals(1, stats.totalInProgress()); assertEquals(0, stats.totalCompleted()); - atomix2.close().join(); + queue2.proxy.close().join(); stats = queue1.stats().join(); assertEquals(1, stats.totalPending()); @@ -153,15 +136,13 @@ public class AtomixWorkQueueTest extends AtomixTestBase { @Test public void testAutomaticTaskProcessing() throws Throwable { String queueName = UUID.randomUUID().toString(); - Atomix atomix1 = createAtomixClient(); - AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue1 = newPrimitive(queueName); Executor executor = Executors.newSingleThreadExecutor(); CountDownLatch latch1 = new CountDownLatch(1); queue1.registerTaskProcessor(s -> latch1.countDown(), 2, executor); - AtomixClient atomix2 = createAtomixClient(); - AtomixWorkQueue queue2 = atomix2.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue2 = newPrimitive(queueName); byte[] item1 = DEFAULT_PAYLOAD; queue2.addOne(item1).join(); @@ -189,13 +170,11 @@ public class AtomixWorkQueueTest extends AtomixTestBase { @Test public void testDestroy() { String queueName = UUID.randomUUID().toString(); - Atomix atomix1 = createAtomixClient(); - AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue1 = newPrimitive(queueName); byte[] item = DEFAULT_PAYLOAD; queue1.addOne(item).join(); - Atomix atomix2 = createAtomixClient(); - AtomixWorkQueue queue2 = atomix2.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue2 = newPrimitive(queueName); byte[] task2 = DEFAULT_PAYLOAD; queue2.addOne(task2).join(); @@ -215,8 +194,7 @@ public class AtomixWorkQueueTest extends AtomixTestBase { @Test public void testCompleteAttemptWithIncorrectSession() { String queueName = UUID.randomUUID().toString(); - Atomix atomix1 = createAtomixClient(); - AtomixWorkQueue queue1 = atomix1.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue1 = newPrimitive(queueName); byte[] item = DEFAULT_PAYLOAD; queue1.addOne(item).join(); @@ -224,8 +202,7 @@ public class AtomixWorkQueueTest extends AtomixTestBase { String taskId = task.taskId(); // Create another client and get a handle to the same queue. - Atomix atomix2 = createAtomixClient(); - AtomixWorkQueue queue2 = atomix2.getResource(queueName, AtomixWorkQueue.class).join(); + AtomixWorkQueue queue2 = newPrimitive(queueName); // Attempt completing the task with new client and verify task is not completed queue2.complete(taskId).join(); diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationService.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationService.java new file mode 100644 index 0000000000..bb4b78cb4e --- /dev/null +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationService.java @@ -0,0 +1,174 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.function.Consumer; +import java.util.function.Function; + +import com.google.common.collect.Maps; +import io.atomix.utils.concurrent.Futures; +import org.onosproject.cluster.NodeId; +import org.onosproject.store.cluster.messaging.ClusterCommunicationService; +import org.onosproject.store.cluster.messaging.ClusterMessageHandler; +import org.onosproject.store.cluster.messaging.MessageSubject; +import org.onosproject.store.cluster.messaging.MessagingException; + +/** + * Cluster communication service implementation used for testing. + */ +public class TestClusterCommunicationService implements ClusterCommunicationService { + private final NodeId localNodeId; + private final Map nodes; + private final Map>> subscribers = + Maps.newConcurrentMap(); + + public TestClusterCommunicationService(NodeId localNodeId, Map nodes) { + this.localNodeId = localNodeId; + this.nodes = nodes; + nodes.put(localNodeId, this); + } + + @Override + public void broadcast(M message, MessageSubject subject, Function encoder) { + nodes.forEach((nodeId, node) -> { + if (!nodeId.equals(localNodeId)) { + node.handle(subject, encoder.apply(message)); + } + }); + } + + @Override + public void broadcastIncludeSelf(M message, MessageSubject subject, Function encoder) { + nodes.values().forEach(node -> node.handle(subject, encoder.apply(message))); + } + + @Override + public CompletableFuture unicast( + M message, MessageSubject subject, Function encoder, NodeId toNodeId) { + TestClusterCommunicationService node = nodes.get(toNodeId); + if (node != null) { + node.handle(subject, encoder.apply(message)); + } + return CompletableFuture.completedFuture(null); + } + + @Override + public void multicast(M message, MessageSubject subject, Function encoder, Set nodeIds) { + nodes.values().stream() + .filter(n -> nodeIds.contains(n)) + .forEach(n -> n.handle(subject, encoder.apply(message))); + } + + @Override + public CompletableFuture sendAndReceive( + M message, + MessageSubject subject, + Function encoder, + Function decoder, + NodeId toNodeId) { + TestClusterCommunicationService node = nodes.get(toNodeId); + if (node == null) { + return Futures.exceptionalFuture(new MessagingException.NoRemoteHandler()); + } + return node.handle(subject, encoder.apply(message)).thenApply(decoder); + } + + private CompletableFuture handle(MessageSubject subject, byte[] message) { + Function> subscriber = subscribers.get(subject); + if (subscriber != null) { + return subscriber.apply(message); + } + return Futures.exceptionalFuture(new MessagingException.NoRemoteHandler()); + } + + private boolean isSubscriber(MessageSubject subject) { + return subscribers.containsKey(subject); + } + + @Override + public void addSubscriber( + MessageSubject subject, + Function decoder, + Function handler, + Function encoder, + Executor executor) { + subscribers.put(subject, message -> { + CompletableFuture future = new CompletableFuture<>(); + executor.execute(() -> { + try { + future.complete(encoder.apply(handler.apply(decoder.apply(message)))); + } catch (Exception e) { + future.completeExceptionally(new MessagingException.RemoteHandlerFailure()); + } + }); + return future; + }); + } + + @Override + public void addSubscriber( + MessageSubject subject, + Function decoder, + + Function> handler, Function encoder) { + subscribers.put(subject, message -> { + CompletableFuture future = new CompletableFuture<>(); + try { + handler.apply(decoder.apply(message)).whenComplete((result, error) -> { + if (error == null) { + future.complete(encoder.apply(result)); + } else { + future.completeExceptionally(new MessagingException.RemoteHandlerFailure()); + } + }); + } catch (Exception e) { + future.completeExceptionally(new MessagingException.RemoteHandlerFailure()); + } + return future; + }); + } + + @Override + public void addSubscriber( + MessageSubject subject, + Function decoder, + Consumer handler, + Executor executor) { + subscribers.put(subject, message -> { + try { + handler.accept(decoder.apply(message)); + } catch (Exception e) { + return Futures.exceptionalFuture(new MessagingException.RemoteHandlerFailure()); + } + return Futures.completedFuture(null); + }); + } + + @Override + public void removeSubscriber(MessageSubject subject) { + subscribers.remove(subject); + } + + @Override + public void addSubscriber(MessageSubject subject, ClusterMessageHandler subscriber, ExecutorService executor) { + throw new UnsupportedOperationException(); + } +} diff --git a/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationServiceFactory.java b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationServiceFactory.java new file mode 100644 index 0000000000..819c9c36e4 --- /dev/null +++ b/core/store/primitives/src/test/java/org/onosproject/store/primitives/resources/impl/TestClusterCommunicationServiceFactory.java @@ -0,0 +1,39 @@ +/* + * Copyright 2017-present Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.primitives.resources.impl; + +import java.util.Map; + +import com.google.common.collect.Maps; +import org.onosproject.cluster.NodeId; +import org.onosproject.store.cluster.messaging.ClusterCommunicationService; + +/** + * Test cluster communication service factory. + */ +public class TestClusterCommunicationServiceFactory { + private final Map nodes = Maps.newConcurrentMap(); + + /** + * Creates a new cluster communication service for the given node. + * + * @param localNodeId the node for which to create the service + * @return the communication service for the given node + */ + public ClusterCommunicationService newCommunicationService(NodeId localNodeId) { + return new TestClusterCommunicationService(localNodeId, nodes); + } +} diff --git a/features/features.xml b/features/features.xml index c9937d2251..e4de0c595a 100644 --- a/features/features.xml +++ b/features/features.xml @@ -59,7 +59,7 @@ mvn:com.typesafe/config/1.2.1 mvn:com.googlecode.concurrent-trees/concurrent-trees/2.6.0 mvn:commons-io/commons-io/2.4 - mvn:io.atomix/atomix-all/1.0.8 + mvn:io.atomix/atomix/2.0.0-alpha1 mvn:org.glassfish.jersey.core/jersey-client/2.25.1 diff --git a/lib/BUCK b/lib/BUCK index 38c65e93d4..f2a85d4956 100644 --- a/lib/BUCK +++ b/lib/BUCK @@ -1,4 +1,4 @@ -# ***** This file was auto-generated at Mon, 10 Jul 2017 20:46:48 GMT. Do not edit this file manually. ***** +# ***** This file was auto-generated at Wed, 12 Jul 2017 18:19:42 GMT. Do not edit this file manually. ***** # ***** Use onos-lib-gen ***** pass_thru_pom( @@ -178,10 +178,10 @@ remote_jar ( remote_jar ( name = 'atomix', - out = 'atomix-all-1.0.8.jar', - url = 'mvn:io.atomix:atomix-all:jar:1.0.8', - sha1 = 'e8cbf8d93ceb151c89a62ed9eca8024bf6030c31', - maven_coords = 'io.atomix:atomix-all:1.0.8', + out = 'atomix-2.0.0-alpha1.jar', + url = 'mvn:io.atomix:atomix:jar:2.0.0-alpha1', + sha1 = '8693b1c112a393ed5c57de3fa385e22c1206b672', + maven_coords = 'io.atomix:atomix:2.0.0-alpha1', visibility = [ 'PUBLIC' ], ) @@ -878,33 +878,6 @@ remote_jar ( visibility = [ 'PUBLIC' ], ) -remote_jar ( - name = 'catalyst-concurrent', - out = 'catalyst-concurrent-1.2.1.jar', - url = 'mvn:io.atomix.catalyst:catalyst-concurrent:jar:1.2.1', - sha1 = '8f62c04cfef9ea69f40d75eaa4728e60add4b79a', - maven_coords = 'io.atomix.catalyst:catalyst-concurrent:1.2.1', - visibility = [ 'PUBLIC' ], -) - -remote_jar ( - name = 'catalyst-netty', - out = 'catalyst-netty-1.2.1.jar', - url = 'mvn:io.atomix.catalyst:catalyst-netty:jar:1.2.1', - sha1 = 'db6a1bf1704c009098bb78e17e8ded88c725fe79', - maven_coords = 'io.atomix.catalyst:catalyst-netty:1.2.1', - visibility = [ 'PUBLIC' ], -) - -remote_jar ( - name = 'catalyst-transport', - out = 'catalyst-transport-1.2.1.jar', - url = 'mvn:io.atomix.catalyst:catalyst-transport:jar:1.2.1', - sha1 = 'a50e7ee0f8b76b9d081ab7531ea86f5bdb63bc09', - maven_coords = 'io.atomix.catalyst:catalyst-transport:1.2.1', - visibility = [ 'PUBLIC' ], -) - remote_jar ( name = 'objenesis', out = 'objenesis-2.2.jar', diff --git a/lib/deps.json b/lib/deps.json index 8eae68aff5..dab231360b 100644 --- a/lib/deps.json +++ b/lib/deps.json @@ -112,7 +112,7 @@ "aopalliance-repackaged": "mvn:org.glassfish.hk2.external:aopalliance-repackaged:2.5.0-b32", "amqp-client": "mvn:com.rabbitmq:amqp-client:jar:3.6.1", "asm": "mvn:org.ow2.asm:asm:5.0.4", - "atomix": "mvn:io.atomix:atomix-all:1.0.8", + "atomix": "mvn:io.atomix:atomix:2.0.0-alpha1", "commons-codec": "mvn:commons-codec:commons-codec:1.10", "commons-collections": "mvn:commons-collections:commons-collections:3.2.2", "commons-configuration": "mvn:commons-configuration:commons-configuration:1.10", @@ -190,9 +190,6 @@ "netty-resolver": "mvn:io.netty:netty-resolver:4.1.8.Final", "netty-codec-http2": "mvn:io.netty:netty-codec-http2:4.1.8.Final", "netty-codec-http": "mvn:io.netty:netty-codec-http:4.1.8.Final", - "catalyst-concurrent": "mvn:io.atomix.catalyst:catalyst-concurrent:1.2.1", - "catalyst-netty": "mvn:io.atomix.catalyst:catalyst-netty:1.2.1", - "catalyst-transport": "mvn:io.atomix.catalyst:catalyst-transport:1.2.1", "objenesis": "mvn:org.objenesis:objenesis:2.2", "openflowj": "mvn:org.onosproject:openflowj:3.2.0.onos", "org.apache.felix.scr": "mvn:org.apache.felix:org.apache.felix.scr:1.8.2",