Created
March 21, 2018 16:22
-
-
Save archer884/482216b6d9b1e2d5a5ade8e824e18c6a to your computer and use it in GitHub Desktop.
Generic retry stuff.
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.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