vantage_api_pool/resilient/
mod.rs1mod attempt;
14mod auth;
15mod breaker;
16mod builder;
17mod error;
18mod observer;
19mod policy;
20mod rate;
21
22pub use auth::{AuthFuture, AuthRefresher};
23pub use breaker::BreakerState;
24pub use builder::ResilientClientBuilder;
25pub use error::{ClientError, ErrorKind};
26pub use observer::{TransportEvent, TransportObserver};
27pub use policy::{BreakerMode, CallPolicy, RetryMode, RetryPolicy};
28
29use std::sync::atomic::{AtomicUsize, Ordering};
30use std::sync::Arc;
31
32use tokio::sync::Semaphore;
33
34use attempt::AttemptLoop;
35use auth::AuthState;
36use breaker::CircuitBreaker;
37
38#[derive(Clone)]
40pub struct ResilientClient {
41 http: reqwest::Client,
42 semaphore: Arc<Semaphore>,
43 policy: RetryPolicy,
44 breaker: Option<Arc<CircuitBreaker>>,
45 auth: Option<Arc<AuthState>>,
46 in_flight: Arc<AtomicUsize>,
47 peak_in_flight: Arc<AtomicUsize>,
48 observer: Option<(Arc<str>, Arc<dyn TransportObserver>)>,
49 rate: Option<Arc<rate::TokenBucket>>,
50}
51
52impl ResilientClient {
53 pub fn builder() -> ResilientClientBuilder {
54 ResilientClientBuilder::default()
55 }
56
57 pub async fn execute<F>(&self, build: F) -> Result<reqwest::Response, ClientError>
60 where
61 F: Fn(&reqwest::Client) -> reqwest::RequestBuilder,
62 {
63 let policy = CallPolicy::bounded(self.policy.clone());
64 self.execute_with(&policy, build).await
65 }
66
67 pub async fn execute_with<F>(
71 &self,
72 policy: &CallPolicy,
73 build: F,
74 ) -> Result<reqwest::Response, ClientError>
75 where
76 F: Fn(&reqwest::Client) -> reqwest::RequestBuilder,
77 {
78 AttemptLoop::run(self, policy, &build).await
79 }
80
81 pub fn in_flight(&self) -> usize {
85 self.in_flight.load(Ordering::SeqCst)
86 }
87
88 pub fn peak_in_flight(&self) -> usize {
90 self.peak_in_flight.load(Ordering::SeqCst)
91 }
92
93 pub fn breaker_state(&self) -> Option<BreakerState> {
97 self.breaker.as_ref().map(|b| b.state())
98 }
99
100 pub fn key(&self) -> Option<&str> {
102 self.observer.as_ref().map(|(k, _)| &**k)
103 }
104
105 pub fn report(&self, event: TransportEvent) {
107 if let Some((key, obs)) = &self.observer {
108 obs.on_event(key, event);
109 }
110 }
111
112 fn enter_flight(&self) -> InFlightGuard {
116 let now = self.in_flight.fetch_add(1, Ordering::SeqCst) + 1;
117 self.peak_in_flight.fetch_max(now, Ordering::SeqCst);
118 InFlightGuard(Arc::clone(&self.in_flight))
119 }
120}
121
122impl std::fmt::Debug for ResilientClient {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 f.debug_struct("ResilientClient")
125 .field("key", &self.key())
126 .field("in_flight", &self.in_flight())
127 .finish_non_exhaustive()
128 }
129}
130
131struct InFlightGuard(Arc<AtomicUsize>);
132
133impl Drop for InFlightGuard {
134 fn drop(&mut self) {
135 self.0.fetch_sub(1, Ordering::SeqCst);
136 }
137}