private async Task BulkDispatchAsync(IEnumerable <Message> posts, CancellationToken cancellationToken) { //Chunk into Topics var messagesByTopic = posts.GroupBy(m => m.Header.Topic); foreach (var topicBatch in messagesByTopic) { var producer = ProducerRegistry.LookupByOrDefault(topicBatch.Key); if (producer is IAmABulkMessageProducerAsync bulkMessageProducer) { var messages = topicBatch.ToArray(); s_logger.LogInformation("Bulk Dispatching {NumberOfMessages} for Topic {TopicName}", messages.Length, topicBatch.Key); var dispatchesMessages = bulkMessageProducer.SendAsync(messages, cancellationToken); await foreach (var successfulMessage in dispatchesMessages.WithCancellation(cancellationToken)) { if (!(producer is ISupportPublishConfirmation)) { await RetryAsync(async ct => await AsyncOutbox.MarkDispatchedAsync(successfulMessage, DateTime.UtcNow, cancellationToken: cancellationToken), cancellationToken : cancellationToken); } } } else { throw new InvalidOperationException("No async bulk message producer defined."); } } }
internal async Task ClearOutboxAsync(IEnumerable <Guid> posts, bool continueOnCapturedContext = false, CancellationToken cancellationToken = default(CancellationToken)) { if (!HasAsyncOutbox()) { throw new InvalidOperationException("No async outbox defined."); } await _clearSemaphoreToken.WaitAsync(cancellationToken); try { foreach (var messageId in posts) { var message = await AsyncOutbox.GetAsync(messageId, OutboxTimeout, cancellationToken); if (message == null || message.Header.MessageType == MessageType.MT_NONE) { throw new NullReferenceException($"Message with Id {messageId} not found in the Outbox"); } await DispatchAsync(new[] { message }, continueOnCapturedContext, cancellationToken); } } finally { _clearSemaphoreToken.Release(); } CheckOutstandingMessages(); }
internal async Task AddToOutboxAsync <T>(T request, bool continueOnCapturedContext, CancellationToken cancellationToken, Message message, IAmABoxTransactionConnectionProvider overridingTransactionConnectionProvider = null) where T : class, IRequest { CheckOutboxOutstandingLimit(); var written = await RetryAsync(async ct => { await AsyncOutbox.AddAsync(message, OutboxTimeout, ct, overridingTransactionConnectionProvider).ConfigureAwait(continueOnCapturedContext); }, continueOnCapturedContext, cancellationToken).ConfigureAwait(continueOnCapturedContext); if (!written) { throw new ChannelFailureException($"Could not write request {request.Id} to the outbox"); } }
private async Task DispatchAsync(IEnumerable <Message> posts, bool continueOnCapturedContext, CancellationToken cancellationToken) { foreach (var message in posts) { s_logger.LogInformation("Decoupled invocation of message: Topic:{Topic} Id:{Id}", message.Header.Topic, message.Id.ToString()); var producer = ProducerRegistry.LookupByOrDefault(message.Header.Topic); if (producer is IAmAMessageProducerAsync producerAsync) { if (producer is ISupportPublishConfirmation) { //mark dispatch handled by a callback - set in constructor await RetryAsync( async ct => await producerAsync.SendAsync(message).ConfigureAwait(continueOnCapturedContext), continueOnCapturedContext, cancellationToken) .ConfigureAwait(continueOnCapturedContext); } else { var sent = await RetryAsync( async ct => await producerAsync.SendAsync(message).ConfigureAwait(continueOnCapturedContext), continueOnCapturedContext, cancellationToken) .ConfigureAwait(continueOnCapturedContext); if (sent) { await RetryAsync(async ct => await AsyncOutbox.MarkDispatchedAsync(message.Id, DateTime.UtcNow, cancellationToken: cancellationToken), cancellationToken : cancellationToken); } } } else { throw new InvalidOperationException("No async message producer defined."); } } }
private async Task BackgroundDispatchUsingAsync(int amountToClear, int minimumAge, bool useBulk) { if (await _backgroundClearSemaphoreToken.WaitAsync(TimeSpan.Zero)) { await _clearSemaphoreToken.WaitAsync(CancellationToken.None); try { var messages = await AsyncOutbox.OutstandingMessagesAsync(minimumAge, amountToClear); s_logger.LogInformation("Found {NumberOfMessages} to clear out of amount {AmountToClear}", messages.Count(), amountToClear); if (useBulk) { await BulkDispatchAsync(messages, CancellationToken.None); } else { await DispatchAsync(messages, false, CancellationToken.None); } s_logger.LogInformation("Messages have been cleared"); } catch (Exception e) { s_logger.LogError(e, "Error while dispatching from outbox"); } finally { _clearSemaphoreToken.Release(); _backgroundClearSemaphoreToken.Release(); } CheckOutstandingMessages(); } else { s_logger.LogInformation("Skipping dispatch of messages as another thread is running"); } }
internal bool ConfigureAsyncPublisherCallbackMaybe(IAmAMessageProducer producer) { if (producer is ISupportPublishConfirmation producerSync) { producerSync.OnMessagePublished += async delegate(bool success, Guid id) { if (success) { s_logger.LogInformation("Sent message: Id:{Id}", id.ToString()); if (AsyncOutbox != null) { await RetryAsync(async ct => await AsyncOutbox.MarkDispatchedAsync(id, DateTime.UtcNow)); } } }; return(true); } return(false); }