Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
CircuitBreakerPolicy.cs
1using System;
2using System.Threading;
3using System.Threading.Tasks;
4
6{
12 {
13 private enum State { Closed, Open, HalfOpen }
14
15 private readonly int threshold;
16 private readonly TimeSpan breakDuration;
17 private readonly Action<string>? onStateChange;
18
19 private int failures;
20 private State state = State.Closed;
21 private DateTimeOffset openedAt;
22 private int halfOpenInProgress; // 0 or 1
23 private readonly object sync = new();
24
25 public CircuitBreakerPolicy(int failureThreshold, TimeSpan breakDuration, Action<string>? onStateChange = null)
26 {
27 ArgumentOutOfRangeException.ThrowIfLessThan(failureThreshold, 1);
28 ArgumentOutOfRangeException.ThrowIfLessThan(breakDuration, TimeSpan.Zero);
29
30 this.threshold = failureThreshold;
31 this.breakDuration = breakDuration;
32 this.onStateChange = onStateChange;
33 }
34
35 public async Task ExecuteAsync(Func<CancellationToken, Task> action, CancellationToken ct)
36 {
37 // Gate based on state
38 this.EnsureEntryAllowed();
39
40 try
41 {
42 await action(ct).ConfigureAwait(false);
43 this.OnSuccess();
44 }
45 catch (OperationCanceledException) when (ct.IsCancellationRequested)
46 {
47 // Forward cancellation without changing state
48 throw;
49 }
50 catch
51 {
52 this.OnFailure();
53 throw;
54 }
55 }
56
57 public async Task<T> ExecuteAsync<T>(Func<CancellationToken, Task<T>> action, CancellationToken ct)
58 {
59 // Gate based on state
60 this.EnsureEntryAllowed();
61
62 try
63 {
64 T result = await action(ct).ConfigureAwait(false);
65 this.OnSuccess();
66 return result;
67 }
68 catch (OperationCanceledException) when (ct.IsCancellationRequested)
69 {
70 // Forward cancellation without changing state
71 throw;
72 }
73 catch
74 {
75 this.OnFailure();
76 throw;
77 }
78 }
79
80 private void EnsureEntryAllowed()
81 {
82 lock (this.sync)
83 {
84 switch (this.state)
85 {
86 case State.Closed:
87 return;
88
89 case State.Open:
90 if (DateTimeOffset.UtcNow - this.openedAt >= this.breakDuration)
91 {
92 this.SetState(State.HalfOpen);
93 // fall through to HalfOpen
94 goto case State.HalfOpen;
95 }
96 throw new InvalidOperationException("Circuit is open");
97
98 case State.HalfOpen:
99 if (Interlocked.CompareExchange(ref this.halfOpenInProgress, 1, 0) == 0)
100 {
101 return; // allow single probe
102 }
103 throw new InvalidOperationException("Circuit is half-open; probe in progress");
104 }
105 }
106 }
107
108 private void OnSuccess()
109 {
110 lock (this.sync)
111 {
112 this.failures = 0;
113 if (this.state != State.Closed)
114 this.SetState(State.Closed);
115 if (this.state != State.HalfOpen)
116 Interlocked.Exchange(ref this.halfOpenInProgress, 0);
117 }
118 }
119
120 private void OnFailure()
121 {
122 lock (this.sync)
123 {
124 if (this.state == State.HalfOpen)
125 {
126 this.Trip();
127 Interlocked.Exchange(ref this.halfOpenInProgress, 0);
128 return;
129 }
130
131 if (++this.failures >= this.threshold)
132 this.Trip();
133 }
134 }
135
136 private void Trip()
137 {
138 this.openedAt = DateTimeOffset.UtcNow;
139 this.SetState(State.Open);
140 }
141
142 private void SetState(State newState)
143 {
144 if (this.state == newState)
145 return;
146 this.state = newState;
147 this.onStateChange?.Invoke(newState.ToString());
148 }
149 }
150}
151
Simple circuit breaker: trips after N consecutive failures and stays open for a cool-down....
async Task< T > ExecuteAsync< T >(Func< CancellationToken, Task< T > > action, CancellationToken ct)
Executes an async action producing a value through the policy pipeline.