I was used to WaitAndRetryForeverAsync
in the past which was wrong because I believe the Retry pattern is supposed to handle only transient faults, such as rate limiting, 429 status code, etc. At the moment that the API I was subscribing to went offline for service maintenance which took about 25 minutes, WaitAndRetryForeverAsync
was retrying forever in a constant interval (not exponential which doesn't really matter in this case) which in fact triggered some firewall rules on the API side and my IP was blocked for a while.
I'm trying to do what Nick Chapsas says in his Circuit Breaker video, i.e. if it fails to retry 5 times -> we make the assumption that the service is in maintenance. So enable the retries after 30 minutes and so on and so forth until it reconnects, even if it takes hours to do (depending on how long the service maintenance is).
The question is how do I achieve that circuit breaker policy after WaitAndRetry
's failure?
/// <summary>
/// This class provides Transient Fault Handling extension methods.
/// </summary>
internal static class Retry
{
public static void Do(Action action, TimeSpan retryInterval, int retryCount = 3)
{
_ = Do<object?>(() =>
{
action();
return null;
}, retryInterval, retryCount);
}
public static async Task DoAsync(Func<Task> action, TimeSpan retryInterval, int retryCount = 3)
{
_ = await DoAsync<object?>(async () =>
{
await action();
return null;
}, retryInterval, retryCount);
}
public static T Do<T>(Func<T> action, TimeSpan retryWait, int retryCount = 3)
{
var policyResult = Policy
.Handle<Exception>()
.WaitAndRetry(retryCount, retryAttempt => retryWait)
.ExecuteAndCapture(action);
if (policyResult.Outcome == OutcomeType.Failure)
{
throw policyResult.FinalException;
}
return policyResult.Result;
}
public static async Task<T> DoAsync<T>(Func<Task<T>> action, TimeSpan retryWait, int retryCount = 3)
{
var policyResult = await Policy
.Handle<Exception>()
.WaitAndRetryAsync(retryCount, retryAttempt => retryWait)
.ExecuteAndCaptureAsync(action);
if (policyResult.Outcome == OutcomeType.Failure)
{
throw policyResult.FinalException;
}
return policyResult.Result;
}
}