Skip to content

Instantly share code, notes, and snippets.

@archer884
Created March 21, 2018 16:22
Show Gist options
  • Save archer884/482216b6d9b1e2d5a5ade8e824e18c6a to your computer and use it in GitHub Desktop.
Save archer884/482216b6d9b1e2d5a5ade8e824e18c6a to your computer and use it in GitHub Desktop.
Generic retry stuff.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace retry
{
public static class Execute
{
static Executer _executer;
static Execute()
{
_executer = new Executer();
}
public static void WithRetry(Action action)
{
_executer.Run(action);
}
public static T WithRetry<T>(Func<T> function)
{
return _executer.Run(function);
}
}
public class RetryFailedException : Exception
{
public RetryFailedException(Exception e)
: base("Retry failed", e)
{
}
}
public class Executer
{
IRetryScheduleProvider _retryScheduleProvider;
IFalloff _falloff;
public Executer()
: this(new RetryScheduleProvider(), new ExponentialFalloff(TimeSpan.FromSeconds(3)))
{
}
public Executer(IRetryScheduleProvider retryScheduleProvider, IFalloff falloff)
{
_retryScheduleProvider = retryScheduleProvider;
_falloff = falloff;
}
public void Run(Action action)
{
var scheduler = _retryScheduleProvider.GetScheduler(_falloff);
while (true)
{
try
{
action();
}
catch (Exception e)
{
if (scheduler.ShouldRetry)
{
scheduler.Wait();
continue;
}
throw new RetryFailedException(e);
}
}
}
public async Task RunAsync(Func<Task> taskProvider)
{
var scheduler = _retryScheduleProvider.GetScheduler(_falloff);
while (true)
{
try
{
await taskProvider();
}
catch (Exception e)
{
if (scheduler.ShouldRetry)
{
await scheduler.WaitAsync();
continue;
}
throw new RetryFailedException(e);
}
}
}
public T Run<T>(Func<T> function)
{
var scheduler = _retryScheduleProvider.GetScheduler(_falloff);
while (true)
{
try
{
return function();
}
catch (Exception e)
{
if (scheduler.ShouldRetry)
{
scheduler.Wait();
continue;
}
throw new RetryFailedException(e);
}
}
}
public async Task<T> RunAsync<T>(Func<Task<T>> taskProvider)
{
var scheduler = _retryScheduleProvider.GetScheduler(_falloff);
while (true)
{
try
{
return await taskProvider();
}
catch (Exception e)
{
if (scheduler.ShouldRetry)
{
await scheduler.WaitAsync();
continue;
}
throw new RetryFailedException(e);
}
}
}
}
public interface IRetryScheduleProvider
{
IRetryScheduler GetScheduler(IFalloff falloff);
}
public class RetryScheduleProvider : IRetryScheduleProvider
{
Func<IFalloff, IRetryScheduler> _factory;
public RetryScheduleProvider()
: this(falloff => new NTimesScheduler(4, falloff))
{
}
public RetryScheduleProvider(Func<IFalloff, IRetryScheduler> factory)
{
_factory = factory;
}
public IRetryScheduler GetScheduler(IFalloff falloff)
{
return _factory(falloff);
}
}
public interface IRetryScheduler
{
bool ShouldRetry { get; }
void Wait();
Task WaitAsync();
}
public class NTimesScheduler : IRetryScheduler
{
IFalloff _falloff;
int _times;
int _current;
public NTimesScheduler(int times, IFalloff falloff)
{
_falloff = falloff;
_times = times;
_current = 0;
}
public bool ShouldRetry => _times > _current;
public void Wait()
{
_current++;
Thread.Sleep(_falloff.GetFalloff(_current));
}
public Task WaitAsync()
{
_current++;
return Task.Delay(_falloff.GetFalloff(_current));
}
}
public interface IFalloff
{
TimeSpan GetFalloff(int attempt);
}
public class ExponentialFalloff : IFalloff
{
TimeSpan _wait;
public ExponentialFalloff(TimeSpan wait)
{
_wait = wait;
}
public TimeSpan GetFalloff(int attempt)
{
return TimeSpan.FromSeconds(Math.Pow(_wait.Seconds, attempt));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment