CircuitStateController<TResult>
using Polly.Utilities;
using System;
namespace Polly.CircuitBreaker
{
internal abstract class CircuitStateController<TResult> : ICircuitController<TResult>
{
protected readonly TimeSpan _durationOfBreak;
protected DateTime _blockedTill;
protected CircuitState _circuitState;
protected DelegateResult<TResult> _lastOutcome;
protected readonly Action<DelegateResult<TResult>, TimeSpan, Context> _onBreak;
protected readonly Action<Context> _onReset;
protected readonly Action _onHalfOpen;
protected readonly object _lock = new object();
public CircuitState CircuitState {
get {
if (_circuitState != CircuitState.Open)
return _circuitState;
using (TimedLock.Lock(_lock)) {
if (_circuitState == CircuitState.Open && !IsInAutomatedBreak_NeedsLock) {
_circuitState = CircuitState.HalfOpen;
_onHalfOpen();
}
return _circuitState;
}
}
}
public Exception LastException {
get {
using (TimedLock.Lock(_lock))
return _lastOutcome.Exception;
}
}
public TResult LastHandledResult {
get {
using (TimedLock.Lock(_lock))
return _lastOutcome.Result;
}
}
protected bool IsInAutomatedBreak_NeedsLock => SystemClock.UtcNow() < _blockedTill;
protected CircuitStateController(TimeSpan durationOfBreak, Action<DelegateResult<TResult>, TimeSpan, Context> onBreak, Action<Context> onReset, Action onHalfOpen)
{
_durationOfBreak = durationOfBreak;
_onBreak = onBreak;
_onReset = onReset;
_onHalfOpen = onHalfOpen;
_circuitState = CircuitState.Closed;
Reset();
}
public void Isolate()
{
using (TimedLock.Lock(_lock)) {
_lastOutcome = new DelegateResult<TResult>(new IsolatedCircuitException("The circuit is manually held open and is not allowing calls."));
BreakFor_NeedsLock(TimeSpan.MaxValue, Context.None);
_circuitState = CircuitState.Isolated;
}
}
protected void Break_NeedsLock(Context context)
{
BreakFor_NeedsLock(_durationOfBreak, context);
}
private void BreakFor_NeedsLock(TimeSpan durationOfBreak, Context context)
{
_blockedTill = ((durationOfBreak > DateTime.MaxValue - SystemClock.UtcNow()) ? DateTime.MaxValue : (SystemClock.UtcNow() + durationOfBreak));
_circuitState = CircuitState.Open;
_onBreak(_lastOutcome, durationOfBreak, context);
}
public void Reset()
{
OnCircuitReset(Context.None);
}
protected void ResetInternal_NeedsLock(Context context)
{
_blockedTill = DateTime.MinValue;
_lastOutcome = new DelegateResult<TResult>(new InvalidOperationException("This exception should never be thrown"));
CircuitState circuitState = _circuitState;
_circuitState = CircuitState.Closed;
if (circuitState != 0)
_onReset(context);
}
public void OnActionPreExecute()
{
switch (CircuitState) {
case CircuitState.Closed:
case CircuitState.HalfOpen:
break;
case CircuitState.Open:
throw (_lastOutcome.Exception != null) ? new BrokenCircuitException("The circuit is now open and is not allowing calls.", _lastOutcome.Exception) : new BrokenCircuitException<TResult>("The circuit is now open and is not allowing calls.", _lastOutcome.Result);
case CircuitState.Isolated:
throw new IsolatedCircuitException("The circuit is manually held open and is not allowing calls.");
default:
throw new InvalidOperationException("Unhandled CircuitState.");
}
}
public abstract void OnActionSuccess(Context context);
public abstract void OnActionFailure(DelegateResult<TResult> outcome, Context context);
public abstract void OnCircuitReset(Context context);
}
}