-
Notifications
You must be signed in to change notification settings - Fork 10
2.12 Error handling
Error handling is the response and recovery procedures from error conditions present in a software application. In other words, it is the process comprised of anticipation, detection and resolution of application errors, programming errors or communication errors. Error handling helps in maintaining the normal flow of program execution.
Signals offers single implementation for the Error handling aspect out of the box:
- Signals.Aspects.ErrorHandling.Polly
Only one implementation can be active at a time.
When we implement any type of process an error handler is automatically executing the process and retrying it if it fails.
public class MyProcess : BusinessProcess<VoidResult>
{
/// <summary>
/// Authenticate process
/// </summary>
/// <returns></returns>
public override VoidResult Auth()
{
return Ok();
}
/// <summary>
/// Validate process
/// </summary>
/// <returns></returns>
public override VoidResult Validate()
{
return Ok();
}
/// <summary>
/// Handle process
/// </summary>
/// <returns></returns>
public override VoidResult Handle()
{
// the error handler will retry the process when it throws exception
// as per configured in the error strategy builder
throw new Exception();
}
}
Other more manual way of using the error handler is through the IStrategyHandler instance.
public class MyClass
{
public TResult ExecuteCodeThatNeedsRetryingIfFails<TResult>(Func<TResult> func)
{
var errorHandler = SystemBootstrapper.GetInstance<IStrategyHandler>();
return errorHandler.Execute(func);
}
}
We configure the error handling aspect by using an instance of Signals.Aspects.ErrorHandling.Polly.StrategyBuilder
which we pass in the ApplicationBootstrapConfiguration
instance (web or background) at startup.
-
IStrategyBuilder Add<TException>(Strategy strategy) where TException : Exception;
: Adds a strategy in the strategy builder -
IStrategyBuilder SetAutoHandling(bool shouldAutoHandle = true);
: set if all processes should be automatically retried on exception
class Strategy
-
OnRetry
: Action to execute when an Exception happens
class RetryStrategy
/// <summary>
/// Retry strategy
/// </summary>
public class RetryStrategy : Strategy
{
/// <summary>
/// Strategy that will retry execution forever
/// </summary>
public static readonly RetryStrategy Forever = new RetryStrategy { RetryCount = -1 };
/// <summary>
/// CTOR
/// </summary>
public RetryStrategy()
{
RetryCooldown = TimeSpan.FromSeconds(0);
}
/// <summary>
/// Number of times execution is retried
/// </summary>
public int RetryCount { get; set; }
/// <summary>
/// Period of time to wait between retries
/// </summary>
public TimeSpan RetryCooldown { get; set; }
}
class CircutBreakerStrategy
/// <summary>
/// Circut breaker strategy
/// </summary>
public class CircutBreakerStrategy : Strategy
{
/// <summary>
/// Number of exceptions that are allowed to happen before the strategy disallows to be executed again
/// </summary>
public int AllowedExceptionsCount { get; set; }
/// <summary>
/// Period of time for the circut to be broken after reaching maximum amount of exceptions
/// </summary>
public TimeSpan CooldownTimeout { get; set; }
/// <summary>
/// Callback for when the circut is reset
/// </summary>
public Action OnReset { get; set; }
/// <summary>
/// Handle for maually allowing execution
/// </summary>
public Action Reset { get; set; }
/// <summary>
/// Handle for manually disallowing execution
/// </summary>
public Action Isolate { get; set; }
}
Using Signals.Aspects.ErrorHandling.Polly
var strategyBuilder = new StrategyBuilder();
var retryStrategy = new RetryStrategy
{
RetryCount = 3,
RetryCooldown = TimeSpan.FromMinutes(5)
};
var circutBreakerStrategy = new CircutBreakerStrategy
{
AllowedExceptionsCount = 3,
CooldownTimeout = TimeSpan.FromMinutes(5)
};
strategyBuilder
.Add<Exception>(retryStrategy)
.Add<Exception>(circutBreakerStrategy)
.SetAutoHandling(false); // disable autohandling process errors
services
.AddSignals(config =>
{
config.StrategyBuilder = strategyBuilder;
});
- Install package Signals.Aspects.ErrorHandling
- Create class with implementation of
Signals.Aspects.ErrorHandling.IStrategyBuilder
/// <summary>
/// Error handling builder
/// </summary>
public class MyStrategyBuilder : IStrategyBuilder
{
/// <summary>
/// Add strategies
/// </summary>
/// <typeparam name="TException"></typeparam>
/// <param name="strategy"></param>
/// <returns></returns>
IStrategyBuilder Add<TException>(Strategy strategy) where TException : Exception;
/// <summary>
/// Should automatically use defined strategy on processes
/// </summary>
/// <param name="shouldAutoHandle"></param>
/// <returns></returns>
IStrategyBuilder SetAutoHandling(bool shouldAutoHandle = true);
/// <summary>
/// Builds handler out of strategies
/// </summary>
/// <returns></returns>
IStrategyHandler Build();
}
- Create class with implementation of
Signals.Aspects.ErrorHandling.IStrategyHandler
/// <summary>
/// Error handler
/// </summary>
public class MyStrategyHandler : IStrategyHandler
{
/// <summary>
/// Should automatically use defined strategy on processes
/// </summary>
bool AutoHandleErrorProcesses { get; }
/// <summary>
/// Execute callback wrapped in an execution strategy
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="action"></param>
/// <returns></returns>
Task<TResult> Execute<TResult>(Func<TResult> action);
}
- Use our implementation of
IStrategyBuilder
when configuring our application
public static IServiceProvider AddSignals(this IServiceCollection services)
{
services
.AddSignals(config =>
{
config.StrategyBuilder = new MyStrategyBuilder();
});
}