X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-distributed-datastore%2Fsrc%2Ftest%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fcluster%2Fdatastore%2FTransactionProxyTest.java;h=ea88dfaf7ab5507e5cdf22d277315d6cb0e9fec2;hb=5273c33b6f2051a7e3b1afcc4eeae4e457b6f26c;hp=6cf63157e16ff7f37033ee3142f4ca1c06e84df8;hpb=daaef05cbf70e6cbec9af181258faead6d9620a6;p=controller.git diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java index 6cf63157e1..ea88dfaf7a 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/datastore/TransactionProxyTest.java @@ -1,3 +1,11 @@ +/* + * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + package org.opendaylight.controller.cluster.datastore; import static org.junit.Assert.assertEquals; @@ -19,6 +27,7 @@ import akka.actor.ActorSelection; import akka.actor.ActorSystem; import akka.actor.Props; import akka.dispatch.Futures; +import akka.util.Timeout; import com.google.common.base.Optional; import com.google.common.collect.Sets; import com.google.common.util.concurrent.CheckedFuture; @@ -34,6 +43,7 @@ import org.junit.Assert; import org.junit.Test; import org.mockito.InOrder; import org.mockito.Mockito; +import org.opendaylight.controller.cluster.datastore.config.Configuration; import org.opendaylight.controller.cluster.datastore.exceptions.NoShardLeaderException; import org.opendaylight.controller.cluster.datastore.exceptions.NotInitializedException; import org.opendaylight.controller.cluster.datastore.exceptions.PrimaryNotFoundException; @@ -59,6 +69,8 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification; +import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import scala.concurrent.Promise; @@ -136,7 +148,7 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { } doReturn(Futures.failed(exToThrow)).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), any()); + any(ActorSelection.class), any(), any(Timeout.class)); TransactionProxy transactionProxy = new TransactionProxy(mockComponentFactory, READ_ONLY); @@ -214,7 +226,8 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { when(mockActorContext).findPrimaryShardAsync(eq(DefaultShardStrategy.DEFAULT_SHARD)); doReturn(Futures.successful(new Object())).when(mockActorContext).executeOperationAsync( - eq(getSystem().actorSelection(actorRef.path())), eqCreateTransaction(memberName, READ_ONLY)); + eq(getSystem().actorSelection(actorRef.path())), eqCreateTransaction(memberName, READ_ONLY), + any(Timeout.class)); TransactionProxy transactionProxy = new TransactionProxy(mockComponentFactory, READ_ONLY); @@ -332,7 +345,7 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { Promise createTxPromise = akka.dispatch.Futures.promise(); doReturn(createTxPromise).when(mockActorContext).executeOperationAsync( eq(getSystem().actorSelection(actorRef.path())), - eqCreateTransaction(memberName, READ_WRITE)); + eqCreateTransaction(memberName, READ_WRITE), any(Timeout.class)); doReturn(readSerializedDataReply(null)).when(mockActorContext).executeOperationAsync( eq(actorSelection(actorRef)), eqSerializedReadData()); @@ -613,6 +626,25 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { verifyCohortFutures((SingleCommitCohortProxy)ready, TestException.class); } + @Test + public void testReadyWithDebugContextEnabled() throws Exception { + dataStoreContextBuilder.transactionDebugContextEnabled(true); + + ActorRef actorRef = setupActorContextWithInitialCreateTransaction(getSystem(), READ_WRITE); + + expectBatchedModificationsReady(actorRef, true); + + TransactionProxy transactionProxy = new TransactionProxy(mockComponentFactory, READ_WRITE); + + transactionProxy.merge(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME)); + + DOMStoreThreePhaseCommitCohort ready = transactionProxy.ready(); + + assertTrue(ready instanceof DebugThreePhaseCommitCohort); + + verifyCohortFutures((DebugThreePhaseCommitCohort)ready, new CommitTransactionReply().toSerializable()); + } + private void testWriteOnlyTxWithFindPrimaryShardFailure(Exception toThrow) throws Exception { doReturn(Futures.failed(toThrow)).when(mockActorContext).findPrimaryShardAsync(anyString()); @@ -777,15 +809,30 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { throttleOperation(operation, 1, true); } + private void throttleOperation(TransactionProxyOperation operation, int outstandingOpsLimit, boolean shardFound){ + throttleOperation(operation, outstandingOpsLimit, shardFound, TimeUnit.MILLISECONDS.toNanos( + mockActorContext.getDatastoreContext().getOperationTimeoutInMillis())); + } + private PrimaryShardInfo newPrimaryShardInfo(ActorRef actorRef){ - return new PrimaryShardInfo(getSystem().actorSelection(actorRef.path()), Optional.absent()); + return new PrimaryShardInfo(getSystem().actorSelection(actorRef.path()), DataStoreVersions.CURRENT_VERSION, + Optional.absent()); } - private void throttleOperation(TransactionProxyOperation operation, int outstandingOpsLimit, boolean shardFound){ + private PrimaryShardInfo newPrimaryShardInfo(ActorRef actorRef, Optional dataTreeOptional){ + return new PrimaryShardInfo(getSystem().actorSelection(actorRef.path()), DataStoreVersions.CURRENT_VERSION, + dataTreeOptional); + } + + + private void throttleOperation(TransactionProxyOperation operation, int outstandingOpsLimit, boolean shardFound, long expectedCompletionTime){ ActorSystem actorSystem = getSystem(); ActorRef shardActorRef = actorSystem.actorOf(Props.create(DoNothingActor.class)); - doReturn(outstandingOpsLimit).when(mockActorContext).getTransactionOutstandingOperationLimit(); + // Note that we setting batchedModificationCount to one less than what we need because in TransactionProxy + // we now allow one extra permit to be allowed for ready + doReturn(dataStoreContextBuilder.operationTimeoutInSeconds(2). + shardBatchedModificationCount(outstandingOpsLimit-1).build()).when(mockActorContext).getDatastoreContext(); doReturn(actorSystem.actorSelection(shardActorRef.path())). when(mockActorContext).actorSelection(shardActorRef.path().toString()); @@ -793,19 +840,19 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { if(shardFound) { doReturn(Futures.successful(newPrimaryShardInfo(shardActorRef))). when(mockActorContext).findPrimaryShardAsync(eq(DefaultShardStrategy.DEFAULT_SHARD)); + doReturn(Futures.successful(newPrimaryShardInfo(shardActorRef))). + when(mockActorContext).findPrimaryShardAsync(eq("cars")); + } else { doReturn(Futures.failed(new Exception("not found"))) .when(mockActorContext).findPrimaryShardAsync(eq(DefaultShardStrategy.DEFAULT_SHARD)); } String actorPath = "akka.tcp://system@127.0.0.1:2550/user/tx-actor"; - CreateTransactionReply createTransactionReply = CreateTransactionReply.newBuilder(). - setTransactionId("txn-1").setTransactionActorPath(actorPath). - setMessageVersion(DataStoreVersions.CURRENT_VERSION).build(); - doReturn(Futures.successful(createTransactionReply)).when(mockActorContext). - executeOperationAsync(eq(actorSystem.actorSelection(shardActorRef.path())), - eqCreateTransaction(memberName, READ_WRITE)); + doReturn(incompleteFuture()).when(mockActorContext). + executeOperationAsync(eq(actorSystem.actorSelection(shardActorRef.path())), + eqCreateTransaction(memberName, READ_WRITE), any(Timeout.class)); doReturn(true).when(mockActorContext).isPathLocal(actorPath); @@ -817,9 +864,9 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { long end = System.nanoTime(); - long expected = TimeUnit.SECONDS.toNanos(mockActorContext.getDatastoreContext().getOperationTimeoutInSeconds()); Assert.assertTrue(String.format("Expected elapsed time: %s. Actual: %s", - expected, (end-start)), (end - start) > expected); + expectedCompletionTime, (end-start)), + ((end - start) > expectedCompletionTime) && ((end - start) < expectedCompletionTime*2)); } @@ -831,8 +878,6 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { ActorSystem actorSystem = getSystem(); ActorRef shardActorRef = actorSystem.actorOf(Props.create(DoNothingActor.class)); - doReturn(1).when(mockActorContext).getTransactionOutstandingOperationLimit(); - doReturn(actorSystem.actorSelection(shardActorRef.path())). when(mockActorContext).actorSelection(shardActorRef.path().toString()); @@ -854,7 +899,7 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { doReturn(Futures.successful(createTransactionReply)).when(mockActorContext). executeOperationAsync(eq(actorSystem.actorSelection(shardActorRef.path())), - eqCreateTransaction(memberName, READ_WRITE)); + eqCreateTransaction(memberName, READ_WRITE), any(Timeout.class)); doReturn(true).when(mockActorContext).isPathLocal(anyString()); @@ -866,14 +911,77 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { long end = System.nanoTime(); - long expected = TimeUnit.SECONDS.toNanos(mockActorContext.getDatastoreContext().getOperationTimeoutInSeconds()); + long expected = TimeUnit.MILLISECONDS.toNanos(mockActorContext.getDatastoreContext().getOperationTimeoutInMillis()); + Assert.assertTrue(String.format("Expected elapsed time: %s. Actual: %s", + expected, (end-start)), (end - start) <= expected); + } + + private void completeOperationLocal(TransactionProxyOperation operation, Optional dataTreeOptional){ + ActorSystem actorSystem = getSystem(); + ActorRef shardActorRef = actorSystem.actorOf(Props.create(DoNothingActor.class)); + + doReturn(actorSystem.actorSelection(shardActorRef.path())). + when(mockActorContext).actorSelection(shardActorRef.path().toString()); + + doReturn(Futures.successful(newPrimaryShardInfo(shardActorRef, dataTreeOptional))). + when(mockActorContext).findPrimaryShardAsync(eq(DefaultShardStrategy.DEFAULT_SHARD)); + + TransactionProxy transactionProxy = new TransactionProxy(mockComponentFactory, READ_WRITE); + + long start = System.nanoTime(); + + operation.run(transactionProxy); + + long end = System.nanoTime(); + + long expected = TimeUnit.MILLISECONDS.toNanos(mockActorContext.getDatastoreContext().getOperationTimeoutInMillis()); Assert.assertTrue(String.format("Expected elapsed time: %s. Actual: %s", expected, (end-start)), (end - start) <= expected); } + private Optional createDataTree(){ + DataTree dataTree = mock(DataTree.class); + Optional dataTreeOptional = Optional.of(dataTree); + DataTreeSnapshot dataTreeSnapshot = mock(DataTreeSnapshot.class); + DataTreeModification dataTreeModification = mock(DataTreeModification.class); + + doReturn(dataTreeSnapshot).when(dataTree).takeSnapshot(); + doReturn(dataTreeModification).when(dataTreeSnapshot).newModification(); + + return dataTreeOptional; + } + + private Optional createDataTree(NormalizedNode readResponse){ + DataTree dataTree = mock(DataTree.class); + Optional dataTreeOptional = Optional.of(dataTree); + DataTreeSnapshot dataTreeSnapshot = mock(DataTreeSnapshot.class); + DataTreeModification dataTreeModification = mock(DataTreeModification.class); + + doReturn(dataTreeSnapshot).when(dataTree).takeSnapshot(); + doReturn(dataTreeModification).when(dataTreeSnapshot).newModification(); + doReturn(Optional.of(readResponse)).when(dataTreeModification).readNode(any(YangInstanceIdentifier.class)); + + return dataTreeOptional; + } + + + @Test + public void testWriteCompletionForLocalShard(){ + completeOperationLocal(new TransactionProxyOperation() { + @Override + public void run(TransactionProxy transactionProxy) { + NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); + + transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); + + transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); + + } + }, createDataTree()); + } + @Test public void testWriteThrottlingWhenShardFound(){ - dataStoreContextBuilder.shardBatchedModificationCount(1); throttleOperation(new TransactionProxyOperation() { @Override public void run(TransactionProxy transactionProxy) { @@ -891,7 +999,6 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { @Test public void testWriteThrottlingWhenShardNotFound(){ // Confirm that there is no throttling when the Shard is not found - dataStoreContextBuilder.shardBatchedModificationCount(1); completeOperation(new TransactionProxyOperation() { @Override public void run(TransactionProxy transactionProxy) { @@ -910,7 +1017,6 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { @Test public void testWriteCompletion(){ - dataStoreContextBuilder.shardBatchedModificationCount(1); completeOperation(new TransactionProxyOperation() { @Override public void run(TransactionProxy transactionProxy) { @@ -927,7 +1033,6 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { @Test public void testMergeThrottlingWhenShardFound(){ - dataStoreContextBuilder.shardBatchedModificationCount(1); throttleOperation(new TransactionProxyOperation() { @Override public void run(TransactionProxy transactionProxy) { @@ -944,7 +1049,6 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { @Test public void testMergeThrottlingWhenShardNotFound(){ - dataStoreContextBuilder.shardBatchedModificationCount(1); completeOperation(new TransactionProxyOperation() { @Override public void run(TransactionProxy transactionProxy) { @@ -961,7 +1065,6 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { @Test public void testMergeCompletion(){ - dataStoreContextBuilder.shardBatchedModificationCount(1); completeOperation(new TransactionProxyOperation() { @Override public void run(TransactionProxy transactionProxy) { @@ -977,6 +1080,22 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { } + @Test + public void testMergeCompletionForLocalShard(){ + completeOperationLocal(new TransactionProxyOperation() { + @Override + public void run(TransactionProxy transactionProxy) { + NormalizedNode nodeToWrite = ImmutableNodes.containerNode(TestModel.TEST_QNAME); + + transactionProxy.merge(TestModel.TEST_PATH, nodeToWrite); + + transactionProxy.merge(TestModel.TEST_PATH, nodeToWrite); + + } + }, createDataTree()); + } + + @Test public void testDeleteThrottlingWhenShardFound(){ @@ -1008,9 +1127,22 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { }, false); } + @Test + public void testDeleteCompletionForLocalShard(){ + completeOperationLocal(new TransactionProxyOperation() { + @Override + public void run(TransactionProxy transactionProxy) { + + transactionProxy.delete(TestModel.TEST_PATH); + + transactionProxy.delete(TestModel.TEST_PATH); + } + }, createDataTree()); + + } + @Test public void testDeleteCompletion(){ - dataStoreContextBuilder.shardBatchedModificationCount(1); completeOperation(new TransactionProxyOperation() { @Override public void run(TransactionProxy transactionProxy) { @@ -1075,6 +1207,33 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { } + @Test + public void testReadCompletionForLocalShard(){ + final NormalizedNode nodeToRead = ImmutableNodes.containerNode(TestModel.TEST_QNAME); + completeOperationLocal(new TransactionProxyOperation() { + @Override + public void run(TransactionProxy transactionProxy) { + transactionProxy.read(TestModel.TEST_PATH); + + transactionProxy.read(TestModel.TEST_PATH); + } + }, createDataTree(nodeToRead)); + + } + + @Test + public void testReadCompletionForLocalShardWhenExceptionOccurs(){ + completeOperationLocal(new TransactionProxyOperation() { + @Override + public void run(TransactionProxy transactionProxy) { + transactionProxy.read(TestModel.TEST_PATH); + + transactionProxy.read(TestModel.TEST_PATH); + } + }, createDataTree()); + + } + @Test public void testExistsThrottlingWhenShardFound(){ @@ -1124,6 +1283,32 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { } + @Test + public void testExistsCompletionForLocalShard(){ + final NormalizedNode nodeToRead = ImmutableNodes.containerNode(TestModel.TEST_QNAME); + completeOperationLocal(new TransactionProxyOperation() { + @Override + public void run(TransactionProxy transactionProxy) { + transactionProxy.exists(TestModel.TEST_PATH); + + transactionProxy.exists(TestModel.TEST_PATH); + } + }, createDataTree(nodeToRead)); + + } + + @Test + public void testExistsCompletionForLocalShardWhenExceptionOccurs(){ + completeOperationLocal(new TransactionProxyOperation() { + @Override + public void run(TransactionProxy transactionProxy) { + transactionProxy.exists(TestModel.TEST_PATH); + + transactionProxy.exists(TestModel.TEST_PATH); + } + }, createDataTree()); + + } @Test public void testReadyThrottling(){ @@ -1134,9 +1319,6 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { expectBatchedModifications(1); - doReturn(incompleteFuture()).when(mockActorContext).executeOperationAsync( - any(ActorSelection.class), any(ReadyTransaction.class)); - transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); transactionProxy.ready(); @@ -1146,7 +1328,6 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { @Test public void testReadyThrottlingWithTwoTransactionContexts(){ - throttleOperation(new TransactionProxyOperation() { @Override public void run(TransactionProxy transactionProxy) { @@ -1160,11 +1341,13 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { transactionProxy.write(TestModel.TEST_PATH, nodeToWrite); - transactionProxy.write(TestModel.TEST_PATH, carsNode); + // Trying to write to Cars will cause another transaction context to get created + transactionProxy.write(CarsModel.BASE_PATH, carsNode); + // Now ready should block for both transaction contexts transactionProxy.ready(); } - }, 2, true); + }, 1, true, TimeUnit.MILLISECONDS.toNanos(mockActorContext.getDatastoreContext().getOperationTimeoutInMillis()) * 2); } private void testModificationOperationBatching(TransactionType type) throws Exception { @@ -1346,8 +1529,6 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { doReturn(memberName).when(mockActorContext).getCurrentMemberName(); - doReturn(10).when(mockActorContext).getTransactionOutstandingOperationLimit(); - doReturn(getSystem().dispatchers().defaultGlobalDispatcher()).when(mockActorContext).getClientDispatcher(); TransactionProxy transactionProxy = new TransactionProxy(mockComponentFactory, READ_ONLY); @@ -1398,7 +1579,7 @@ public class TransactionProxyTest extends AbstractTransactionProxyTest { doReturn(Futures.successful(createTransactionReply(txActorRef, DataStoreVersions.CURRENT_VERSION))).when(mockActorContext). executeOperationAsync(eq(actorSystem.actorSelection(shardActorRef.path())), - eqCreateTransaction(memberName, TransactionType.READ_ONLY)); + eqCreateTransaction(memberName, TransactionType.READ_ONLY), any(Timeout.class)); doReturn(readSerializedDataReply(expectedNode)).when(mockActorContext).executeOperationAsync( eq(actorSelection(txActorRef)), eqSerializedReadData(YangInstanceIdentifier.builder().build()));