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]; | |
} | |
} | |
} |
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
Variable 'delay' is declared twice in CreateDelayForException(...). Is 'delay' parameter from RetryOnExceptionAsync(...) and passed down even needed since delay is calculated from failed attempts?