Skip to content

Instantly share code, notes, and snippets.

Created August 22, 2021 21:07
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
What would you like to do?
public class RescheduleStrategy
private const string RetryAttemptPropertyName = "retry-attempt";
private const int MaxRetryAttempts = 5;
private const int Exponent = 2;
private readonly Random _random = new Random();
private readonly IQueueClient _queueClientImplementation;
private readonly TimeSpan _initialDelay = TimeSpan.FromSeconds(5);
public async Task ScheduleRetryAsync(Message message)
int attempt = message.UserProperties.ContainsKey(RetryAttemptPropertyName)
? (int)message.UserProperties[RetryAttemptPropertyName] + 1
: 1;
if (!CanDelay(attempt))
await _queueClientImplementation.DeadLetterAsync(
"Exceed retry attempts.");
// Clone message to copy properties and content
Message messageCopy = message.Clone();
// Have to set new id if message duplicate detection is turned on
// Otherwise message will be ignored
messageCopy.MessageId = Guid.NewGuid().ToString();
// Enhance message with new retry attempt
message.UserProperties[RetryAttemptPropertyName] = attempt;
// It's preferred to use transaction scope for rescheduling
// to perform both scheduling and completion of the initial message simultaneously
// However it's only possible if the same queue is used
using (var scope = new TransactionScope())
await _queueClientImplementation.ScheduleMessageAsync(
DateTimeOffset.UtcNow + GetDelay(attempt));
await _queueClientImplementation.CompleteAsync(message.SystemProperties.LockToken);
private bool CanDelay(int attempt) => attempt <= MaxRetryAttempts;
private TimeSpan GetDelay(int attempt)
return TimeSpan.FromMilliseconds(
Math.Pow(Exponent, attempt)));
private double UniformRandom(double a, double b)
if (a == b) return a;
return a + (b - a) * _random.NextDouble();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment