<PackageReference Include="Polly" Version="5.0.3" />

CircuitStateController<TResult>

abstract class CircuitStateController<TResult> : ICircuitController<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); } }