+ // Send a close transaction chain request to each and every shard
+ actorContext.broadcast(new CloseTransactionChain(transactionChainId));
+ }
+
+ public String getTransactionChainId() {
+ return transactionChainId;
+ }
+
+ private class ChainedTransactionProxy extends TransactionProxy {
+
+ ChainedTransactionProxy(ActorContext actorContext, TransactionType transactionType) {
+ super(actorContext, transactionType, transactionChainId);
+ }
+
+ @Override
+ protected void onTransactionReady(List<Future<ActorSelection>> cohortFutures) {
+ if(!cohortFutures.isEmpty()) {
+ previousTxReadyFutures = new SimpleEntry<>(getIdentifier(), cohortFutures);
+ } else {
+ previousTxReadyFutures = null;
+ }
+ }
+
+ /**
+ * This method is overridden to ensure the previous Tx's ready operations complete
+ * before we create the next shard Tx in the chain to avoid creation failures if the
+ * previous Tx's ready operations haven't completed yet.
+ */
+ @Override
+ protected Future<Object> sendCreateTransaction(final ActorSelection shard,
+ final Object serializedCreateMessage) {
+ // Check if there are any previous ready Futures. Also make sure the previous ready
+ // Futures aren't for this Tx as deadlock would occur if tried to wait on our own
+ // Futures. This may happen b/c the shard Tx creates are done async so it's possible
+ // for the client to ready this Tx before we've even attempted to create a shard Tx.
+ if(previousTxReadyFutures == null ||
+ previousTxReadyFutures.getKey().equals(getIdentifier())) {
+ return super.sendCreateTransaction(shard, serializedCreateMessage);
+ }
+
+ // Combine the ready Futures into 1.
+ Future<Iterable<ActorSelection>> combinedFutures = akka.dispatch.Futures.sequence(
+ previousTxReadyFutures.getValue(), actorContext.getActorSystem().dispatcher());
+
+ // Add a callback for completion of the combined Futures.
+ final Promise<Object> createTxPromise = akka.dispatch.Futures.promise();
+ OnComplete<Iterable<ActorSelection>> onComplete = new OnComplete<Iterable<ActorSelection>>() {
+ @Override
+ public void onComplete(Throwable failure, Iterable<ActorSelection> notUsed) {
+ if(failure != null) {
+ // A Ready Future failed so fail the returned Promise.
+ createTxPromise.failure(failure);
+ } else {
+ // Send the CreateTx message and use the resulting Future to complete the
+ // returned Promise.
+ createTxPromise.completeWith(actorContext.executeOperationAsync(shard,
+ serializedCreateMessage));
+ }
+ }
+ };
+
+ combinedFutures.onComplete(onComplete, actorContext.getActorSystem().dispatcher());
+
+ return createTxPromise.future();
+ }