BUG-5626: remove CompositeModification(ByteString)Payload
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / SimpleShardDataTreeCohort.java
index 9f22ce8a73e2380625f2f03c0e1ab5e7b35a2c29..5dda3612a14150c1397664d2ef94918795a09ea0 100644 (file)
@@ -10,8 +10,13 @@ package org.opendaylight.controller.cluster.datastore;
 import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
+import org.opendaylight.controller.cluster.datastore.utils.PruningDataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateTip;
 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -21,11 +26,14 @@ final class SimpleShardDataTreeCohort extends ShardDataTreeCohort {
     private static final ListenableFuture<Void> VOID_FUTURE = Futures.immediateFuture(null);
     private final DataTreeModification transaction;
     private final ShardDataTree dataTree;
+    private final String transactionId;
     private DataTreeCandidateTip candidate;
 
-    SimpleShardDataTreeCohort(final ShardDataTree dataTree, final DataTreeModification transaction) {
+    SimpleShardDataTreeCohort(final ShardDataTree dataTree, final DataTreeModification transaction,
+            final String transactionId) {
         this.dataTree = Preconditions.checkNotNull(dataTree);
         this.transaction = Preconditions.checkNotNull(transaction);
+        this.transactionId = transactionId;
     }
 
     @Override
@@ -35,11 +43,25 @@ final class SimpleShardDataTreeCohort extends ShardDataTreeCohort {
 
     @Override
     public ListenableFuture<Boolean> canCommit() {
+        DataTreeModification modification = getDataTreeModification();
         try {
-            dataTree.getDataTree().validate(transaction);
-            LOG.debug("Transaction {} validated", transaction);
+            dataTree.getDataTree().validate(modification);
+            LOG.trace("Transaction {} validated", transaction);
             return TRUE_FUTURE;
+        }
+        catch (ConflictingModificationAppliedException e) {
+            LOG.warn("Store Tx {}: Conflicting modification for path {}.", transactionId, e.getPath());
+            return Futures.immediateFailedFuture(new OptimisticLockFailedException("Optimistic lock failed.", e));
+        } catch (DataValidationFailedException e) {
+            LOG.warn("Store Tx {}: Data validation failed for path {}.", transactionId, e.getPath(), e);
+
+            // For debugging purposes, allow dumping of the modification. Coupled with the above
+            // precondition log, it should allow us to understand what went on.
+            LOG.debug("Store Tx {}: modifications: {} tree: {}", transactionId, modification, dataTree.getDataTree());
+
+            return Futures.immediateFailedFuture(new TransactionCommitFailedException("Data did not pass validation.", e));
         } catch (Exception e) {
+            LOG.warn("Unexpected failure in validation phase", e);
             return Futures.immediateFailedFuture(e);
         }
     }
@@ -47,19 +69,32 @@ final class SimpleShardDataTreeCohort extends ShardDataTreeCohort {
     @Override
     public ListenableFuture<Void> preCommit() {
         try {
-            candidate = dataTree.getDataTree().prepare(transaction);
+            candidate = dataTree.getDataTree().prepare(getDataTreeModification());
             /*
              * FIXME: this is the place where we should be interacting with persistence, specifically by invoking
              *        persist on the candidate (which gives us a Future).
              */
-            LOG.debug("Transaction {} prepared candidate {}", transaction, candidate);
+            LOG.trace("Transaction {} prepared candidate {}", transaction, candidate);
             return VOID_FUTURE;
         } catch (Exception e) {
-            LOG.debug("Transaction {} failed to prepare", transaction, e);
+            if(LOG.isTraceEnabled()) {
+                LOG.trace("Transaction {} failed to prepare", transaction, e);
+            } else {
+                LOG.error("Transaction failed to prepare", e);
+            }
             return Futures.immediateFailedFuture(e);
         }
     }
 
+    @Override
+    DataTreeModification getDataTreeModification() {
+        DataTreeModification dataTreeModification = transaction;
+        if(transaction instanceof PruningDataTreeModification){
+            dataTreeModification = ((PruningDataTreeModification) transaction).getResultingModification();
+        }
+        return dataTreeModification;
+    }
+
     @Override
     public ListenableFuture<Void> abort() {
         // No-op, really
@@ -71,11 +106,15 @@ final class SimpleShardDataTreeCohort extends ShardDataTreeCohort {
         try {
             dataTree.getDataTree().commit(candidate);
         } catch (Exception e) {
-            LOG.error("Transaction {} failed to commit", transaction, e);
+            if(LOG.isTraceEnabled()) {
+                LOG.trace("Transaction {} failed to commit", transaction, e);
+            } else {
+                LOG.error("Transaction failed to commit", e);
+            }
             return Futures.immediateFailedFuture(e);
         }
 
-        LOG.debug("Transaction {} committed, proceeding to notify", transaction);
+        LOG.trace("Transaction {} committed, proceeding to notify", transaction);
         dataTree.notifyListeners(candidate);
         return VOID_FUTURE;
     }