Delay snapshot backed transaction ready error 14/67914/3
authorJaime Caamaño Ruiz <jcaamano@suse.com>
Mon, 5 Feb 2018 11:07:42 +0000 (12:07 +0100)
committerJaime Caamaño Ruiz <jcaamano@suse.com>
Thu, 8 Feb 2018 12:52:27 +0000 (13:52 +0100)
Delay snapshot backed transaction ready error to 3PC canCommit.

Change-Id: Ief659423b401936a286f04c2f6c3732722c5aabf
JIRA: CONTROLLER-1812
Signed-off-by: Jaime Caamaño Ruiz <jcaamano@suse.com>
12 files changed:
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/LocalThreePhaseCommitCohort.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/LocalTransactionChain.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/LocalTransactionFactoryImpl.java
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/AbstractSnapshotBackedTransactionChain.java
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/SnapshotBackedWriteTransaction.java
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ChainedTransactionCommitImpl.java
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/DOMStoreTransactionChainImpl.java
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMDataStore.java
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDOMStoreThreePhaseCommitCohort.java
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/InMemoryDataStoreTest.java
opendaylight/md-sal/sal-inmemory-datastore/src/test/java/org/opendaylight/controller/md/sal/dom/store/impl/TestModel.java
opendaylight/md-sal/sal-inmemory-datastore/src/test/resources/odl-datastore-test.yang

index 610212fdf3c8fb3dc8f90f41c490474af5fd68e0..80d5417e0bf392d6150f64d73c9a519e3d58efaf 100644 (file)
@@ -41,12 +41,13 @@ class LocalThreePhaseCommitCohort implements DOMStoreThreePhaseCommitCohort {
 
     protected LocalThreePhaseCommitCohort(final ActorContext actorContext, final ActorSelection leader,
             final SnapshotBackedWriteTransaction<TransactionIdentifier> transaction,
-            final DataTreeModification modification) {
+            final DataTreeModification modification,
+            final Exception operationError) {
         this.actorContext = Preconditions.checkNotNull(actorContext);
         this.leader = Preconditions.checkNotNull(leader);
         this.transaction = Preconditions.checkNotNull(transaction);
         this.modification = Preconditions.checkNotNull(modification);
-        this.operationError = null;
+        this.operationError = operationError;
     }
 
     protected LocalThreePhaseCommitCohort(final ActorContext actorContext, final ActorSelection leader,
index de98dce5620ba84434cf3d7e679a29fde9d05bca..10ea3c63fca12d670e0a14f23d4cb1347547f061 100644 (file)
@@ -61,8 +61,9 @@ final class LocalTransactionChain extends AbstractSnapshotBackedTransactionChain
     @Override
     protected DOMStoreThreePhaseCommitCohort createCohort(
             final SnapshotBackedWriteTransaction<TransactionIdentifier> transaction,
-            final DataTreeModification modification) {
-        return new LocalChainThreePhaseCommitCohort(transaction, modification);
+            final DataTreeModification modification,
+            final Exception operationError) {
+        return new LocalChainThreePhaseCommitCohort(transaction, modification, operationError);
     }
 
     @Override
@@ -102,8 +103,8 @@ final class LocalTransactionChain extends AbstractSnapshotBackedTransactionChain
     private class LocalChainThreePhaseCommitCohort extends LocalThreePhaseCommitCohort {
 
         protected LocalChainThreePhaseCommitCohort(SnapshotBackedWriteTransaction<TransactionIdentifier> transaction,
-                DataTreeModification modification) {
-            super(parent.getActorContext(), leader, transaction, modification);
+                DataTreeModification modification, Exception operationError) {
+            super(parent.getActorContext(), leader, transaction, modification, operationError);
         }
 
         protected LocalChainThreePhaseCommitCohort(SnapshotBackedWriteTransaction<TransactionIdentifier> transaction,
index d01cae8451451a0b5f35d5dc01f4fd10e789b932..a4b2e4e62cb4d980b2d6e4a3352155c4627ede92 100644 (file)
@@ -67,8 +67,10 @@ final class LocalTransactionFactoryImpl extends TransactionReadyPrototype<Transa
 
     @Override
     protected DOMStoreThreePhaseCommitCohort transactionReady(
-            final SnapshotBackedWriteTransaction<TransactionIdentifier> tx, final DataTreeModification tree) {
-        return new LocalThreePhaseCommitCohort(actorContext, leader, tx, tree);
+            final SnapshotBackedWriteTransaction<TransactionIdentifier> tx,
+            final DataTreeModification tree,
+            final Exception readyError) {
+        return new LocalThreePhaseCommitCohort(actorContext, leader, tx, tree, readyError);
     }
 
     @SuppressWarnings({"unchecked", "checkstyle:IllegalCatch"})
@@ -81,13 +83,6 @@ final class LocalTransactionFactoryImpl extends TransactionReadyPrototype<Transa
                     (SnapshotBackedWriteTransaction<TransactionIdentifier>)tx, operationError);
         }
 
-        try {
-            return (LocalThreePhaseCommitCohort) tx.ready();
-        } catch (Exception e) {
-            // Unfortunately we need to cast to SnapshotBackedWriteTransaction here as it's required by
-            // LocalThreePhaseCommitCohort.
-            return new LocalThreePhaseCommitCohort(actorContext, leader,
-                    (SnapshotBackedWriteTransaction<TransactionIdentifier>)tx, e);
-        }
+        return (LocalThreePhaseCommitCohort) tx.ready();
     }
 }
