Attempts to consume `tokens` from the bucket and returns whether the action succeeded. TODO (Issue #259): handle cases where a single request is larger than the full capacity for such cases we need to support partial fulfilment of requests
(&mut self, mut tokens: u64)
| 160 | // TODO (Issue #259): handle cases where a single request is larger than the full capacity |
| 161 | // for such cases we need to support partial fulfilment of requests |
| 162 | pub fn reduce(&mut self, mut tokens: u64) -> BucketReduction { |
| 163 | // First things first: consume the one-time-burst budget. |
| 164 | if self.one_time_burst > 0 { |
| 165 | // We still have burst budget for *all* tokens requests. |
| 166 | if self.one_time_burst >= tokens { |
| 167 | self.one_time_burst -= tokens; |
| 168 | self.last_update = Instant::now(); |
| 169 | // No need to continue to the refill process, we still have burst budget to consume from. |
| 170 | return BucketReduction::Success; |
| 171 | } |
| 172 | // We still have burst budget for *some* of the tokens requests. |
| 173 | // The tokens left unfulfilled will be consumed from current `self.budget`. |
| 174 | tokens -= self.one_time_burst; |
| 175 | self.one_time_burst = 0; |
| 176 | } |
| 177 | |
| 178 | // Compute time passed since last refill/update. |
| 179 | let time_delta = self.last_update.elapsed().as_nanos() as u64; |
| 180 | self.last_update = Instant::now(); |
| 181 | |
| 182 | // At each 'time_delta' nanoseconds the bucket should refill with: |
| 183 | // refill_amount = (time_delta * size) / (complete_refill_time_ms * 1_000_000) |
| 184 | // `processed_capacity` and `processed_refill_time` are the result of simplifying above |
| 185 | // fraction formula with their greatest-common-factor. |
| 186 | self.budget += (time_delta * self.processed_capacity) / self.processed_refill_time; |
| 187 | |
| 188 | if self.budget >= self.size { |
| 189 | self.budget = self.size; |
| 190 | } |
| 191 | |
| 192 | if tokens > self.budget { |
| 193 | // This operation requests a bandwidth higher than the bucket size |
| 194 | if tokens > self.size { |
| 195 | error!( |
| 196 | "Consumed {} tokens from bucket of size {}", |
| 197 | tokens, self.size |
| 198 | ); |
| 199 | // Empty the bucket and report an overconsumption of |
| 200 | // (remaining tokens / size) times larger than the bucket size |
| 201 | tokens -= self.budget; |
| 202 | self.budget = 0; |
| 203 | return BucketReduction::OverConsumption(tokens as f64 / self.size as f64); |
| 204 | } |
| 205 | // If not enough tokens consume() fails, return false. |
| 206 | return BucketReduction::Failure; |
| 207 | } |
| 208 | |
| 209 | self.budget -= tokens; |
| 210 | BucketReduction::Success |
| 211 | } |
| 212 | |
| 213 | /// "Manually" adds tokens to bucket. |
| 214 | pub fn replenish(&mut self, tokens: u64) { |