private async Task ReleaseMessage(Message msg, MessageReleaseAction releaseAction, Stopwatch roundtripStopwatch) { switch (releaseAction.Kind) { case MessageReleaseActionKind.Complete: await msg.SafeCompleteAsync( this.subscription, client, success => { if (success) { this.dynamicThrottling.NotifyWorkCompleted(); } else { this.dynamicThrottling.NotifyWorkCompletedWithError(); } }, logger, roundtripStopwatch); break; case MessageReleaseActionKind.Abandon: await msg.SafeAbandonAsync( this.subscription, client, success => { dynamicThrottling.NotifyWorkCompletedWithError(); }, logger, roundtripStopwatch); break; case MessageReleaseActionKind.DeadLetter: await msg.SafeDeadLetterAsync( this.subscription, client, releaseAction.DeadLetterReason, releaseAction.DeadLetterDescription, success => { this.dynamicThrottling.NotifyWorkCompletedWithError(); }, logger, roundtripStopwatch); break; default: break; } }
/* * /// <summary> * /// Receives the messages in an asynchronous loop and closes the session once there are no more messages. * /// </summary> * private async Task ReceiveMessagesAndCloseSession(IMessageSession session, Message message, CancellationToken cancellationToken) * { * CountdownEvent unreleasedMessages = new CountdownEvent(1); * * Func<bool, Task> closeSession = async (bool success) => * { * Func<Task> doClose = () => * { * try * { * unreleasedMessages.Signal(); * if (!unreleasedMessages.Wait(15000, cancellationToken)) * { * logger.LogWarning("Waited for pending unreleased messages before closing session in subscription {0} but they did not complete in time", this.subscription); * } * } * catch (OperationCanceledException) * { * } * finally * { * unreleasedMessages.Dispose(); * } * * return receiveRetryPolicy.ExecuteAsync( * async () => * { * try { * await session.CloseAsync(); * * if (success) * { * dynamicThrottling.NotifyWorkCompleted(); * } * else * { * dynamicThrottling.NotifyWorkCompletedWithError(); * } * * return Task.CompletedTask; * } * catch (Exception ex) * { * logger.LogError("An unrecoverable error occurred while trying to close a session in subscription {1}:\r\n{0}", ex, this.subscription); * dynamicThrottling.NotifyWorkCompletedWithError(); * return Task.CompletedTask; * } * }); * }; * * if (this.requiresSequentialProcessing) * { * await doClose(); * } * else * { * // Allow some time for releasing the messages before closing. Also, continue in a non I/O completion thread in order to block. * await Task.Delay(200).ContinueWith(t => doClose()); * } * }; * * // Declare an action to receive the next message in the queue or closes the session if cancelled. * Func<Message, Task> receiveNext = null; * * // Declare an action acting as a callback whenever a non-transient exception occurs while receiving or processing messages. * Func<Exception, Task> recoverReceive = null; * * // Declare an action responsible for the core operations in the message receive loop. * Func<Message, Task> receiveMessage = ((msg) => * { * // Use a retry policy to execute the Receive action in an asynchronous and reliable fashion. * //await this.receiveRetryPolicy.ExecuteAsync * //( * //() => * //{ * try * { * // Start receiving a new message asynchronously. * // Does not wait for new messages to arrive in a session. If no further messages we will just close the session. * //var msg = await session.ReceiveAsync(TimeSpan.Zero); * * // Process the message once it was successfully received * // Check if we actually received any messages. * if (msg != null) * { * var roundtripStopwatch = Stopwatch.StartNew(); * * unreleasedMessages.AddCount(); * * //return Task.Run(async () => * //{ * var releaseAction = MessageReleaseAction.AbandonMessage; * Task releaseTask = null; * try * { * * // Make sure the process was told to stop receiving while it was waiting for a new message. * if (!cancellationToken.IsCancellationRequested) * { * try * { * releaseAction = this.InvokeMessageHandler(msg); * } * catch * { * throw; * } * finally * { * if (roundtripStopwatch.Elapsed > TimeSpan.FromSeconds(45)) * { * this.dynamicThrottling.Penalize(); * } * } * } * } * finally * { * // Ensure that any resources allocated by a BrokeredMessage instance are released. * if (this.requiresSequentialProcessing) * { * //releaseTask = session.CompleteAsync(msg.SystemProperties.LockToken); * * releaseTask = this.ReleaseMessage(session, msg, releaseAction, * async () => { await receiveNext(msg); }, * async () => { await closeSession(false); }, * unreleasedMessages, roundtripStopwatch); * } * else * { * // Receives next without waiting for the message to be released. * releaseTask = this.ReleaseMessage(session, msg, releaseAction, * () => { }, * () => { this.dynamicThrottling.Penalize(); }, * unreleasedMessages, roundtripStopwatch); * } * } * * return releaseTask; * //}); * } * else * { * // no more messages in the session, close it and do not continue receiving * return closeSession(true); * } * } * catch (Exception ex) * { * // Invoke a custom action to indicate that we have encountered an exception and * // need further decision as to whether to continue receiving messages. * return recoverReceive(ex); * } * //}); * }); * * // Initialize an action to receive the next message in the queue or closes the session if cancelled. * receiveNext = async (msg) => * { * if (!cancellationToken.IsCancellationRequested) * { * // Continue receiving and processing new messages until told to stop. * await receiveMessage(msg); * } * else * { * await closeSession(true); * } * }; * * // Initialize a custom action acting as a callback whenever a non-transient exception occurs while receiving or processing messages. * recoverReceive = async (ex) => * { * // Just log an exception. Do not allow an unhandled exception to terminate the message receive loop abnormally. * logger.LogError("An unrecoverable error occurred while trying to receive a new message from subscription {1}:\r\n{0}", ex, this.subscription); * * // Cannot continue to receive messages from this session. * await closeSession(false); * }; * * // Start receiving messages asynchronously for the session. * await receiveNext(message); * } */ private async Task ReleaseMessage(IMessageSession session, Message msg, MessageReleaseAction releaseAction, Action completeReceive, Action onReleaseError, Stopwatch roundtripStopwatch) { switch (releaseAction.Kind) { case MessageReleaseActionKind.Complete: await msg.SafeCompleteAsync( this.subscription, session, operationSucceeded => { if (operationSucceeded) { completeReceive(); } else { onReleaseError(); } }, logger, roundtripStopwatch); break; case MessageReleaseActionKind.Abandon: this.dynamicThrottling.Penalize(); await msg.SafeAbandonAsync( this.subscription, session, succeeded => onReleaseError(), logger, roundtripStopwatch); break; case MessageReleaseActionKind.DeadLetter: this.dynamicThrottling.Penalize(); await msg.SafeDeadLetterAsync( this.subscription, session, releaseAction.DeadLetterReason, releaseAction.DeadLetterDescription, operationSucceeded => { if (operationSucceeded) { completeReceive(); } else { onReleaseError(); } }, logger, roundtripStopwatch); break; default: break; } }