public async Task <bool> FinishProcessing(string transactionId, ITransactionRecordContainer transaction) { await transaction.Load().ConfigureAwait(false); if (transaction.MessageId != transactionId) { return(false); } await FinishProcessing(transaction).ConfigureAwait(false); return(true); }
/// <summary> /// The algorithm stores metadata about messages that are going to be sent before the claim check entries are stored /// in order to prevent accumulation of garbage messages in the claim check. First, the IDs of messages that are about /// to be sent are stored in the transaction record with assigned attempt ID and incoming message ID. Then, the message /// bodies are actually uploaded to the claim check. Next, the transaction record is committed. /// /// At any point in time the OutgoingMessages collection of the transaction record may contain messages produced by /// processing different incoming messages and different attempts but they don't interfere with each other e.g. /// - Message A processing attempt 1 is started, resulting in message records stored as A-1-1 and A-1-2. Then it fails. /// - Message B processing attempt 1 is started, resulting in B-1-1 and B-1-2 /// - Message B processing attempt 1 is finished. No claim check message are removed. /// - Message A processing attempt 2 is started, resulting in message records stored as A-2-1. /// - Message A processing attempt 2 is finished. Messages A-1-1 and A-1-2 are deleted. /// </summary> public async Task <ProcessingResult <TResult> > Process <TResult>(string currentMessageId, ITransactionRecordContainer transaction, TContext context, Func <TContext, ITransactionContext, Task <ProcessingResult <TResult> > > invokeMessageHandlers) { await transaction.Load().ConfigureAwait(false); var previousTransactionId = transaction.MessageId; if (previousTransactionId != null) { log.Log($"Unfinished transaction {previousTransactionId} detected. Attempting to complete that transaction."); await FinishProcessing(transaction).ConfigureAwait(false); if (previousTransactionId == currentMessageId) { log.Log($"Duplicate message {currentMessageId} detected. Ignoring."); return(ProcessingResult <TResult> .Duplicate); } } var attemptId = Guid.NewGuid(); log.Log($"Beginning attempt {attemptId} to process message {currentMessageId}."); await transaction.BeginStateTransition().ConfigureAwait(false); var result = await invokeMessageHandlers(context, new TransactionContext(attemptId, transaction)).ConfigureAwait(false); if (result.IsDuplicate) { log.Log($"Duplicate message {currentMessageId} detected. Ignoring."); return(result); } log.Log($"Committing transaction for attempt {attemptId} message {currentMessageId}."); await transaction.CommitStateTransition(currentMessageId, attemptId).ConfigureAwait(false); await FinishProcessing(transaction).ConfigureAwait(false); return(result); }
public Task Load() { return(impl.Load()); }