1use std::{
2 fmt,
3 future::Future,
4 pin::Pin,
5 sync::{Arc, Mutex},
6 task::{ready, Context, Poll},
7};
8
9use futures::FutureExt as _;
10use pin_project_lite::pin_project;
11use tower::{retry::Policy, Layer, Service};
12use tracing::debug;
13
14#[derive(Debug)]
16pub enum Error<E, R> {
17 Service(E),
19
20 Retry(R),
22
23 Open(R),
25}
26
27impl<E, R> std::error::Error for Error<E, R>
28where
29 E: std::error::Error + 'static,
30 R: fmt::Debug,
31{
32 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
33 match self {
34 Self::Service(e) => Some(e),
35 Self::Retry(_) | Self::Open(_) => None,
36 }
37 }
38}
39
40impl<E, R> fmt::Display for Error<E, R>
41where
42 E: fmt::Display,
43{
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 match self {
46 Self::Service(e) => write!(f, "service error: {}", e),
47 Self::Retry(_) => write!(f, "request should be retried"),
48 Self::Open(_) => write!(f, "circuit breaker open"),
49 }
50 }
51}
52
53impl<E, R> PartialEq for Error<E, R>
54where
55 E: PartialEq,
56 R: PartialEq,
57{
58 fn eq(&self, other: &Self) -> bool {
59 match (self, other) {
60 (Self::Service(a), Self::Service(b)) => a == b,
61 (Self::Retry(a), Self::Retry(b)) => a == b,
62 (Self::Open(a), Self::Open(b)) => a == b,
63 _ => false,
64 }
65 }
66}
67
68pin_project! {
69 pub struct ResponseFuture<P, F, Request> {
71 state: Arc<Mutex<State<P>>>,
72 #[pin]
73 inner: Option<F>,
74 req: Option<Request>,
75 }
76}
77
78impl<P, F, T, E, Request> Future for ResponseFuture<P, F, Request>
79where
80 P: Policy<Request, T, E>,
81 P::Future: Send + 'static,
82 F: Future<Output = Result<T, E>>,
83{
84 type Output = Result<T, Error<E, Request>>;
85
86 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
87 let this = self.project();
101 if let Some(inner) = this.inner.as_pin_mut() {
102 let mut result = ready!(inner.poll(cx));
103
104 let mut state = this.state.lock().unwrap();
105 match this.req.take() {
106 Some(mut req) => match state.policy.retry(&mut req, &mut result) {
107 Some(backoff) => {
108 if state.backoff.is_none() {
112 debug!("no existing backoff future present, setting delay backoff");
113 state.backoff = Some(backoff.boxed());
114 }
115
116 Poll::Ready(Err(Error::Retry(req)))
117 }
118 None => {
119 debug!("request completed, no retry indicated");
120 Poll::Ready(result.map_err(Error::Service))
121 }
122 },
123 None => {
124 debug!("request completed, but request not cloneable so returning response as-is");
125 Poll::Ready(result.map_err(Error::Service))
126 }
127 }
128 } else {
129 debug!("circuit breaker open prior to call, returning error");
130 Poll::Ready(Err(Error::Open(
131 this.req.take().expect("response future polled after completion"),
132 )))
133 }
134 }
135}
136
137struct State<P> {
138 policy: P,
139 backoff: Option<Pin<Box<dyn Future<Output = ()> + Send>>>,
140}
141
142impl<P> std::fmt::Debug for State<P>
143where
144 P: std::fmt::Debug,
145{
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 let backoff = if self.backoff.is_some() { "set" } else { "unset" };
148 f.debug_struct("State")
149 .field("policy", &self.policy)
150 .field("backoff", &backoff)
151 .finish()
152 }
153}
154
155pub struct RetryCircuitBreakerLayer<P> {
159 policy: P,
160}
161
162impl<P> RetryCircuitBreakerLayer<P> {
163 pub const fn new(policy: P) -> Self {
165 Self { policy }
166 }
167}
168
169impl<P, S> Layer<S> for RetryCircuitBreakerLayer<P>
170where
171 P: Clone,
172{
173 type Service = RetryCircuitBreaker<S, P>;
174
175 fn layer(&self, inner: S) -> Self::Service {
176 RetryCircuitBreaker::new(inner, self.policy.clone())
177 }
178}
179
180#[derive(Debug)]
198pub struct RetryCircuitBreaker<S, P> {
199 inner: S,
200 state: Arc<Mutex<State<P>>>,
201}
202
203impl<S, P> RetryCircuitBreaker<S, P> {
204 pub fn new(inner: S, policy: P) -> Self {
206 Self {
207 inner,
208 state: Arc::new(Mutex::new(State { policy, backoff: None })),
209 }
210 }
211}
212
213impl<S, P, Request> Service<Request> for RetryCircuitBreaker<S, P>
214where
215 S: Service<Request>,
216 P: Policy<Request, S::Response, S::Error>,
217 P::Future: Send + 'static,
218{
219 type Response = S::Response;
220 type Error = Error<S::Error, Request>;
221 type Future = ResponseFuture<P, S::Future, Request>;
222
223 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
224 {
225 let mut state = self.state.lock().unwrap();
227 if let Some(backoff) = state.backoff.as_mut() {
228 ready!(backoff.as_mut().poll(cx));
229
230 debug!("circuit breaker backoff complete");
231
232 state.backoff = None;
234 }
235 }
236
237 self.inner.poll_ready(cx).map_err(Error::Service)
239 }
240
241 fn call(&mut self, req: Request) -> Self::Future {
242 let response_state = Arc::clone(&self.state);
243
244 let mut state = self.state.lock().unwrap();
245 if state.backoff.is_some() {
246 ResponseFuture {
247 state: response_state,
248 inner: None,
249 req: Some(req),
250 }
251 } else {
252 let cloned_req = state.policy.clone_request(&req);
254 let inner = self.inner.call(req);
255
256 ResponseFuture {
257 state: response_state,
258 inner: Some(inner),
259 req: cloned_req,
260 }
261 }
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use std::{
268 future::{ready, Ready},
269 time::Duration,
270 };
271
272 use tokio::time::Sleep;
273 use tokio_test::{assert_pending, assert_ready_ok};
274 use tower::{retry::Policy, ServiceExt as _};
275
276 use super::*;
277
278 const BACKOFF_DUR: Duration = Duration::from_secs(1);
279
280 #[derive(Clone, Debug, Eq, PartialEq)]
281 enum BasicRequest {
282 Ok(String),
283 Err(String),
284 }
285
286 impl BasicRequest {
287 fn success<S: AsRef<str>>(value: S) -> Self {
288 Self::Ok(value.as_ref().to_string())
289 }
290
291 fn failure<S: AsRef<str>>(value: S) -> Self {
292 Self::Err(value.as_ref().to_string())
293 }
294
295 fn as_service_response(&self) -> Result<String, Error<String, Self>> {
296 match self {
297 Self::Ok(value) => Ok(value.clone()),
298 Self::Err(value) => Err(Error::Service(value.clone())),
299 }
300 }
301
302 fn as_retry_response(&self) -> Result<String, Error<String, Self>> {
303 Err(Error::Retry(self.clone()))
304 }
305
306 fn as_open_response(&self) -> Result<String, Error<String, Self>> {
307 Err(Error::Open(self.clone()))
308 }
309 }
310
311 impl PartialEq<Result<String, String>> for BasicRequest {
312 fn eq(&self, other: &Result<String, String>) -> bool {
313 match self {
314 Self::Ok(value) => other.as_ref() == Ok(value),
315 Self::Err(value) => other.as_ref() == Err(value),
316 }
317 }
318 }
319
320 #[derive(Debug)]
321 struct LoopbackService;
322
323 impl Service<BasicRequest> for LoopbackService {
324 type Response = String;
325 type Error = String;
326 type Future = Ready<Result<String, String>>;
327
328 fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
329 Poll::Ready(Ok(()))
330 }
331
332 fn call(&mut self, req: BasicRequest) -> Self::Future {
333 let res = match req {
334 BasicRequest::Ok(value) => Ok(value),
335 BasicRequest::Err(value) => Err(value),
336 };
337 ready(res)
338 }
339 }
340
341 #[derive(Debug)]
342 struct CloneableTestRetryPolicy;
343
344 impl<Req, T, E> Policy<Req, T, E> for CloneableTestRetryPolicy
345 where
346 Req: Clone,
347 {
348 type Future = Sleep;
349
350 fn retry(&mut self, _: &mut Req, res: &mut Result<T, E>) -> Option<Self::Future> {
351 match res {
352 Ok(_) => None,
353 Err(_) => Some(tokio::time::sleep(BACKOFF_DUR)),
354 }
355 }
356
357 fn clone_request(&mut self, req: &Req) -> Option<Req> {
358 Some(req.clone())
359 }
360 }
361
362 #[derive(Debug)]
363 struct NonCloneableTestRetryPolicy;
364
365 impl<Req, T, E> Policy<Req, T, E> for NonCloneableTestRetryPolicy {
366 type Future = Sleep;
367
368 fn retry(&mut self, _: &mut Req, res: &mut Result<T, E>) -> Option<Self::Future> {
369 match res {
370 Ok(_) => None,
371 Err(_) => Some(tokio::time::sleep(BACKOFF_DUR)),
372 }
373 }
374
375 fn clone_request(&mut self, _: &Req) -> Option<Req> {
376 None
377 }
378 }
379
380 #[tokio::test(start_paused = true)]
381 async fn basic() {
382 let good_req = BasicRequest::success("good");
383 let bad_req = BasicRequest::failure("bad");
384
385 let mut circuit_breaker = RetryCircuitBreaker::new(LoopbackService, CloneableTestRetryPolicy);
386
387 let svc = circuit_breaker.ready().await.expect("should never fail to be ready");
391 let fut = svc.call(good_req.clone());
392 let result = fut.await;
393 assert_eq!(result, good_req.as_service_response());
394
395 let svc = circuit_breaker.ready().await.expect("should never fail to be ready");
399 let fut = svc.call(bad_req.clone());
400 let result = fut.await;
401 assert_eq!(result, bad_req.as_retry_response());
402
403 let mut svc_fut = tokio_test::task::spawn(circuit_breaker.ready());
406 assert_pending!(svc_fut.poll());
407
408 tokio::time::advance(BACKOFF_DUR + Duration::from_millis(1)).await;
410 assert!(svc_fut.is_woken());
411 let svc = assert_ready_ok!(svc_fut.poll());
412
413 let fut = svc.call(good_req.clone());
414 let result = fut.await;
415 assert_eq!(result, good_req.as_service_response());
416
417 let svc = circuit_breaker.ready().await.expect("should never fail to be ready");
419 let fut = svc.call(good_req.clone());
420 let result = fut.await;
421 assert_eq!(result, good_req.as_service_response());
422 }
423
424 #[tokio::test]
425 async fn retry_policy_no_clone() {
426 let good_req = BasicRequest::success("good");
427 let bad_req = BasicRequest::failure("bad");
428
429 let mut circuit_breaker = RetryCircuitBreaker::new(LoopbackService, NonCloneableTestRetryPolicy);
433 let svc = circuit_breaker.ready().await.expect("should never fail to be ready");
434 let fut = svc.call(good_req.clone());
435 let result = fut.await;
436 assert_eq!(result, good_req.as_service_response());
437
438 let mut circuit_breaker = RetryCircuitBreaker::new(LoopbackService, NonCloneableTestRetryPolicy);
441 let svc = circuit_breaker.ready().await.expect("should never fail to be ready");
442 let fut = svc.call(bad_req.clone());
443 let result = fut.await;
444 assert_eq!(result, bad_req.as_service_response());
445 }
446
447 #[tokio::test(start_paused = true)]
448 async fn concurrent_calls_can_advance() {
449 let good_req = BasicRequest::success("good");
450 let bad_req = BasicRequest::failure("bad");
451
452 let mut circuit_breaker = RetryCircuitBreaker::new(LoopbackService, CloneableTestRetryPolicy);
453
454 let svc = circuit_breaker.ready().await.expect("should never fail to be ready");
456 let fut = svc.call(good_req.clone());
457 let result = fut.await;
458 assert_eq!(result, good_req.as_service_response());
459
460 let svc = circuit_breaker.ready().await.expect("should never fail to be ready");
465 let bad_fut = svc.call(bad_req.clone());
466
467 let svc = circuit_breaker.ready().await.expect("should never fail to be ready");
468 let good_fut = svc.call(good_req.clone());
469
470 let bad_result = bad_fut.await;
471 assert_eq!(bad_result, bad_req.as_retry_response());
472
473 let good_result = good_fut.await;
474 assert_eq!(good_result, good_req.as_service_response());
475
476 let mut svc_fut = tokio_test::task::spawn(circuit_breaker.ready());
479 assert_pending!(svc_fut.poll());
480 }
481
482 #[tokio::test(start_paused = true)]
483 async fn breaker_open_between_ready_and_call() {
484 let good_req = BasicRequest::success("good");
485 let bad_req = BasicRequest::failure("bad");
486
487 let mut circuit_breaker = RetryCircuitBreaker::new(LoopbackService, CloneableTestRetryPolicy);
488
489 let svc = circuit_breaker.ready().await.expect("should never fail to be ready");
494 let bad_fut = svc.call(bad_req.clone());
495
496 let svc = circuit_breaker.ready().await.expect("should never fail to be ready");
499
500 let bad_result = bad_fut.await;
502 assert_eq!(bad_result, bad_req.as_retry_response());
503
504 let good_fut = svc.call(good_req.clone());
506 let good_result = good_fut.await;
507 assert_eq!(good_result, good_req.as_open_response());
508 }
509}