Skip to content

Instantly share code, notes, and snippets.

@alastairtree
Last active November 30, 2023 13:51
Show Gist options
  • Save alastairtree/f83908705768a62559feae286543b4f9 to your computer and use it in GitHub Desktop.
Save alastairtree/f83908705768a62559feae286543b4f9 to your computer and use it in GitHub Desktop.
using System;
using System.Configuration;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using log4net;
namespace Utils
{
public static class RetryHelper
{
//I am using log4net but swap in your favourite
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static async Task RetryOnExceptionAsync(
int times, TimeSpan delay, Func<Task> operation)
{
await RetryOnExceptionAsync<Exception>(times, delay, operation);
}
public static async Task RetryOnExceptionAsync<TException>(
int times, TimeSpan delay, Func<Task> operation) where TException : Exception
{
if (times <= 0)
throw new ArgumentOutOfRangeException(nameof(times));
var attempts = 0;
do
{
try
{
attempts++;
await operation();
break;
}
catch (TException ex)
{
if (attempts == times)
throw;
await CreateDelayForException(times, attempts, delay, ex);
}
} while (true);
}
private static Task CreateDelayForException(
int times, int attempts, TimeSpan delay, Exception ex)
{
var delay = IncreasingDelayInSeconds(attempts);
Log.Warn($"Exception on attempt {attempts} of {times}. " +
"Will retry after sleeping for {delay}.", ex);
return Task.Delay(delay);
}
internal static int[] DelayPerAttemptInSeconds =
{
(int) TimeSpan.FromSeconds(2).TotalSeconds,
(int) TimeSpan.FromSeconds(30).TotalSeconds,
(int) TimeSpan.FromMinutes(2).TotalSeconds,
(int) TimeSpan.FromMinutes(10).TotalSeconds,
(int) TimeSpan.FromMinutes(30).TotalSeconds
};
static int IncreasingDelayInSeconds(int failedAttempts)
{
if (failedAttempts <= 0) throw new ArgumentOutOfRangeException();
return failedAttempts > DelayPerAttemptInSeconds.Length ? DelayPerAttemptInSeconds.Last() : DelayPerAttemptInSeconds[failedAttempts];
}
}
}
@alastairtree
Copy link
Author

@RichardD2 - yes you could, nice!

@cconway25 - i have added the implementation, must have forgotten to copy that bit.

@CNBoland
Copy link

Variable 'delay' is declared twice in CreateDelayForException(...). Is 'delay' parameter from RetryOnExceptionAsync(...) and passed down even needed since delay is calculated from failed attempts?

@dust63
Copy link

dust63 commented Dec 16, 2019

Agree with @CNBoland

You can use uint for the times parameter to remove
if (times <= 0) throw new ArgumentOutOfRangeException(nameof(times));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment