use crate::RateLimiter;
use backoff::backoff::Backoff;
use backoff::{ExponentialBackoff, ExponentialBackoffBuilder};
use std::hash::Hash;
use std::time::Duration;
pub struct MultiRateLimiter<K> {
period: Duration,
rate_limiters: dashmap::DashMap<K, RateLimiter>,
}
impl<K: Eq + Hash + Clone> MultiRateLimiter<K> {
pub fn new(period: Duration) -> Self {
Self {
period,
rate_limiters: dashmap::DashMap::new(),
}
}
pub async fn throttle<Fut, F, T>(&self, key: K, f: F) -> T
where
Fut: std::future::Future<Output = T>,
F: FnOnce() -> Fut,
{
loop {
let mut backoff = get_backoff();
match self.rate_limiters.try_entry(key.clone()) {
None => {
tokio::time::sleep(backoff.next_backoff().unwrap()).await
}
Some(entry) => {
let rate_limiter = entry.or_insert_with(|| RateLimiter::new(self.period));
return rate_limiter.value().throttle(f).await;
}
}
}
}
}
fn get_backoff() -> ExponentialBackoff {
ExponentialBackoffBuilder::default()
.with_initial_interval(Duration::from_millis(50))
.with_max_elapsed_time(None)
.build()
}