mirror of
https://github.com/MingweiSamuel/Riven.git
synced 2025-03-27 00:53:16 -07:00
84 lines
2.6 KiB
Rust
84 lines
2.6 KiB
Rust
use std::cmp;
|
|
use std::time::{
|
|
Duration,
|
|
Instant,
|
|
};
|
|
|
|
use parking_lot::{
|
|
RwLock,
|
|
};
|
|
|
|
use super::token_bucket::{
|
|
TokenBucket,
|
|
VectorTokenBucket,
|
|
};
|
|
use super::rate_limit_type::RateLimitType;
|
|
|
|
pub struct RateLimit {
|
|
rate_limit_type: RateLimitType,
|
|
// Buckets for this rate limit (synchronized).
|
|
// Almost always read, written only when rate limit rates are updated
|
|
// from API response.
|
|
// TODO: Question of writer starvation.
|
|
buckets: RwLock<Vec<VectorTokenBucket>>,
|
|
// Set to when we can retry if a retry-after header is received.
|
|
retry_after: Option<Instant>,
|
|
}
|
|
|
|
impl RateLimit {
|
|
/// Header specifying which RateLimitType caused a 429.
|
|
const HEADER_XRATELIMITTYPE: &'static str = "X-Rate-Limit-Type";
|
|
/// Header specifying retry after time in seconds after a 429.
|
|
const HEADER_RETRYAFTER: &'static str = "Retry-After";
|
|
|
|
pub fn new(rate_limit_type: RateLimitType) -> Self {
|
|
let initial_bucket = VectorTokenBucket::new(Duration::from_secs(1), 1);
|
|
RateLimit {
|
|
rate_limit_type: rate_limit_type,
|
|
// Rate limit before getting from response: 1/s.
|
|
buckets: RwLock::new(vec![initial_bucket]),
|
|
retry_after: None,
|
|
}
|
|
}
|
|
|
|
pub fn get_both_or_delay(app_rate_limit: &Self, method_rate_limit: &Self) -> Option<Duration> {
|
|
// Check retry after.
|
|
let retry_after_delay = app_rate_limit.get_retry_after_delay()
|
|
.and_then(|a| method_rate_limit.get_retry_after_delay().map(|m| cmp::max(a, m)));
|
|
if retry_after_delay.is_some() {
|
|
return retry_after_delay
|
|
}
|
|
// Check buckets.
|
|
let app_buckets = app_rate_limit.buckets.read();
|
|
let method_buckets = method_rate_limit.buckets.read();
|
|
for bucket in app_buckets.iter().chain(method_buckets.iter()) {
|
|
let delay = bucket.get_delay();
|
|
if delay.is_some() {
|
|
return delay;
|
|
}
|
|
}
|
|
// Success.
|
|
for bucket in app_buckets.iter().chain(method_buckets.iter()) {
|
|
bucket.get_tokens(1);
|
|
}
|
|
None
|
|
}
|
|
|
|
pub fn get_retry_after_delay(&self) -> Option<Duration> {
|
|
self.retry_after.and_then(|i| Instant::now().checked_duration_since(i))
|
|
}
|
|
|
|
pub fn on_response(&self, _response: &reqwest::Response) {
|
|
unimplemented!();
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
fn send_sync() {
|
|
fn is_send_sync<T: Send + Sync>() {}
|
|
is_send_sync::<RateLimit>();
|
|
}
|
|
}
|