use of akka.dispatch.OnComplete in project controller by opendaylight.
the class ShardTest method testConcurrentThreePhaseCommits.
@Test
@SuppressWarnings("checkstyle:IllegalCatch")
public void testConcurrentThreePhaseCommits() throws Exception {
final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
final CountDownLatch commitLatch = new CountDownLatch(2);
final long timeoutSec = 5;
final FiniteDuration duration = FiniteDuration.create(timeoutSec, TimeUnit.SECONDS);
final Timeout timeout = new Timeout(duration);
final TestActorRef<Shard> shard = actorFactory.createTestActor(newShardProps().withDispatcher(Dispatchers.DefaultDispatcherId()), "testConcurrentThreePhaseCommits");
class OnFutureComplete extends OnComplete<Object> {
private final Class<?> expRespType;
OnFutureComplete(final Class<?> expRespType) {
this.expRespType = expRespType;
}
@Override
public void onComplete(final Throwable error, final Object resp) {
if (error != null) {
caughtEx.set(new AssertionError(getClass().getSimpleName() + " failure", error));
} else {
try {
assertEquals("Commit response type", expRespType, resp.getClass());
onSuccess(resp);
} catch (final Exception e) {
caughtEx.set(e);
}
}
}
void onSuccess(final Object resp) throws Exception {
}
}
class OnCommitFutureComplete extends OnFutureComplete {
OnCommitFutureComplete() {
super(CommitTransactionReply.class);
}
@Override
public void onComplete(final Throwable error, final Object resp) {
super.onComplete(error, resp);
commitLatch.countDown();
}
}
class OnCanCommitFutureComplete extends OnFutureComplete {
private final TransactionIdentifier transactionID;
OnCanCommitFutureComplete(final TransactionIdentifier transactionID) {
super(CanCommitTransactionReply.class);
this.transactionID = transactionID;
}
@Override
void onSuccess(final Object resp) throws Exception {
final CanCommitTransactionReply canCommitReply = CanCommitTransactionReply.fromSerializable(resp);
assertEquals("Can commit", true, canCommitReply.getCanCommit());
final Future<Object> commitFuture = Patterns.ask(shard, new CommitTransaction(transactionID, CURRENT_VERSION).toSerializable(), timeout);
commitFuture.onComplete(new OnCommitFutureComplete(), getSystem().dispatcher());
}
}
new ShardTestKit(getSystem()) {
{
waitUntilLeader(shard);
final TransactionIdentifier transactionID1 = nextTransactionId();
final TransactionIdentifier transactionID2 = nextTransactionId();
final TransactionIdentifier transactionID3 = nextTransactionId();
final Map<TransactionIdentifier, CapturingShardDataTreeCohort> cohortMap = setupCohortDecorator(shard.underlyingActor(), transactionID1, transactionID2, transactionID3);
final CapturingShardDataTreeCohort cohort1 = cohortMap.get(transactionID1);
final CapturingShardDataTreeCohort cohort2 = cohortMap.get(transactionID2);
final CapturingShardDataTreeCohort cohort3 = cohortMap.get(transactionID3);
shard.tell(prepareBatchedModifications(transactionID1, TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME), false), getRef());
final ReadyTransactionReply readyReply = ReadyTransactionReply.fromSerializable(expectMsgClass(duration, ReadyTransactionReply.class));
assertEquals("Cohort path", shard.path().toString(), readyReply.getCohortPath());
// Send the CanCommitTransaction message for the first Tx.
shard.tell(new CanCommitTransaction(transactionID1, CURRENT_VERSION).toSerializable(), getRef());
final CanCommitTransactionReply canCommitReply = CanCommitTransactionReply.fromSerializable(expectMsgClass(duration, CanCommitTransactionReply.class));
assertEquals("Can commit", true, canCommitReply.getCanCommit());
// Ready 2 more Tx's.
shard.tell(prepareBatchedModifications(transactionID2, TestModel.OUTER_LIST_PATH, ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build(), false), getRef());
expectMsgClass(duration, ReadyTransactionReply.class);
shard.tell(prepareBatchedModifications(transactionID3, YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH).nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1).build(), ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1), false), getRef());
expectMsgClass(duration, ReadyTransactionReply.class);
// Send the CanCommitTransaction message for the next 2 Tx's.
// These should get queued and
// processed after the first Tx completes.
final Future<Object> canCommitFuture1 = Patterns.ask(shard, new CanCommitTransaction(transactionID2, CURRENT_VERSION).toSerializable(), timeout);
final Future<Object> canCommitFuture2 = Patterns.ask(shard, new CanCommitTransaction(transactionID3, CURRENT_VERSION).toSerializable(), timeout);
// Send the CommitTransaction message for the first Tx. After it
// completes, it should
// trigger the 2nd Tx to proceed which should in turn then
// trigger the 3rd.
shard.tell(new CommitTransaction(transactionID1, CURRENT_VERSION).toSerializable(), getRef());
expectMsgClass(duration, CommitTransactionReply.class);
// Wait for the next 2 Tx's to complete.
canCommitFuture1.onComplete(new OnCanCommitFutureComplete(transactionID2), getSystem().dispatcher());
canCommitFuture2.onComplete(new OnCanCommitFutureComplete(transactionID3), getSystem().dispatcher());
final boolean done = commitLatch.await(timeoutSec, TimeUnit.SECONDS);
final Throwable t = caughtEx.get();
if (t != null) {
Throwables.propagateIfPossible(t, Exception.class);
throw new RuntimeException(t);
}
assertEquals("Commits complete", true, done);
// final InOrder inOrder = inOrder(cohort1.getCanCommit(), cohort1.getPreCommit(), cohort1.getCommit(),
// cohort2.getCanCommit(), cohort2.getPreCommit(), cohort2.getCommit(), cohort3.getCanCommit(),
// cohort3.getPreCommit(), cohort3.getCommit());
// inOrder.verify(cohort1.getCanCommit()).onSuccess(any(Void.class));
// inOrder.verify(cohort1.getPreCommit()).onSuccess(any(DataTreeCandidate.class));
// inOrder.verify(cohort2.getCanCommit()).onSuccess(any(Void.class));
// inOrder.verify(cohort2.getPreCommit()).onSuccess(any(DataTreeCandidate.class));
// inOrder.verify(cohort3.getCanCommit()).onSuccess(any(Void.class));
// inOrder.verify(cohort3.getPreCommit()).onSuccess(any(DataTreeCandidate.class));
// inOrder.verify(cohort1.getCommit()).onSuccess(any(UnsignedLong.class));
// inOrder.verify(cohort2.getCommit()).onSuccess(any(UnsignedLong.class));
// inOrder.verify(cohort3.getCommit()).onSuccess(any(UnsignedLong.class));
// Verify data in the data store.
verifyOuterListEntry(shard, 1);
verifyLastApplied(shard, 5);
}
};
}
use of akka.dispatch.OnComplete in project controller by opendaylight.
the class ThreePhaseCommitCohortProxy method finishCanCommit.
private void finishCanCommit(final SettableFuture<Boolean> returnFuture) {
LOG.debug("Tx {} finishCanCommit", transactionId);
// For empty transactions return immediately
if (cohorts.size() == 0) {
LOG.debug("Tx {}: canCommit returning result true", transactionId);
returnFuture.set(Boolean.TRUE);
return;
}
commitOperationCallback = new TransactionRateLimitingCallback(actorContext);
commitOperationCallback.run();
final Iterator<CohortInfo> iterator = cohorts.iterator();
final OnComplete<Object> onComplete = new OnComplete<Object>() {
@Override
public void onComplete(final Throwable failure, final Object response) {
if (failure != null) {
LOG.debug("Tx {}: a canCommit cohort Future failed", transactionId, failure);
returnFuture.setException(failure);
commitOperationCallback.failure();
return;
}
// Only the first call to pause takes effect - subsequent calls before resume are no-ops. So
// this means we'll only time the first transaction canCommit which should be fine.
commitOperationCallback.pause();
boolean result = true;
if (CanCommitTransactionReply.isSerializedType(response)) {
CanCommitTransactionReply reply = CanCommitTransactionReply.fromSerializable(response);
LOG.debug("Tx {}: received {}", transactionId, response);
if (!reply.getCanCommit()) {
result = false;
}
} else {
LOG.error("Unexpected response type {}", response.getClass());
returnFuture.setException(new IllegalArgumentException(String.format("Unexpected response type %s", response.getClass())));
return;
}
if (iterator.hasNext() && result) {
sendCanCommitTransaction(iterator.next(), this);
} else {
LOG.debug("Tx {}: canCommit returning result: {}", transactionId, result);
returnFuture.set(Boolean.valueOf(result));
}
}
};
sendCanCommitTransaction(iterator.next(), onComplete);
}
use of akka.dispatch.OnComplete in project controller by opendaylight.
the class TransactionChainProxy method findPrimaryShard.
/**
* This method is overridden to ensure the previous Tx's ready operations complete
* before we initiate the next Tx in the chain to avoid creation failures if the
* previous Tx's ready operations haven't completed yet.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
protected Future<PrimaryShardInfo> findPrimaryShard(final String shardName, final TransactionIdentifier txId) {
// Read current state atomically
final State localState = currentState;
// There are no outstanding futures, shortcut
Future<?> previous = localState.previousFuture();
if (previous == null) {
return combineFutureWithPossiblePriorReadOnlyTxFutures(parent.findPrimaryShard(shardName, txId), txId);
}
final String previousTransactionId;
if (localState instanceof Pending) {
previousTransactionId = ((Pending) localState).getIdentifier().toString();
LOG.debug("Tx: {} - waiting for ready futures with pending Tx {}", txId, previousTransactionId);
} else {
previousTransactionId = "";
LOG.debug("Waiting for ready futures on chain {}", getHistoryId());
}
previous = combineFutureWithPossiblePriorReadOnlyTxFutures(previous, txId);
// Add a callback for completion of the combined Futures.
final Promise<PrimaryShardInfo> returnPromise = Futures.promise();
final OnComplete onComplete = new OnComplete() {
@Override
public void onComplete(final Throwable failure, final Object notUsed) {
if (failure != null) {
// A Ready Future failed so fail the returned Promise.
LOG.error("Tx: {} - ready future failed for previous Tx {}", txId, previousTransactionId);
returnPromise.failure(failure);
} else {
LOG.debug("Tx: {} - previous Tx {} readied - proceeding to FindPrimaryShard", txId, previousTransactionId);
// Send the FindPrimaryShard message and use the resulting Future to complete the
// returned Promise.
returnPromise.completeWith(parent.findPrimaryShard(shardName, txId));
}
}
};
previous.onComplete(onComplete, getActorContext().getClientDispatcher());
return returnPromise.future();
}
use of akka.dispatch.OnComplete in project controller by opendaylight.
the class RemoteTransactionContext method executeRead.
@Override
public <T> void executeRead(final AbstractRead<T> readCmd, final SettableFuture<T> returnFuture) {
LOG.debug("Tx {} executeRead {} called path = {}", getIdentifier(), readCmd.getClass().getSimpleName(), readCmd.getPath());
final Throwable failure = failedModification;
if (failure != null) {
// If we know there was a previous modification failure, we must not send a read request, as it risks
// returning incorrect data. We check this before acquiring an operation simply because we want the app
// to complete this transaction as soon as possible.
returnFuture.setException(new ReadFailedException("Previous modification failed, cannot " + readCmd.getClass().getSimpleName() + " for path " + readCmd.getPath(), failure));
return;
}
// Send any batched modifications. This is necessary to honor the read uncommitted semantics of the
// public API contract.
final boolean havePermit = acquireOperation();
sendBatchedModifications();
OnComplete<Object> onComplete = new OnComplete<Object>() {
@Override
public void onComplete(Throwable failure, Object response) {
// We have previously acquired an operation, now release it, no matter what happened
if (havePermit) {
limiter.release();
}
if (failure != null) {
LOG.debug("Tx {} {} operation failed: {}", getIdentifier(), readCmd.getClass().getSimpleName(), failure);
returnFuture.setException(new ReadFailedException("Error checking " + readCmd.getClass().getSimpleName() + " for path " + readCmd.getPath(), failure));
} else {
LOG.debug("Tx {} {} operation succeeded", getIdentifier(), readCmd.getClass().getSimpleName());
readCmd.processResponse(response, returnFuture);
}
}
};
final Future<Object> future = actorContext.executeOperationAsync(getActor(), readCmd.asVersion(getTransactionVersion()).toSerializable(), actorContext.getOperationTimeout());
future.onComplete(onComplete, actorContext.getClientDispatcher());
}
Aggregations