@Override protected void onStateChanged() { boolean isLeader = isLeader(); boolean hasLeader = hasLeader(); changeSupport.onLeadershipChange(isLeader, hasLeader); treeChangeSupport.onLeadershipChange(isLeader, hasLeader); // If this actor is no longer the leader close all the transaction chains if (!isLeader) { if (LOG.isDebugEnabled()) { LOG.debug( "{}: onStateChanged: Closing all transaction chains because shard {} is no longer the leader", persistenceId(), getId()); } store.closeAllTransactionChains(); commitCoordinator.abortPendingTransactions( "The transacton was aborted due to inflight leadership change.", this); } if (hasLeader && !isIsolatedLeader()) { messageRetrySupport.retryMessages(); } }
private void handleForwardedReadyTransaction(ForwardedReadyTransaction forwardedReady) { LOG.debug( "{}: handleForwardedReadyTransaction for {}", persistenceId(), forwardedReady.getTransactionID()); boolean isLeaderActive = isLeaderActive(); if (isLeader() && isLeaderActive) { commitCoordinator.handleForwardedReadyTransaction(forwardedReady, getSender(), this); } else { ActorSelection leader = getLeader(); if (!isLeaderActive || leader == null) { messageRetrySupport.addMessageToRetry( forwardedReady, getSender(), "Could not commit transaction " + forwardedReady.getTransactionID()); } else { LOG.debug("{}: Forwarding ForwardedReadyTransaction to leader {}", persistenceId(), leader); ReadyLocalTransaction readyLocal = new ReadyLocalTransaction( forwardedReady.getTransactionID(), forwardedReady.getTransaction().getSnapshot(), forwardedReady.isDoImmediateCommit()); readyLocal.setRemoteVersion(getCurrentBehavior().getLeaderPayloadVersion()); leader.forward(readyLocal, getContext()); } } }
private void finishCommit( @Nonnull final ActorRef sender, @Nonnull final String transactionID, @Nonnull final CohortEntry cohortEntry) { LOG.debug( "{}: Finishing commit for transaction {}", persistenceId(), cohortEntry.getTransactionID()); try { cohortEntry.commit(); sender.tell(CommitTransactionReply.INSTANCE.toSerializable(), getSelf()); shardMBean.incrementCommittedTransactionCount(); shardMBean.setLastCommittedTransactionTime(System.currentTimeMillis()); } catch (Exception e) { sender.tell(new akka.actor.Status.Failure(e), getSelf()); LOG.error( "{}, An exception occurred while committing transaction {}", persistenceId(), transactionID, e); shardMBean.incrementFailedTransactionsCount(); } finally { commitCoordinator.currentTransactionComplete(transactionID, true); } }
private void handleReadyLocalTransaction(final ReadyLocalTransaction message) { LOG.debug( "{}: handleReadyLocalTransaction for {}", persistenceId(), message.getTransactionID()); boolean isLeaderActive = isLeaderActive(); if (isLeader() && isLeaderActive) { try { commitCoordinator.handleReadyLocalTransaction(message, getSender(), this); } catch (Exception e) { LOG.error( "{}: Error handling ReadyLocalTransaction for Tx {}", persistenceId(), message.getTransactionID(), e); getSender().tell(new akka.actor.Status.Failure(e), getSelf()); } } else { ActorSelection leader = getLeader(); if (!isLeaderActive || leader == null) { messageRetrySupport.addMessageToRetry( message, getSender(), "Could not commit transaction " + message.getTransactionID()); } else { LOG.debug("{}: Forwarding ReadyLocalTransaction to leader {}", persistenceId(), leader); message.setRemoteVersion(getCurrentBehavior().getLeaderPayloadVersion()); leader.forward(message, getContext()); } } }
protected void handleBatchedModificationsLocal(BatchedModifications batched, ActorRef sender) { try { commitCoordinator.handleBatchedModifications(batched, sender, this); } catch (Exception e) { LOG.error( "{}: Error handling BatchedModifications for Tx {}", persistenceId(), batched.getTransactionID(), e); sender.tell(new akka.actor.Status.Failure(e), getSelf()); } }
private void finishCommit(@Nonnull final ActorRef sender, final @Nonnull String transactionID) { // With persistence enabled, this method is called via applyState by the leader strategy // after the commit has been replicated to a majority of the followers. CohortEntry cohortEntry = commitCoordinator.getCohortEntryIfCurrent(transactionID); if (cohortEntry == null) { // The transaction is no longer the current commit. This can happen if the transaction // was aborted prior, most likely due to timeout in the front-end. We need to finish // committing the transaction though since it was successfully persisted and replicated // however we can't use the original cohort b/c it was already preCommitted and may // conflict with the current commit or may have been aborted so we commit with a new // transaction. cohortEntry = commitCoordinator.getAndRemoveCohortEntry(transactionID); if (cohortEntry != null) { try { store.applyForeignCandidate(transactionID, cohortEntry.getCandidate()); } catch (DataValidationFailedException e) { shardMBean.incrementFailedTransactionsCount(); LOG.error("{}: Failed to re-apply transaction {}", persistenceId(), transactionID, e); } sender.tell(CommitTransactionReply.INSTANCE.toSerializable(), getSelf()); } else { // This really shouldn't happen - it likely means that persistence or replication // took so long to complete such that the cohort entry was expired from the cache. IllegalStateException ex = new IllegalStateException( String.format( "%s: Could not finish committing transaction %s - no CohortEntry found", persistenceId(), transactionID)); LOG.error(ex.getMessage()); sender.tell(new akka.actor.Status.Failure(ex), getSelf()); } } else { finishCommit(sender, transactionID, cohortEntry); } }
protected void onDatastoreContext(DatastoreContext context) { datastoreContext = context; commitCoordinator.setQueueCapacity(datastoreContext.getShardTransactionCommitQueueCapacity()); setTransactionCommitTimeout(); if (datastoreContext.isPersistent() && !persistence().isRecoveryApplicable()) { setPersistence(true); } else if (!datastoreContext.isPersistent() && persistence().isRecoveryApplicable()) { setPersistence(false); } updateConfigParams(datastoreContext.getShardRaftConfig()); }
@Override public void postStop() { LOG.info("Stopping Shard {}", persistenceId()); super.postStop(); messageRetrySupport.close(); if (txCommitTimeoutCheckSchedule != null) { txCommitTimeoutCheckSchedule.cancel(); } commitCoordinator.abortPendingTransactions("Transaction aborted due to shutdown.", this); shardMBean.unregisterMBean(); }
@Override protected void pauseLeader(Runnable operation) { LOG.debug("{}: In pauseLeader, operation: {}", persistenceId(), operation); commitCoordinator.setRunOnPendingTransactionsComplete(operation); }
void doAbortTransaction(final String transactionID, final ActorRef sender) { commitCoordinator.handleAbort(transactionID, sender, this); }
private void handleCanCommitTransaction(final CanCommitTransaction canCommit) { LOG.debug("{}: Can committing transaction {}", persistenceId(), canCommit.getTransactionID()); commitCoordinator.handleCanCommit(canCommit.getTransactionID(), getSender(), this); }
private void handleCommitTransaction(final CommitTransaction commit) { if (!commitCoordinator.handleCommit(commit.getTransactionID(), getSender(), this)) { shardMBean.incrementFailedTransactionsCount(); } }
public int getCohortCacheSize() { return commitCoordinator.getCohortCacheSize(); }
public int getPendingTxCommitQueueSize() { return commitCoordinator.getQueueSize(); }
@Override public void onReceiveCommand(final Object message) throws Exception { MessageTracker.Context context = appendEntriesReplyTracker.received(message); if (context.error().isPresent()) { LOG.trace( "{} : AppendEntriesReply failed to arrive at the expected interval {}", persistenceId(), context.error()); } try { if (CreateTransaction.SERIALIZABLE_CLASS.isInstance(message)) { handleCreateTransaction(message); } else if (BatchedModifications.class.isInstance(message)) { handleBatchedModifications((BatchedModifications) message); } else if (message instanceof ForwardedReadyTransaction) { handleForwardedReadyTransaction((ForwardedReadyTransaction) message); } else if (message instanceof ReadyLocalTransaction) { handleReadyLocalTransaction((ReadyLocalTransaction) message); } else if (CanCommitTransaction.SERIALIZABLE_CLASS.isInstance(message)) { handleCanCommitTransaction(CanCommitTransaction.fromSerializable(message)); } else if (CommitTransaction.SERIALIZABLE_CLASS.isInstance(message)) { handleCommitTransaction(CommitTransaction.fromSerializable(message)); } else if (AbortTransaction.SERIALIZABLE_CLASS.isInstance(message)) { handleAbortTransaction(AbortTransaction.fromSerializable(message)); } else if (CloseTransactionChain.SERIALIZABLE_CLASS.isInstance(message)) { closeTransactionChain(CloseTransactionChain.fromSerializable(message)); } else if (message instanceof RegisterChangeListener) { changeSupport.onMessage((RegisterChangeListener) message, isLeader(), hasLeader()); } else if (message instanceof RegisterDataTreeChangeListener) { treeChangeSupport.onMessage( (RegisterDataTreeChangeListener) message, isLeader(), hasLeader()); } else if (message instanceof UpdateSchemaContext) { updateSchemaContext((UpdateSchemaContext) message); } else if (message instanceof PeerAddressResolved) { PeerAddressResolved resolved = (PeerAddressResolved) message; setPeerAddress(resolved.getPeerId().toString(), resolved.getPeerAddress()); } else if (message.equals(TX_COMMIT_TIMEOUT_CHECK_MESSAGE)) { commitCoordinator.checkForExpiredTransactions(transactionCommitTimeout, this); } else if (message instanceof DatastoreContext) { onDatastoreContext((DatastoreContext) message); } else if (message instanceof RegisterRoleChangeListener) { roleChangeNotifier.get().forward(message, context()); } else if (message instanceof FollowerInitialSyncUpStatus) { shardMBean.setFollowerInitialSyncStatus( ((FollowerInitialSyncUpStatus) message).isInitialSyncDone()); context().parent().tell(message, self()); } else if (GET_SHARD_MBEAN_MESSAGE.equals(message)) { sender().tell(getShardMBean(), self()); } else if (message instanceof GetShardDataTree) { sender().tell(store.getDataTree(), self()); } else if (message instanceof ServerRemoved) { context().parent().forward(message, context()); } else if (ShardTransactionMessageRetrySupport.TIMER_MESSAGE_CLASS.isInstance(message)) { messageRetrySupport.onTimerMessage(message); } else { super.onReceiveCommand(message); } } finally { context.done(); } }