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

CircuitStateController

using Polly.Utilities; using System; namespace Polly.CircuitBreaker { internal abstract class CircuitStateController : ICircuitController { protected readonly TimeSpan _durationOfBreak; protected DateTime _blockedTill; protected CircuitState _circuitState; protected Exception _lastException; protected readonly Action<Exception, TimeSpan, Context> _onBreak; protected readonly Action<Context> _onReset; protected readonly Action _onHalfOpen; protected readonly object _lock = new object(); public CircuitState CircuitState { get { 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 _lastException; } } protected bool IsInAutomatedBreak_NeedsLock => SystemClock.UtcNow() < _blockedTill; protected CircuitStateController(TimeSpan durationOfBreak, Action<Exception, 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)) { _lastException = new IsolatedCircuitException("The circuit is manually held open and is not allowing calls."); BreakFor_NeedsLock(TimeSpan.MaxValue, Context.Empty); _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(_lastException, durationOfBreak, context ?? Context.Empty); } public void Reset() { OnCircuitReset(Context.Empty); } protected void ResetInternal_NeedsLock(Context context) { _blockedTill = DateTime.MinValue; _lastException = new InvalidOperationException("This exception should never be thrown"); CircuitState circuitState = _circuitState; _circuitState = CircuitState.Closed; if (circuitState != 0) _onReset(context ?? Context.Empty); } public void OnActionPreExecute() { using (TimedLock.Lock(_lock)) { switch (CircuitState) { case CircuitState.Closed: case CircuitState.HalfOpen: break; case CircuitState.Open: throw new BrokenCircuitException("The circuit is now open and is not allowing calls.", _lastException); 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(Exception ex, Context context); public abstract void OnCircuitReset(Context context); } }