Skip to content

Instantly share code, notes, and snippets.

@jrgcubano
Last active August 29, 2018 14:42
Show Gist options
  • Select an option

  • Save jrgcubano/de375db2154f094d04339c09641013eb to your computer and use it in GitHub Desktop.

Select an option

Save jrgcubano/de375db2154f094d04339c09641013eb to your computer and use it in GitHub Desktop.
Retry and circuit breaker pattern in C# (services, httpclient, polly)
https://github.com/App-vNext/Polly/wiki/Circuit-Breaker
CircuitBreakerPolicy breaker = Policy
.Handle<HttpException>()
.CircuitBreaker(
exceptionsAllowedBeforeBreaking: 2,
durationOfBreak: TimeSpan.FromMinutes(1)
);
// Via https://alastaircrabtree.com/implementing-the-retry-pattern-for-async-tasks-in-c/
// Use
var maxRetryAttempts = 3;
var pauseBetweenFailures = TimeSpan.FromSeconds(2);
await RetryHelper.RetryOnExceptionAsync<HttpRequestException>
(maxRetryAttempts, pauseBetweenFailures, async () => {
var response = await httpClient.DeleteAsync(
"https://example.com/api/products/1");
response.EnsureSuccessStatusCode();
});
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];
}
}
}
// Example 1:
var httpClient = new HttpClient();
var maxRetryAttempts = 3;
var pauseBetweenFailures = TimeSpan.FromSeconds(2);
var retryPolicy = Policy
.Handle<HttpRequestException>()
.WaitAndRetryAsync(maxRetryAttempts, i => pauseBetweenFailures);
await retryPolicy.ExecuteAsync(async () =>
{
var response = await httpClient
.DeleteAsync("https://example.com/api/products/1");
response.EnsureSuccessStatusCode();
});
// Example 2: (increase delay on each retry)
Policy
.Handle<HttpRequestException>()
.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(4),
TimeSpan.FromSeconds(8)
});
// Example 3 (calculate the delay at run-time with retry attemp)
Policy
.Handle<HttpRequestException>()
.WaitAndRetryAsync(3, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
);
// Use
var maxRetryAttempts = 3;
var pauseBetweenFailures = TimeSpan.FromSeconds(2);
RetryHelper.RetryOnException(maxRetryAttempts, pauseBetweenFailures, () => {
product = httpClient.Get("https://example.com/api/products/1");
});
public static class RetryHelper
{
private static ILog logger = LogManager.GetLogger(); //use a logger or trace of your choice
public static void RetryOnException(int times, TimeSpan delay, Action operation)
{
var attempts = 0;
do
{
try
{
attempts++;
operation();
break; // Sucess! Lets exit the loop!
}
catch (Exception ex)
{
if (attempts == times)
throw;
logger.Error($"Exception caught on attempt {attempts} - will retry after delay {delay}", ex);
Task.Delay(delay).Wait();
}
} while (true);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment