tower/retry/
policy.rs

1use std::future::Future;
2
3/// A "retry policy" to classify if a request should be retried.
4///
5/// # Example
6///
7/// ```
8/// use tower::retry::Policy;
9/// use futures_util::future;
10///
11/// type Req = String;
12/// type Res = String;
13///
14/// struct Attempts(usize);
15///
16/// impl<E> Policy<Req, Res, E> for Attempts {
17///     type Future = future::Ready<()>;
18///
19///     fn retry(&mut self, req: &mut Req, result: &mut Result<Res, E>) -> Option<Self::Future> {
20///         match result {
21///             Ok(_) => {
22///                 // Treat all `Response`s as success,
23///                 // so don't retry...
24///                 None
25///             },
26///             Err(_) => {
27///                 // Treat all errors as failures...
28///                 // But we limit the number of attempts...
29///                 if self.0 > 0 {
30///                     // Try again!
31///                     self.0 -= 1;
32///                     Some(future::ready(()))
33///                 } else {
34///                     // Used all our attempts, no retry...
35///                     None
36///                 }
37///             }
38///         }
39///     }
40///
41///     fn clone_request(&mut self, req: &Req) -> Option<Req> {
42///         Some(req.clone())
43///     }
44/// }
45/// ```
46pub trait Policy<Req, Res, E> {
47    /// The [`Future`] type returned by [`Policy::retry`].
48    type Future: Future<Output = ()>;
49
50    /// Check the policy if a certain request should be retried.
51    ///
52    /// This method is passed a reference to the original request, and either
53    /// the [`Service::Response`] or [`Service::Error`] from the inner service.
54    ///
55    /// If the request should **not** be retried, return `None`.
56    ///
57    /// If the request *should* be retried, return `Some` future that will delay
58    /// the next retry of the request. This can be used to sleep for a certain
59    /// duration, to wait for some external condition to be met before retrying,
60    /// or resolve right away, if the request should be retried immediately.
61    ///
62    /// ## Mutating Requests
63    ///
64    /// The policy MAY chose to mutate the `req`: if the request is mutated, the
65    /// mutated request will be sent to the inner service in the next retry.
66    /// This can be helpful for use cases like tracking the retry count in a
67    /// header.
68    ///
69    /// ## Mutating Results
70    ///
71    /// The policy MAY chose to mutate the result. This enables the retry
72    /// policy to convert a failure into a success and vice versa. For example,
73    /// if the policy is used to poll while waiting for a state change, the
74    /// policy can switch the result to emit a specific error when retries are
75    /// exhausted.
76    ///
77    /// The policy can also record metadata on the request to include
78    /// information about the number of retries required or to record that a
79    /// failure failed after exhausting all retries.
80    ///
81    /// [`Service::Response`]: crate::Service::Response
82    /// [`Service::Error`]: crate::Service::Error
83    fn retry(&mut self, req: &mut Req, result: &mut Result<Res, E>) -> Option<Self::Future>;
84
85    /// Tries to clone a request before being passed to the inner service.
86    ///
87    /// If the request cannot be cloned, return [`None`]. Moreover, the retry
88    /// function will not be called if the [`None`] is returned.
89    fn clone_request(&mut self, req: &Req) -> Option<Req>;
90}
91
92// Ensure `Policy` is object safe
93#[cfg(test)]
94fn _obj_safe(_: Box<dyn Policy<(), (), (), Future = futures::future::Ready<()>>>) {}