2021-06-30 16:34:34 -07:00
|
|
|
use std::collections::VecDeque;
|
2023-05-10 11:20:15 -07:00
|
|
|
use std::fmt;
|
2021-06-30 16:34:34 -07:00
|
|
|
use std::time::Duration;
|
|
|
|
|
|
|
|
use parking_lot::{Mutex, MutexGuard};
|
|
|
|
|
|
|
|
use super::Instant; // Hack for token_bucket_test.rs.
|
|
|
|
|
|
|
|
/// A `TokenBucket` keeps track of number of requests allowed per duration of
|
|
|
|
/// time.
|
|
|
|
///
|
|
|
|
/// Respone headers contain descriptions of rate limits such as
|
|
|
|
/// `"X-App-Rate-Limit": "20:1,100:120"`. Each `TokenBucket` corresponds to a
|
|
|
|
/// single `"100:120"` (100 requests per 120 seconds).
|
|
|
|
pub trait TokenBucket {
|
|
|
|
/// Get the duration til the next available token, or None if a token
|
|
|
|
/// is available.
|
|
|
|
/// # Returns
|
|
|
|
/// Duration or 0 duration.
|
|
|
|
fn get_delay(&self) -> Option<Duration>;
|
|
|
|
|
|
|
|
/// Gets n tokens, regardless of whether they are available.
|
|
|
|
/// # Parameters
|
|
|
|
/// * `n` - Number of tokens to take.
|
|
|
|
/// # Returns
|
|
|
|
/// True if the tokens were obtained without violating limits, false
|
|
|
|
/// otherwise.
|
|
|
|
fn get_tokens(&self, n: usize) -> bool;
|
|
|
|
|
|
|
|
/// Get the duration of this bucket.
|
|
|
|
/// # Returns
|
|
|
|
/// Duration of the bucket.
|
|
|
|
fn get_bucket_duration(&self) -> Duration;
|
|
|
|
|
|
|
|
/// Get the total limit of this bucket per timespan.
|
|
|
|
/// # Returns
|
|
|
|
/// Total limit per timespan.
|
|
|
|
fn get_total_limit(&self) -> usize;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct VectorTokenBucket {
|
2021-07-23 17:57:41 -07:00
|
|
|
/// The total limit supplied to the constructor, unadjusted by the [rate_usage_factor].
|
|
|
|
_given_total_limit: usize,
|
|
|
|
/// Additional factor to reduce rate limit usage, in range (0, 1\].
|
|
|
|
_rate_usage_factor: f32,
|
|
|
|
|
2021-06-30 16:34:34 -07:00
|
|
|
/// Duration of this TokenBucket.
|
|
|
|
duration: Duration,
|
|
|
|
// Total tokens available from this TokenBucket.
|
|
|
|
total_limit: usize,
|
2021-07-23 17:57:41 -07:00
|
|
|
|
2021-06-30 16:34:34 -07:00
|
|
|
/// Extra duration to be considered on top of `duration`, to account for
|
|
|
|
/// varying network latency.
|
|
|
|
duration_overhead: Duration,
|
|
|
|
|
|
|
|
/// Duration considered for burst factor.
|
|
|
|
burst_duration: Duration,
|
|
|
|
/// Limit allowed per burst_duration, for burst factor.
|
|
|
|
burst_limit: usize,
|
|
|
|
|
|
|
|
/// Record of timestamps (synchronized).
|
|
|
|
timestamps: Mutex<VecDeque<Instant>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl VectorTokenBucket {
|
2023-05-10 11:20:15 -07:00
|
|
|
pub fn new(
|
|
|
|
duration: Duration,
|
|
|
|
given_total_limit: usize,
|
|
|
|
duration_overhead: Duration,
|
|
|
|
burst_factor: f32,
|
|
|
|
rate_usage_factor: f32,
|
|
|
|
) -> Self {
|
|
|
|
debug_assert!(
|
|
|
|
0.0 < rate_usage_factor && rate_usage_factor <= 1.0,
|
|
|
|
"BAD rate_usage_factor {}.",
|
|
|
|
rate_usage_factor
|
|
|
|
);
|
|
|
|
debug_assert!(
|
|
|
|
0.0 < burst_factor && burst_factor <= 1.0,
|
|
|
|
"BAD burst_factor {}.",
|
|
|
|
burst_factor
|
|
|
|
);
|
2021-06-30 16:34:34 -07:00
|
|
|
// Float ops may lose precision, but nothing should be that precise.
|
2021-07-23 17:57:41 -07:00
|
|
|
// API always uses round numbers, burst_factor is frac of 256.
|
|
|
|
|
|
|
|
// Adjust everything by rate_usage_factor.
|
2023-05-10 11:20:15 -07:00
|
|
|
let total_limit = std::cmp::max(
|
|
|
|
1,
|
|
|
|
(given_total_limit as f32 * rate_usage_factor).floor() as usize,
|
|
|
|
);
|
2021-06-30 16:34:34 -07:00
|
|
|
|
|
|
|
// Effective duration.
|
|
|
|
let d_eff = duration + duration_overhead;
|
2021-07-23 17:57:41 -07:00
|
|
|
let burst_duration = d_eff.mul_f32(burst_factor);
|
2023-05-10 11:20:15 -07:00
|
|
|
let burst_limit = std::cmp::max(1, (total_limit as f32 * burst_factor).floor() as usize);
|
2021-06-30 16:34:34 -07:00
|
|
|
debug_assert!(burst_limit <= total_limit);
|
|
|
|
|
|
|
|
VectorTokenBucket {
|
2021-07-23 17:57:41 -07:00
|
|
|
_given_total_limit: given_total_limit,
|
|
|
|
_rate_usage_factor: rate_usage_factor,
|
2021-06-30 16:34:34 -07:00
|
|
|
|
2021-07-23 17:57:41 -07:00
|
|
|
duration,
|
|
|
|
total_limit,
|
|
|
|
|
|
|
|
duration_overhead,
|
|
|
|
burst_duration,
|
|
|
|
burst_limit,
|
2021-06-30 16:34:34 -07:00
|
|
|
|
|
|
|
timestamps: Mutex::new(VecDeque::with_capacity(total_limit)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_get_timestamps(&self) -> MutexGuard<VecDeque<Instant>> {
|
|
|
|
let mut timestamps = self.timestamps.lock();
|
|
|
|
let cutoff = Instant::now() - self.duration - self.duration_overhead;
|
2021-07-23 17:57:41 -07:00
|
|
|
// Pop off timestamps that are beyound the bucket duration.
|
2021-06-30 16:34:34 -07:00
|
|
|
while timestamps.back().map_or(false, |ts| *ts < cutoff) {
|
|
|
|
timestamps.pop_back();
|
|
|
|
}
|
2021-10-29 22:38:48 -07:00
|
|
|
timestamps
|
2021-06-30 16:34:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TokenBucket for VectorTokenBucket {
|
|
|
|
fn get_delay(&self) -> Option<Duration> {
|
|
|
|
let timestamps = self.update_get_timestamps();
|
|
|
|
|
|
|
|
// Full rate limit.
|
|
|
|
if let Some(ts) = timestamps.get(self.total_limit - 1) {
|
|
|
|
// Return amount of time needed for timestamp `ts` to go away.
|
2023-05-10 11:20:15 -07:00
|
|
|
Instant::now()
|
|
|
|
.checked_duration_since(*ts)
|
|
|
|
.and_then(|passed_dur| {
|
|
|
|
(self.duration + self.duration_overhead).checked_sub(passed_dur)
|
|
|
|
})
|
2021-06-30 16:34:34 -07:00
|
|
|
}
|
|
|
|
// Otherwise burst rate limit.
|
|
|
|
else if let Some(ts) = timestamps.get(self.burst_limit - 1) {
|
|
|
|
// Return amount of time needed for timestamp `ts` to go away.
|
2023-05-10 11:20:15 -07:00
|
|
|
Instant::now()
|
|
|
|
.checked_duration_since(*ts)
|
2021-06-30 16:34:34 -07:00
|
|
|
.and_then(|passed_dur| self.burst_duration.checked_sub(passed_dur))
|
|
|
|
}
|
|
|
|
// No delay needed.
|
|
|
|
else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_tokens(&self, n: usize) -> bool {
|
|
|
|
let mut timestamps = self.update_get_timestamps();
|
|
|
|
|
|
|
|
let now = Instant::now();
|
|
|
|
|
|
|
|
timestamps.reserve(n);
|
|
|
|
for _ in 0..n {
|
|
|
|
timestamps.push_front(now);
|
|
|
|
}
|
2021-07-23 17:57:41 -07:00
|
|
|
|
|
|
|
// Check total limit.
|
|
|
|
if self.total_limit < timestamps.len() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check burst limit.
|
|
|
|
if let Some(burst_time) = timestamps.get(self.burst_limit) {
|
|
|
|
let duration_since = now.duration_since(*burst_time); // `now` before `burst_time` will panic.
|
|
|
|
if duration_since < self.burst_duration {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-29 22:38:48 -07:00
|
|
|
true
|
2021-06-30 16:34:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_bucket_duration(&self) -> Duration {
|
|
|
|
self.duration
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_total_limit(&self) -> usize {
|
|
|
|
self.total_limit
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Debug for VectorTokenBucket {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2023-05-10 11:20:15 -07:00
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"({}/{}:{})",
|
|
|
|
self.timestamps.lock().len(),
|
|
|
|
self.total_limit,
|
|
|
|
self.duration.as_secs()
|
|
|
|
)
|
2021-06-30 16:34:34 -07:00
|
|
|
}
|
|
|
|
}
|