*/
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.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
@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(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() {
if (invokeOperation) {
operation.invoke(transactionContext);
+ } else {
+ limiter.acquire();
}
}
// 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;
}
// 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;
+ }
+
+
}