index f8a16b606a341316fde1e2806e07265c5b88c1f9..383a0e5a46d56bdace7094c9135a6d3b62c318d4 100644 (file)
@@ -180,7 +180,11 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
     }
 
     @Override
-    protected final DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<T> tx, final DataTreeModification tree) {
+    protected final DOMStoreThreePhaseCommitCohort transactionReady(
+            final SnapshotBackedWriteTransaction<T> tx,
+            final DataTreeModification tree,
+            final Exception readyError) {
+
         final State localState = state;
 
         if (localState instanceof Allocated) {
@@ -192,7 +196,7 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
             LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
         }
 
-        return createCohort(tx, tree);
+        return createCohort(tx, tree, readyError);
     }
 
     @Override
@@ -276,7 +280,10 @@ public abstract class AbstractSnapshotBackedTransactionChain<T> extends Transact
      *
      * @param transaction Transaction handle
      * @param modification {@link DataTreeModification} which needs to be applied to the backend
+     * @param operationError Any previous error that could be reported through three phase commit
      * @return A {@link DOMStoreThreePhaseCommitCohort} cohort.
      */
-    protected abstract DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<T> transaction, final DataTreeModification modification);
+    protected abstract DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<T> transaction,
+                                                                   final DataTreeModification modification,
+                                                                   final Exception operationError);
 }
index a5b7ea6253c0a6c15fcd745965b8052536246761..eaabb3a21fbb802e82e90fcd70a2ef1e41c46e39 100644 (file)
@@ -15,6 +15,7 @@ import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Throwables;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import javax.annotation.Nullable;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
@@ -138,8 +139,13 @@ public class SnapshotBackedWriteTransaction<T> extends AbstractDOMStoreTransacti
 
         final DataTreeModification tree = mutableTree;
         TREE_UPDATER.lazySet(this, null);
-        tree.ready();
-        return wasReady.transactionReady(this, tree);
+        try {
+            tree.ready();
+            return wasReady.transactionReady(this, tree, null);
+        } catch (RuntimeException e) {
+            LOG.debug("Store transaction: {}: unexpected failure when readying", getIdentifier(), e);
+            return wasReady.transactionReady(this, tree, e);
+        }
     }
 
     @Override
@@ -185,8 +191,12 @@ public class SnapshotBackedWriteTransaction<T> extends AbstractDOMStoreTransacti
          *            Transaction on which ready was invoked.
          * @param tree
          *            Modified data tree which has been constructed.
+         * @param readyError
+         *            Any error that has already happened when readying.
          * @return DOMStoreThreePhaseCommitCohort associated with transaction
          */
-        protected abstract DOMStoreThreePhaseCommitCohort transactionReady(SnapshotBackedWriteTransaction<T> tx, DataTreeModification tree);
+        protected abstract DOMStoreThreePhaseCommitCohort transactionReady(SnapshotBackedWriteTransaction<T> tx,
+                                                                           DataTreeModification tree,
+                                                                           @Nullable Exception readyError);
     }
 }
index 35d891dac025f7acc5cfb0095f40974ad2dfb601..89ca972b68b061cfec8a0cd0523a57fa2f43d897 100644 (file)
@@ -15,9 +15,12 @@ import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification
 final class ChainedTransactionCommitImpl extends InMemoryDOMStoreThreePhaseCommitCohort {
     private final DOMStoreTransactionChainImpl txChain;
 
-    ChainedTransactionCommitImpl(final InMemoryDOMDataStore store, final SnapshotBackedWriteTransaction<String> transaction,
-        final DataTreeModification modification, final DOMStoreTransactionChainImpl txChain) {
-        super(store, transaction, modification);
+    ChainedTransactionCommitImpl(final InMemoryDOMDataStore store,
+                                 final SnapshotBackedWriteTransaction<String> transaction,
+                                 final DataTreeModification modification,
+                                 final DOMStoreTransactionChainImpl txChain,
+                                 final Exception operationError) {
+        super(store, transaction, modification, operationError);
         this.txChain = Preconditions.checkNotNull(txChain);
     }
 
index 2cf79d899b3682bf06696e2465ad035b56c9e2a2..84c9c46cdd2ed4861287eca0a384beb239fabe64 100644 (file)
@@ -22,8 +22,10 @@ final class DOMStoreTransactionChainImpl extends AbstractSnapshotBackedTransacti
     }
 
     @Override
-    protected DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<String> tx, final DataTreeModification modification) {
-        return new ChainedTransactionCommitImpl(store, tx, modification, this);
+    protected DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<String> tx,
+                                                          final DataTreeModification modification,
+                                                          final Exception operationError) {
+        return new ChainedTransactionCommitImpl(store, tx, modification, this, operationError);
     }
 
     @Override
index b5e5e8331e0680910dc734689bf75e2dd9bb71aa..3582ff6ef1061749f1649cc58aed21def801d002 100644 (file)
@@ -223,9 +223,11 @@ public class InMemoryDOMDataStore extends TransactionReadyPrototype<String> impl
     }
 
     @Override
-    protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<String> tx, final DataTreeModification modification) {
+    protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<String> tx,
+                                                              final DataTreeModification modification,
+                                                              final Exception readyError) {
         LOG.debug("Tx: {} is submitted. Modifications: {}", tx.getIdentifier(), modification);
-        return new InMemoryDOMStoreThreePhaseCommitCohort(this, tx, modification);
+        return new InMemoryDOMStoreThreePhaseCommitCohort(this, tx, modification, readyError);
     }
 
     String nextIdentifier() {
index 8c326262b06e41761449173bca11cb54f18a12d9..a77732a11ffad421b48e96adab86122dde4c2926 100644 (file)
@@ -32,11 +32,16 @@ class InMemoryDOMStoreThreePhaseCommitCohort implements DOMStoreThreePhaseCommit
     private final DataTreeModification modification;
     private final InMemoryDOMDataStore store;
     private DataTreeCandidate candidate;
+    private final Exception operationError;
 
-    public InMemoryDOMStoreThreePhaseCommitCohort(final InMemoryDOMDataStore store, final SnapshotBackedWriteTransaction<String> writeTransaction, final DataTreeModification modification) {
+    public InMemoryDOMStoreThreePhaseCommitCohort(final InMemoryDOMDataStore store,
+                                                  final SnapshotBackedWriteTransaction<String> writeTransaction,
+                                                  final DataTreeModification modification,
+                                                  final Exception operationError) {
         this.transaction = Preconditions.checkNotNull(writeTransaction);
         this.modification = Preconditions.checkNotNull(modification);
         this.store = Preconditions.checkNotNull(store);
+        this.operationError = operationError;
     }
 
     private static void warnDebugContext(final AbstractDOMStoreTransaction<?> transaction) {
@@ -48,6 +53,10 @@ class InMemoryDOMStoreThreePhaseCommitCohort implements DOMStoreThreePhaseCommit
 
     @Override
     public final ListenableFuture<Boolean> canCommit() {
+        if (operationError != null) {
+            return Futures.immediateFailedFuture(operationError);
+        }
+
         try {
             store.validate(modification);
             LOG.debug("Store Transaction: {} can be committed", getTransaction().getIdentifier());
index 568f88376cbc816b9ea151a68fb36b1d4a5f15a7..5734f9ac5b27c635fdf01a9fab66d084863c0ab4 100644 (file)
@@ -16,10 +16,12 @@ import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
 import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
@@ -31,10 +33,12 @@ import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransact
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 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.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
@@ -329,6 +333,23 @@ public class InMemoryDataStoreTest {
         writeTx.ready();
     }
 
+    @Test
+    public void testReadyWithMissingMandatoryData() throws InterruptedException {
+        DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+        NormalizedNode<?, ?> testNode = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
+                .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
+                .build();
+        writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
+        DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
+        try {
+            ready.canCommit().get();
+            Assert.fail("Expected exception on canCommit");
+        } catch (ExecutionException e) {
+            // nop
+        }
+    }
+
     @Test
     public void testTransactionAbort() throws InterruptedException, ExecutionException {
 
index 72ecf3e6a560993f3d454a1d451e0997fe1bd062..96749b037d8cf3c8f89d70d6eed07edcdf3d55da 100644 (file)
@@ -27,6 +27,16 @@ public class TestModel {
     public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME);
     public static final YangInstanceIdentifier OUTER_LIST_PATH =
             YangInstanceIdentifier.builder(TEST_PATH).node(OUTER_LIST_QNAME).build();
+
+    public static final QName MANDATORY_DATA_TEST_QNAME =
+            QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test",
+                    "2014-03-13",
+                    "mandatory-data-test");
+    public static final QName OPTIONAL_QNAME = QName.create(MANDATORY_DATA_TEST_QNAME, "optional-data");
+    public static final QName MANDATORY_QNAME = QName.create(MANDATORY_DATA_TEST_QNAME, "mandatory-data");
+    public static final YangInstanceIdentifier MANDATORY_DATA_TEST_PATH =
+            YangInstanceIdentifier.of(MANDATORY_DATA_TEST_QNAME);
+
     private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
 
     public static SchemaContext createTestContext() {
index 2d7601e0b85a29434385ef36db290d230712abb9..ffc8fe07c88b9adfeaa078e586b996616b7b633b 100644 (file)
@@ -41,4 +41,16 @@ module odl-datastore-test {
             }
         }
     }
+
+    container mandatory-data-test {
+        presence "needs to be present when empty";
+
+        leaf optional-data {
+            type string;
+        }
+        leaf mandatory-data {
+            type string;
+            mandatory true;
+        }
+    }
 }