1use crate::SharedFailureClassifier;
2use crate::events::CircuitBreakerEvent;
3use std::sync::Arc;
4use std::time::Duration;
5use tower_resilience_core::EventListeners;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum SlidingWindowType {
10 CountBased,
12 TimeBased,
14}
15
16pub struct CircuitBreakerConfig<Res, Err> {
18 pub(crate) failure_rate_threshold: f64,
19 pub(crate) sliding_window_type: SlidingWindowType,
20 pub(crate) sliding_window_size: usize,
21 pub(crate) sliding_window_duration: Option<Duration>,
22 pub(crate) wait_duration_in_open: Duration,
23 pub(crate) permitted_calls_in_half_open: usize,
24 pub(crate) minimum_number_of_calls: usize,
25 pub(crate) failure_classifier: SharedFailureClassifier<Res, Err>,
26 pub(crate) slow_call_duration_threshold: Option<Duration>,
27 pub(crate) slow_call_rate_threshold: f64,
28 pub(crate) event_listeners: EventListeners<CircuitBreakerEvent>,
29 pub(crate) name: String,
30}
31
32impl<Res, Err> CircuitBreakerConfig<Res, Err> {
33 pub fn builder() -> CircuitBreakerConfigBuilder<Res, Err> {
35 CircuitBreakerConfigBuilder::new()
36 }
37}
38
39pub struct CircuitBreakerConfigBuilder<Res, Err> {
41 failure_rate_threshold: f64,
42 sliding_window_type: SlidingWindowType,
43 sliding_window_size: usize,
44 sliding_window_duration: Option<Duration>,
45 wait_duration_in_open: Duration,
46 permitted_calls_in_half_open: usize,
47 failure_classifier: SharedFailureClassifier<Res, Err>,
48 minimum_number_of_calls: Option<usize>,
49 slow_call_duration_threshold: Option<Duration>,
50 slow_call_rate_threshold: f64,
51 event_listeners: EventListeners<CircuitBreakerEvent>,
52 name: String,
53}
54
55impl<Res, Err> CircuitBreakerConfigBuilder<Res, Err> {
56 pub fn new() -> Self {
58 Self {
59 failure_rate_threshold: 0.5,
60 sliding_window_type: SlidingWindowType::CountBased,
61 sliding_window_size: 100,
62 sliding_window_duration: None,
63 wait_duration_in_open: Duration::from_secs(30),
64 permitted_calls_in_half_open: 1,
65 failure_classifier: Arc::new(|res| res.is_err()),
66 minimum_number_of_calls: None,
67 slow_call_duration_threshold: None,
68 slow_call_rate_threshold: 1.0,
69 event_listeners: EventListeners::new(),
70 name: String::from("<unnamed>"),
71 }
72 }
73
74 pub fn failure_rate_threshold(mut self, rate: f64) -> Self {
78 self.failure_rate_threshold = rate;
79 self
80 }
81
82 pub fn sliding_window_type(mut self, window_type: SlidingWindowType) -> Self {
86 self.sliding_window_type = window_type;
87 self
88 }
89
90 pub fn sliding_window_size(mut self, size: usize) -> Self {
97 self.sliding_window_size = size;
98 self
99 }
100
101 pub fn sliding_window_duration(mut self, duration: Duration) -> Self {
108 self.sliding_window_duration = Some(duration);
109 self
110 }
111
112 pub fn wait_duration_in_open(mut self, duration: Duration) -> Self {
116 self.wait_duration_in_open = duration;
117 self
118 }
119
120 pub fn permitted_calls_in_half_open(mut self, n: usize) -> Self {
124 self.permitted_calls_in_half_open = n;
125 self
126 }
127
128 pub fn failure_classifier<F>(mut self, classifier: F) -> Self
132 where
133 F: Fn(&Result<Res, Err>) -> bool + Send + Sync + 'static,
134 {
135 self.failure_classifier = Arc::new(classifier);
136 self
137 }
138
139 pub fn minimum_number_of_calls(mut self, n: usize) -> Self {
143 self.minimum_number_of_calls = Some(n);
144 self
145 }
146
147 pub fn slow_call_duration_threshold(mut self, duration: Duration) -> Self {
154 self.slow_call_duration_threshold = Some(duration);
155 self
156 }
157
158 pub fn slow_call_rate_threshold(mut self, rate: f64) -> Self {
164 self.slow_call_rate_threshold = rate;
165 self
166 }
167
168 pub fn name<N: Into<String>>(mut self, n: N) -> Self {
172 self.name = n.into();
173 self
174 }
175
176 pub fn on_state_transition<F>(mut self, f: F) -> Self
178 where
179 F: Fn(crate::CircuitState, crate::CircuitState) + Send + Sync + 'static,
180 {
181 use tower_resilience_core::FnListener;
182 self.event_listeners
183 .add(FnListener::new(move |event: &CircuitBreakerEvent| {
184 if let CircuitBreakerEvent::StateTransition {
185 from_state,
186 to_state,
187 ..
188 } = event
189 {
190 f(*from_state, *to_state);
191 }
192 }));
193 self
194 }
195
196 pub fn on_call_permitted<F>(mut self, f: F) -> Self
198 where
199 F: Fn(crate::CircuitState) + Send + Sync + 'static,
200 {
201 self.event_listeners
202 .add(tower_resilience_core::FnListener::new(
203 move |event: &CircuitBreakerEvent| {
204 if let CircuitBreakerEvent::CallPermitted { state, .. } = event {
205 f(*state);
206 }
207 },
208 ));
209 self
210 }
211
212 pub fn on_call_rejected<F>(mut self, f: F) -> Self
214 where
215 F: Fn() + Send + Sync + 'static,
216 {
217 self.event_listeners
218 .add(tower_resilience_core::FnListener::new(
219 move |event: &CircuitBreakerEvent| {
220 if matches!(event, CircuitBreakerEvent::CallRejected { .. }) {
221 f();
222 }
223 },
224 ));
225 self
226 }
227
228 pub fn on_success<F>(mut self, f: F) -> Self
230 where
231 F: Fn(crate::CircuitState) + Send + Sync + 'static,
232 {
233 self.event_listeners
234 .add(tower_resilience_core::FnListener::new(
235 move |event: &CircuitBreakerEvent| {
236 if let CircuitBreakerEvent::SuccessRecorded { state, .. } = event {
237 f(*state);
238 }
239 },
240 ));
241 self
242 }
243
244 pub fn on_failure<F>(mut self, f: F) -> Self
246 where
247 F: Fn(crate::CircuitState) + Send + Sync + 'static,
248 {
249 self.event_listeners
250 .add(tower_resilience_core::FnListener::new(
251 move |event: &CircuitBreakerEvent| {
252 if let CircuitBreakerEvent::FailureRecorded { state, .. } = event {
253 f(*state);
254 }
255 },
256 ));
257 self
258 }
259
260 pub fn on_slow_call<F>(mut self, f: F) -> Self
262 where
263 F: Fn(Duration) + Send + Sync + 'static,
264 {
265 use tower_resilience_core::FnListener;
266 self.event_listeners
267 .add(FnListener::new(move |event: &CircuitBreakerEvent| {
268 if let CircuitBreakerEvent::SlowCallDetected { duration, .. } = event {
269 f(*duration);
270 }
271 }));
272 self
273 }
274
275 pub fn build(self) -> crate::layer::CircuitBreakerLayer<Res, Err> {
277 if self.sliding_window_type == SlidingWindowType::TimeBased
279 && self.sliding_window_duration.is_none()
280 {
281 panic!("sliding_window_duration must be set when using TimeBased sliding window");
282 }
283
284 let config = CircuitBreakerConfig {
285 failure_rate_threshold: self.failure_rate_threshold,
286 sliding_window_type: self.sliding_window_type,
287 sliding_window_size: self.sliding_window_size,
288 sliding_window_duration: self.sliding_window_duration,
289 wait_duration_in_open: self.wait_duration_in_open,
290 permitted_calls_in_half_open: self.permitted_calls_in_half_open,
291 failure_classifier: self.failure_classifier,
292 minimum_number_of_calls: self
293 .minimum_number_of_calls
294 .unwrap_or(self.sliding_window_size),
295 slow_call_duration_threshold: self.slow_call_duration_threshold,
296 slow_call_rate_threshold: self.slow_call_rate_threshold,
297 event_listeners: self.event_listeners,
298 name: self.name,
299 };
300
301 crate::layer::CircuitBreakerLayer::new(config)
302 }
303}
304
305impl<Res, Err> Default for CircuitBreakerConfigBuilder<Res, Err> {
306 fn default() -> Self {
307 Self::new()
308 }
309}