Last active
November 30, 2023 13:51
-
-
Save alastairtree/f83908705768a62559feae286543b4f9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]; | |
} | |
} | |
} |
What is your implementation of IncreasingDelayInSeconds(attempts)? Or is that just meant to be pseudo-code?
@RichardD2 - yes you could, nice!
@cconway25 - i have added the implementation, must have forgotten to copy that bit.
Variable 'delay' is declared twice in CreateDelayForException(...). Is 'delay' parameter from RetryOnExceptionAsync(...) and passed down even needed since delay is calculated from failed attempts?
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
Since you're using C# 6, you could use an exception filter: