Last active
December 12, 2019 19:48
-
-
Save anderly/49f34162605d639a2a0c265bb5a0d00a to your computer and use it in GitHub Desktop.
MediatR Retry Pipeline Behavior
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
public class Handler : IRequestHandler<Query, Customer>, IRetryableRequest<Query, Customer> | |
{ | |
private readonly CustomerContext _db; | |
private readonly IConfigurationProvider _configuration; | |
// By implementing IRetryableRequest<TRequest, TResponse> I get the following | |
// properties to control how my "Handle" method will get retried. | |
public int RetryAttempts => 2; | |
public int RetryDelay => 500; | |
public bool RetryWithExponentialBackoff => true; | |
public int ExceptionsAllowedBeforeCircuitTrip => 2; | |
public Handler(CustomerContext db, IConfigurationProvider configuration) | |
{ | |
_db = db; | |
_configuration = configuration; | |
} | |
public async Task<Customer> Handle(Query message, CancellationToken token) | |
{ | |
return await _db.Customers.Where(c => c.Number == message.CustomerNumber) | |
.ProjectTo<Models.Customer>(_configuration) | |
.FirstOrDefaultAsync(token); | |
} | |
} |
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
public interface IRetryableRequest<TRequest, TResponse> where TRequest : IRequest<TResponse> | |
{ | |
int RetryAttempts => 1; | |
int RetryDelay => 250; | |
bool RetryWithExponentialBackoff => false; | |
int ExceptionsAllowedBeforeCircuitTrip => 1; | |
} |
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
public class RetryBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse> | |
{ | |
private readonly IEnumerable<IRetryableRequest<TRequest, TResponse>> _retryHandlers; | |
private readonly ILogger<RetryBehavior<TRequest, TResponse>> _logger; | |
public RetryBehavior(IEnumerable<IRetryableRequest<TRequest, TResponse>> retryHandlers, ILogger<RetryBehavior<TRequest, TResponse>> logger) | |
{ | |
_retryHandlers = retryHandlers; | |
_logger = logger; | |
} | |
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) | |
{ | |
var retryHandler = _retryHandlers.FirstOrDefault(); | |
if (retryHandler == null) | |
{ | |
// No retry handler found, continue through pipeline | |
return await next(); | |
} | |
var circuitBreaker = Policy<TResponse> | |
.Handle<Exception>() | |
.CircuitBreakerAsync(retryHandler.ExceptionsAllowedBeforeCircuitTrip, TimeSpan.FromMilliseconds(5000), | |
(exception, things) => | |
{ | |
_logger.LogDebug("Circuit Tripped!"); | |
}, | |
() => | |
{ | |
}); | |
var retryPolicy = Policy<TResponse> | |
.Handle<Exception>() | |
.WaitAndRetryAsync(retryHandler.RetryAttempts, retryAttempt => | |
{ | |
var retryDelay = retryHandler.RetryWithExponentialBackoff | |
? TimeSpan.FromMilliseconds(Math.Pow(2, retryAttempt) * retryHandler.RetryDelay) | |
: TimeSpan.FromMilliseconds(_handler.RetryDelay); | |
_logger.LogDebug($"Retrying, waiting {retryDelay}..."); | |
return retryDelay; | |
}); | |
var response = await retryPolicy.ExecuteAsync(async () => await next()); | |
return response; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment