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

CircuitStateController<TResult>

abstract class CircuitStateController<TResult> : ICircuitController<TResult>
using Polly.Utilities; using System; using System.Threading; namespace Polly.CircuitBreaker { internal abstract class CircuitStateController<TResult> : ICircuitController<TResult> { protected readonly TimeSpan DurationOfBreak; protected readonly Action<DelegateResult<TResult>, CircuitState, TimeSpan, Context> OnBreak; protected readonly Action<Context> OnReset; protected readonly Action OnHalfOpen; protected readonly object Lock = new object(); protected long BlockedTill; protected CircuitState InternalCircuitState; protected DelegateResult<TResult> LastOutcome; public CircuitState CircuitState { get { if (InternalCircuitState != CircuitState.Open) return InternalCircuitState; using (TimedLock.Lock(Lock)) { if (InternalCircuitState == CircuitState.Open && !IsInAutomatedBreak_NeedsLock) { InternalCircuitState = CircuitState.HalfOpen; OnHalfOpen(); } return InternalCircuitState; } } } public Exception LastException { get { using (TimedLock.Lock(Lock)) { DelegateResult<TResult> lastOutcome = LastOutcome; return (lastOutcome != null) ? lastOutcome.Exception : null; } } } public TResult LastHandledResult { get { using (TimedLock.Lock(Lock)) return (LastOutcome != null) ? LastOutcome.Result : default(TResult); } } protected bool IsInAutomatedBreak_NeedsLock => SystemClock.UtcNow().Ticks < BlockedTill; protected CircuitStateController(TimeSpan durationOfBreak, Action<DelegateResult<TResult>, CircuitState, TimeSpan, Context> onBreak, Action<Context> onReset, Action onHalfOpen) { DurationOfBreak = durationOfBreak; OnBreak = onBreak; OnReset = onReset; OnHalfOpen = onHalfOpen; InternalCircuitState = 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()); InternalCircuitState = 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.Ticks : (SystemClock.UtcNow() + durationOfBreak).Ticks); CircuitState internalCircuitState = InternalCircuitState; InternalCircuitState = CircuitState.Open; OnBreak(LastOutcome, internalCircuitState, durationOfBreak, context); } public void Reset() { OnCircuitReset(Context.None()); } protected void ResetInternal_NeedsLock(Context context) { BlockedTill = DateTime.MinValue.Ticks; LastOutcome = null; CircuitState internalCircuitState = InternalCircuitState; InternalCircuitState = CircuitState.Closed; if (internalCircuitState != 0) OnReset(context); } protected bool PermitHalfOpenCircuitTest() { long blockedTill = BlockedTill; DateTime dateTime = SystemClock.UtcNow(); if (dateTime.Ticks < blockedTill) return false; ref long blockedTill2 = ref BlockedTill; dateTime = SystemClock.UtcNow(); return Interlocked.CompareExchange(ref blockedTill2, dateTime.Ticks + DurationOfBreak.Ticks, blockedTill) == blockedTill; } private BrokenCircuitException GetBreakingException() { DelegateResult<TResult> lastOutcome = LastOutcome; if (lastOutcome == null) return new BrokenCircuitException("The circuit is now open and is not allowing calls."); if (lastOutcome.Exception != null) return new BrokenCircuitException("The circuit is now open and is not allowing calls.", lastOutcome.Exception); return new BrokenCircuitException<TResult>("The circuit is now open and is not allowing calls.", lastOutcome.Result); } public void OnActionPreExecute() { switch (CircuitState) { case CircuitState.Closed: break; case CircuitState.HalfOpen: if (!PermitHalfOpenCircuitTest()) throw GetBreakingException(); break; case CircuitState.Open: throw GetBreakingException(); 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); } }