BUG-5280: switch transactionIdentifier
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / TransactionContextWrapper.java
index ea4e5139425f5eee21151a0cc759910d77b8f6cc..ef9ee68bf016df46e5dfcd1711d42adcc20f4664 100644 (file)
@@ -7,14 +7,21 @@
  */
 package org.opendaylight.controller.cluster.datastore;
 
+import akka.actor.ActorSelection;
+import akka.dispatch.Futures;
+import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 import javax.annotation.concurrent.GuardedBy;
-import org.opendaylight.controller.cluster.datastore.identifiers.TransactionIdentifier;
+import org.opendaylight.controller.cluster.access.concepts.TransactionIdentifier;
+import org.opendaylight.controller.cluster.datastore.utils.ActorContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import scala.concurrent.Future;
+import scala.concurrent.Promise;
 
 /**
  * A helper class that wraps an eventual TransactionContext instance. Operations destined for the target
@@ -32,15 +39,20 @@ class TransactionContextWrapper {
     @GuardedBy("queuedTxOperations")
     private final List<TransactionOperation> queuedTxOperations = Lists.newArrayList();
 
+    private final TransactionIdentifier identifier;
+
     /**
      * The resulting TransactionContext.
      */
     private volatile TransactionContext transactionContext;
 
-    private final TransactionIdentifier identifier;
+    private final OperationLimiter limiter;
 
-    TransactionContextWrapper(final TransactionIdentifier identifier) {
-        this.identifier = identifier;
+    TransactionContextWrapper(final TransactionIdentifier identifier, final ActorContext actorContext) {
+        this.identifier = Preconditions.checkNotNull(identifier);
+        this.limiter = new OperationLimiter(identifier,
+                actorContext.getDatastoreContext().getShardBatchedModificationCount() + 1, // 1 extra permit for the ready operation
+                TimeUnit.MILLISECONDS.toSeconds(actorContext.getDatastoreContext().getOperationTimeoutInMillis()));
     }
 
     TransactionContext getTransactionContext() {
@@ -56,7 +68,7 @@ class TransactionContextWrapper {
      */
     private void enqueueTransactionOperation(final TransactionOperation operation) {
         final boolean invokeOperation;
-        synchronized(queuedTxOperations) {
+        synchronized (queuedTxOperations) {
             if (transactionContext == null) {
                 LOG.debug("Tx {} Queuing TransactionOperation", getIdentifier());
 
@@ -69,6 +81,8 @@ class TransactionContextWrapper {
 
         if (invokeOperation) {
             operation.invoke(transactionContext);
+        } else {
+            limiter.acquire();
         }
     }
 
@@ -95,10 +109,14 @@ class TransactionContextWrapper {
             // queued (eg a put operation from a client read Future callback that is notified
             // synchronously).
             Collection<TransactionOperation> operationsBatch = null;
-            synchronized(queuedTxOperations) {
-                if(queuedTxOperations.isEmpty()) {
+            synchronized (queuedTxOperations) {
+                if (queuedTxOperations.isEmpty()) {
                     // We're done invoking the TransactionOperations so we can now publish the
                     // TransactionContext.
+                    localTransactionContext.operationHandOffComplete();
+                    if(!localTransactionContext.usesOperationLimiting()){
+                        limiter.releaseAll();
+                    }
                     transactionContext = localTransactionContext;
                     break;
                 }
@@ -110,9 +128,32 @@ class TransactionContextWrapper {
             // Invoke TransactionOperations outside the sync block to avoid unnecessary blocking.
             // A slight down-side is that we need to re-acquire the lock below but this should
             // be negligible.
-            for(TransactionOperation oper: operationsBatch) {
+            for (TransactionOperation oper : operationsBatch) {
                 oper.invoke(localTransactionContext);
             }
         }
     }
+
+    Future<ActorSelection> readyTransaction() {
+        // avoid the creation of a promise and a TransactionOperation
+        if (transactionContext != null) {
+            return transactionContext.readyTransaction();
+        }
+
+        final Promise<ActorSelection> promise = Futures.promise();
+        enqueueTransactionOperation(new TransactionOperation() {
+            @Override
+            public void invoke(TransactionContext transactionContext) {
+                promise.completeWith(transactionContext.readyTransaction());
+            }
+        });
+
+        return promise.future();
+    }
+
+    public OperationLimiter getLimiter() {
+        return limiter;
+    }
+
+
 }