Skip to content

Instantly share code, notes, and snippets.

@Qrtic
Created August 22, 2021 21:07
Show Gist options
  • Save Qrtic/72d7e66a4fe38004526aed6615ad4c96 to your computer and use it in GitHub Desktop.
Save Qrtic/72d7e66a4fe38004526aed6615ad4c96 to your computer and use it in GitHub Desktop.
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(
message.SystemProperties.LockToken,
"Exceed retry attempts.");
return;
}
// 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(
messageCopy,
DateTimeOffset.UtcNow + GetDelay(attempt));
await _queueClientImplementation.CompleteAsync(message.SystemProperties.LockToken);
scope.Complete();
}
}
private bool CanDelay(int attempt) => attempt <= MaxRetryAttempts;
private TimeSpan GetDelay(int attempt)
{
return TimeSpan.FromMilliseconds(
UniformRandom(_initialDelay.TotalMilliseconds,
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