BUG 7801: prevent OptimisticLockFailedExceptions in write-transactions. 84/53784/7
authorTomas Cere <tcere@cisco.com>
Fri, 24 Mar 2017 10:29:16 +0000 (11:29 +0100)
committerTom Pantelis <tompantelis@gmail.com>
Sat, 1 Apr 2017 08:40:14 +0000 (08:40 +0000)
When multiple instances of this rpc are running concurrently in paralel
we would run into an optimistic lock since every instance tries to write
the topmost parent list first.
When these happen handle these failures as expected and resume with the
next stage of the rpc.

Change-Id: I43efaea3315b04272113eb86733e68609e434984
Signed-off-by: Tomas Cere <tcere@cisco.com>
opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/impl/WriteTransactionsHandler.java

index fd47b7176b2411b2bb629d65c56c5a1bac7474af..664e7fd26fcb85f8fedbb2ae1f36c0fe2c2ea8ac 100644 (file)
@@ -26,6 +26,7 @@ import java.util.concurrent.TimeUnit;
 import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
@@ -127,17 +128,31 @@ public class WriteTransactionsHandler implements Runnable {
 
     private boolean ensureListExists(final SettableFuture<RpcResult<WriteTransactionsOutput>> settableFuture) {
 
 
     private boolean ensureListExists(final SettableFuture<RpcResult<WriteTransactionsOutput>> settableFuture) {
 
+        final MapNode mapNode = ImmutableNodes.mapNodeBuilder(ID_INTS).build();
+
+        DOMDataWriteTransaction tx = txProvider.createTransaction();
+        // write only the top list
+        tx.merge(LogicalDatastoreType.CONFIGURATION, ID_INTS_YID, mapNode);
+        try {
+            tx.submit().checkedGet();
+        } catch (final OptimisticLockFailedException e) {
+            // when multiple write-transactions are executed concurrently we need to ignore this.
+            // If we get optimistic lock here it means id-ints already exists and we can continue.
+            LOG.debug("Got an optimistic lock when writing initial top level list element.", e);
+        } catch (final TransactionCommitFailedException e) {
+            LOG.warn("Unable to ensure IdInts list for id: {} exists.", id, e);
+            settableFuture.set(RpcResultBuilder.<WriteTransactionsOutput>failed()
+                    .withError(RpcError.ErrorType.APPLICATION, "Unexpected-exception", e).build());
+            return false;
+        }
+
         final MapEntryNode entry = ImmutableNodes.mapEntryBuilder(ID_INTS, ID, id)
                 .withChild(ImmutableNodes.mapNodeBuilder(ITEM).build())
                 .build();
         final MapEntryNode entry = ImmutableNodes.mapEntryBuilder(ID_INTS, ID, id)
                 .withChild(ImmutableNodes.mapNodeBuilder(ITEM).build())
                 .build();
-        final MapNode mapNode =
-                ImmutableNodes.mapNodeBuilder(ID_INTS)
-                        .withChild(entry)
-                        .build();
 
 
-        final DOMDataWriteTransaction tx = txProvider.createTransaction();
         idListWithKey = ID_INTS_YID.node(entry.getIdentifier());
         idListWithKey = ID_INTS_YID.node(entry.getIdentifier());
-        tx.merge(LogicalDatastoreType.CONFIGURATION, ID_INTS_YID, mapNode);
+        tx = txProvider.createTransaction();
+        tx.merge(LogicalDatastoreType.CONFIGURATION, idListWithKey, entry);
 
         try {
             tx.submit().checkedGet();
 
         try {
             tx.submit().checkedGet();