use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::{RwLock, Semaphore};
use super::auth::{AuthRefresher, AuthState};
use super::breaker::CircuitBreaker;
use super::rate::TokenBucket;
use super::{ResilientClient, RetryPolicy, TransportObserver};
pub struct ResilientClientBuilder {
http: Option<reqwest::Client>,
max_parallel: usize,
policy: RetryPolicy,
breaker: Option<(usize, Duration, Duration)>,
auth: Option<(AuthRefresher, String, String)>,
observer: Option<(Arc<str>, Arc<dyn TransportObserver>)>,
rate: Option<(f64, usize)>,
}
impl Default for ResilientClientBuilder {
fn default() -> Self {
Self {
http: None,
max_parallel: 8,
policy: RetryPolicy::default(),
breaker: None,
auth: None,
observer: None,
rate: None,
}
}
}
impl ResilientClientBuilder {
pub fn max_parallel(mut self, n: usize) -> Self {
self.max_parallel = n.max(1);
self
}
pub fn retry(mut self, policy: RetryPolicy) -> Self {
self.policy = policy;
self
}
pub fn circuit_breaker(mut self, threshold: usize, cooldown: Duration) -> Self {
self.breaker = Some((threshold, cooldown, cooldown));
self
}
pub fn circuit_breaker_growing(
mut self,
threshold: usize,
base_cooldown: Duration,
max_cooldown: Duration,
) -> Self {
self.breaker = Some((threshold, base_cooldown, max_cooldown));
self
}
pub fn default_breaker(self) -> Self {
self.circuit_breaker_growing(5, Duration::from_secs(5), Duration::from_secs(60))
}
pub fn bearer_auth(self, refresher: AuthRefresher) -> Self {
self.auth(refresher, "Authorization", "Bearer ")
}
pub fn auth(
mut self,
refresher: AuthRefresher,
header: impl Into<String>,
scheme: impl Into<String>,
) -> Self {
self.auth = Some((refresher, header.into(), scheme.into()));
self
}
pub fn http_client(mut self, client: reqwest::Client) -> Self {
self.http = Some(client);
self
}
pub fn rate_limit(self, per_second: f64) -> Self {
let burst = per_second.ceil().max(1.0) as usize;
self.rate_limit_with_burst(per_second, burst)
}
pub fn rate_limit_with_burst(mut self, per_second: f64, burst: usize) -> Self {
self.rate = Some((per_second, burst));
self
}
pub fn observer(
mut self,
key: impl Into<Arc<str>>,
observer: Arc<dyn TransportObserver>,
) -> Self {
self.observer = Some((key.into(), observer));
self
}
pub fn build(self) -> ResilientClient {
ResilientClient {
http: self.http.unwrap_or_default(),
semaphore: Arc::new(Semaphore::new(self.max_parallel)),
policy: self.policy,
breaker: self
.breaker
.map(|(t, base, max)| Arc::new(CircuitBreaker::new(t, base, max))),
auth: self.auth.map(|(refresh, header, scheme)| {
Arc::new(AuthState {
token: RwLock::new(None),
refresh,
header,
scheme,
})
}),
in_flight: Arc::new(AtomicUsize::new(0)),
peak_in_flight: Arc::new(AtomicUsize::new(0)),
observer: self.observer,
rate: self.rate.map(|(r, b)| Arc::new(TokenBucket::new(r, b))),
}
}
}