private async Task SendOutgoingMessages(ConcurrentQueue <AmazonOutgoingMessage> outgoingMessages, ITransactionContext context) { if (!outgoingMessages.Any()) { return; } var client = _amazonInternalSettings.CreateSqsClient(context); var messagesByDestination = outgoingMessages.GroupBy(m => m.DestinationAddress).ToList(); await Task.WhenAll(messagesByDestination.Select(async batch => { var entries = batch.Select(message => { var transportMessage = message.TransportMessage; var headers = transportMessage.Headers; var messageId = headers[Headers.MessageId]; var sqsMessage = new AmazonTransportMessage(transportMessage.Headers, StringHelper.GetBody(transportMessage.Body)); var entry = new SendMessageBatchRequestEntry(messageId, _amazonInternalSettings.MessageSerializer.Serialize(sqsMessage)); var delaySeconds = GetDelaySeconds(headers); if (delaySeconds != null) { entry.DelaySeconds = delaySeconds.Value; } return(entry); }).ToList(); var destinationUrl = _amazonSqsQueueContext.GetDestinationQueueUrlByName(batch.Key, context); foreach (var batchToSend in entries.Batch(10)) { var request = new SendMessageBatchRequest(destinationUrl, batchToSend); var response = await client.SendMessageBatchAsync(request); if (response.Failed.Count == 0) { continue; } var failed = response.Failed.Select(f => new AmazonSQSException($"Failed {f.Message} with Id={f.Id}, Code={f.Code}, SenderFault={f.SenderFault}")); throw new AggregateException(failed); } })); }
/// <inheritdoc /> public async Task <TransportMessage> Receive(ITransactionContext context, string address, CancellationToken cancellationToken) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (address == null) { throw new InvalidOperationException("This Amazon SQS transport does not have an input queue, hence it is not possible to receive anything"); } var queueUrl = m_amazonSqsQueueContext.GetDestinationQueueUrlByName(address, context); if (string.IsNullOrWhiteSpace(queueUrl)) { throw new InvalidOperationException("The queue URL is empty - has the transport not been initialized?"); } var client = m_amazonInternalSettings.CreateSqsClient(context); var request = new ReceiveMessageRequest(queueUrl) { MaxNumberOfMessages = 1, WaitTimeSeconds = m_amazonInternalSettings.AmazonSnsAndSqsTransportOptions.ReceiveWaitTimeSeconds, AttributeNames = new List <string>(new[] { "All" }), MessageAttributeNames = new List <string>(new[] { "All" }) }; var response = await client.ReceiveMessageAsync(request, cancellationToken); if (response.Messages.Any() == false) { return(null); } var sqsMessage = response.Messages.First(); var renewalTask = CreateRenewalTaskForMessage(sqsMessage, queueUrl, client); context.OnCompleted(async() => { renewalTask.Dispose(); // if we get this far, we don't want to pass on the cancellation token // ReSharper disable once MethodSupportsCancellation await client.DeleteMessageAsync(new DeleteMessageRequest(queueUrl, sqsMessage.ReceiptHandle)); }); context.OnAborted(() => { renewalTask.Dispose(); Task.Run(() => client.ChangeMessageVisibilityAsync(queueUrl, sqsMessage.ReceiptHandle, 0, cancellationToken), cancellationToken).Wait(cancellationToken); }); IAmazonMessageProcessor amazonMessageProcessor = _amazonMessageProcessorFactory.Create(sqsMessage); var transportMessage = amazonMessageProcessor.ProcessMessage(); if (transportMessage.MessageIsExpired(sqsMessage)) { // if the message is expired , we don't want to pass on the cancellation token // ReSharper disable once MethodSupportsCancellation await client.DeleteMessageAsync(new DeleteMessageRequest(queueUrl, sqsMessage.ReceiptHandle)); return(null); } renewalTask.Start(); return(transportMessage); }