]>
git.proxmox.com Git - proxmox.git/blob - proxmox-http/src/client/rate_limiter.rs
677dfb1001979ca85d3bc164fa4854a0da751d4a
1 use std
::time
::{Duration, Instant}
;
2 use std
::convert
::TryInto
;
4 /// Token bucket based rate limiter
5 pub struct RateLimiter
{
6 rate
: u64, // tokens/second
8 traffic
: u64, // overall traffic
16 const NO_DELAY
: Duration
= Duration
::from_millis(0);
18 /// Creates a new instance, using [Instant::now] as start time.
19 pub fn new(rate
: u64, bucket_size
: u64) -> Self {
20 let start_time
= Instant
::now();
21 Self::with_start_time(rate
, bucket_size
, start_time
)
24 /// Creates a new instance with specified `rate`, `bucket_size` and `start_time`.
25 pub fn with_start_time(rate
: u64, bucket_size
: u64, start_time
: Instant
) -> Self {
31 last_update
: start_time
,
32 // start with empty bucket (all tokens consumed)
33 consumed_tokens
: bucket_size
,
37 /// Returns the average rate (since `start_time`)
38 pub fn average_rate(&self, current_time
: Instant
) -> f64 {
39 let time_diff
= current_time
.saturating_duration_since(self.start_time
).as_secs_f64();
43 (self.traffic
as f64) / time_diff
47 fn refill_bucket(&mut self, current_time
: Instant
) {
48 let time_diff
= match current_time
.checked_duration_since(self.last_update
) {
49 Some(duration
) => duration
.as_nanos(),
51 //log::error!("update_time: got negative time diff");
56 if time_diff
== 0 { return; }
58 self.last_update
= current_time
;
60 let allowed_traffic
= ((time_diff
.saturating_mul(self.rate
as u128
)) / 1_000_000_000)
61 .try_into().unwrap_or(u64::MAX
);
63 self.consumed_tokens
= self.consumed_tokens
.saturating_sub(allowed_traffic
);
66 /// Register traffic, returning a proposed delay to reach the expected rate.
67 pub fn register_traffic(&mut self, current_time
: Instant
, data_len
: u64) -> Duration
{
68 self.refill_bucket(current_time
);
70 self.traffic
+= data_len
;
71 self.consumed_tokens
+= data_len
;
73 if self.consumed_tokens
<= self.bucket_size
{
74 return Self::NO_DELAY
;
76 Duration
::from_nanos((self.consumed_tokens
- self.bucket_size
).saturating_mul(1_000_000_000)/ self.rate
)