async Task RouteBatchWithEnforcedBatchSizeAsync(IMessageSender messageSender, IEnumerable <BrokeredMessage> messagesToSend) { var chunk = new List <BrokeredMessage>(); long batchSize = 0; var chunkNumber = 1; foreach (var message in messagesToSend) { if (await GuardMessageSizeAsync(message).ConfigureAwait(false)) { return; } var messageSize = message.EstimatedSize(); if (batchSize + messageSize > maximuMessageSizeInKilobytes * 1024) { if (chunk.Any()) { logger.Debug($"Routing batched messages, chunk #{chunkNumber++}."); var currentChunk = chunk; await messageSender.RetryOnThrottleAsync(s => s.SendBatch(currentChunk), s => s.SendBatch(currentChunk.Select(x => x.Clone())), backOffTimeOnThrottle, maxRetryAttemptsOnThrottle).ConfigureAwait(false); } chunk = new List <BrokeredMessage> { message }; batchSize = messageSize; } else { chunk.Add(message); batchSize += messageSize; } } if (chunk.Any()) { logger.Debug($"Routing batched messages, chunk #{chunkNumber}."); await messageSender.RetryOnThrottleAsync(s => s.SendBatch(chunk), s => s.SendBatch(chunk.Select(x => x.Clone())), backOffTimeOnThrottle, maxRetryAttemptsOnThrottle).ConfigureAwait(false); } }
public static async Task RetryOnThrottleAsync(this IMessageSender sender, Func <IMessageSender, Task> action, Func <IMessageSender, Task> retryAction, TimeSpan delay, int maxRetryAttempts, int retryAttempts = 0) { try { // upon retries have to use new BrokeredMessage instances var actionToTake = retryAttempts == 0 ? action : retryAction; await actionToTake(sender).ConfigureAwait(false); } catch (ServerBusyException) { if (retryAttempts < maxRetryAttempts) { logger.Warn($"We are throttled, backing off for {delay.TotalSeconds} seconds (attempt {retryAttempts + 1}/{maxRetryAttempts})."); await Task.Delay(delay).ConfigureAwait(false); await sender.RetryOnThrottleAsync(action, retryAction, delay, maxRetryAttempts, ++retryAttempts).ConfigureAwait(false); } else { throw; } } }