ratelimit_meter/state/
direct.rs

1//! An in-memory rate limiter that can make decisions for a single
2//! situation.
3
4use crate::lib::*;
5
6use crate::{
7    algorithms::{Algorithm, DefaultAlgorithm},
8    clock, InconsistentCapacity, NegativeMultiDecision,
9};
10
11/// An in-memory rate limiter that makes direct (un-keyed)
12/// rate-limiting decisions. Direct rate limiters can be used to
13/// e.g. regulate the transmission of packets on a single connection,
14/// or to ensure that an API client stays within a server's rate
15/// limit.
16#[derive(Debug, Clone)]
17pub struct DirectRateLimiter<
18    A: Algorithm<C::Instant> = DefaultAlgorithm,
19    C: clock::Clock = clock::DefaultClock,
20> {
21    state: A::BucketState,
22    algorithm: A,
23    clock: C,
24}
25
26impl<A, C> DirectRateLimiter<A, C>
27where
28    C: clock::Clock,
29    A: Algorithm<C::Instant>,
30{
31    /// Construct a new rate limiter that allows `capacity` cells per
32    /// time unit through.
33    /// # Examples
34    /// You can construct a GCRA rate limiter like so:
35    /// ```
36    /// # use std::num::NonZeroU32;
37    /// # use std::time::Duration;
38    /// use ratelimit_meter::{DirectRateLimiter, GCRA};
39    /// # #[macro_use] extern crate nonzero_ext;
40    /// # extern crate ratelimit_meter;
41    /// # fn main () {
42    /// let _gcra = DirectRateLimiter::<GCRA>::new(nonzero!(100u32), Duration::from_secs(5));
43    /// # }
44    /// ```
45    ///
46    /// and similarly, for a leaky bucket:
47    /// ```
48    /// # use std::time::Duration;
49    /// use ratelimit_meter::{DirectRateLimiter, LeakyBucket};
50    /// # #[macro_use] extern crate nonzero_ext;
51    /// # extern crate ratelimit_meter;
52    /// # fn main () {
53    /// let _lb = DirectRateLimiter::<LeakyBucket>::new(nonzero!(100u32), Duration::from_secs(5));
54    /// # }
55    /// ```
56    pub fn new(capacity: NonZeroU32, per_time_unit: Duration) -> Self {
57        DirectRateLimiter {
58            state: <A as Algorithm<C::Instant>>::BucketState::default(),
59            algorithm: <A as Algorithm<C::Instant>>::construct(
60                capacity,
61                nonzero!(1u32),
62                per_time_unit,
63            )
64            .unwrap(),
65            clock: Default::default(),
66        }
67    }
68
69    /// Construct a new rate limiter that allows `capacity` cells per
70    /// second.
71    /// # Examples
72    /// Constructing a GCRA rate limiter that lets through 100 cells per second:
73    /// ```
74    /// # use std::time::Duration;
75    /// use ratelimit_meter::{DirectRateLimiter, GCRA};
76    /// # #[macro_use] extern crate nonzero_ext;
77    /// # extern crate ratelimit_meter;
78    /// # fn main () {
79    /// let _gcra = DirectRateLimiter::<GCRA>::per_second(nonzero!(100u32));
80    /// # }
81    /// ```
82    ///
83    /// and a leaky bucket:
84    /// ```
85    /// # use std::time::Duration;
86    /// use ratelimit_meter::{DirectRateLimiter, LeakyBucket};
87    /// # #[macro_use] extern crate nonzero_ext;
88    /// # extern crate ratelimit_meter;
89    /// # fn main () {
90    /// let _gcra = DirectRateLimiter::<LeakyBucket>::per_second(nonzero!(100u32));
91    /// # }
92    /// ```
93    pub fn per_second(capacity: NonZeroU32) -> Self {
94        Self::new(capacity, Duration::from_secs(1))
95    }
96
97    /// Return a builder that can be used to construct a rate limiter using
98    /// the parameters passed to the Builder.
99    pub fn build_with_capacity(capacity: NonZeroU32) -> Builder<C, A> {
100        Builder {
101            capacity,
102            cell_weight: nonzero!(1u32),
103            time_unit: Duration::from_secs(1),
104            end_result: PhantomData,
105            clock: Default::default(),
106        }
107    }
108
109    /// Tests whether a single cell can be accommodated at the given
110    /// time stamp. See [`check`](#method.check).
111    pub fn check_at(
112        &mut self,
113        at: C::Instant,
114    ) -> Result<(), <A as Algorithm<C::Instant>>::NegativeDecision> {
115        self.algorithm.test_and_update(&self.state, at)
116    }
117
118    /// Tests if `n` cells can be accommodated at the given time
119    /// (`Instant::now()`), using [`check_n`](#method.check_n)
120    pub fn check_n_at(
121        &mut self,
122        n: u32,
123        at: C::Instant,
124    ) -> Result<(), NegativeMultiDecision<<A as Algorithm<C::Instant>>::NegativeDecision>> {
125        self.algorithm.test_n_and_update(&self.state, n, at)
126    }
127
128    /// Tests if a single cell can be accommodated at the clock's
129    /// current reading. If it can be, `check` updates the rate
130    /// limiter state to account for the conforming cell and returns
131    /// `Ok(())`.
132    ///
133    /// If the cell is non-conforming (i.e., it can't be accomodated
134    /// at this time stamp), `check_at` returns `Err` with information
135    /// about the earliest time at which a cell could be considered
136    /// conforming.
137    pub fn check(&mut self) -> Result<(), <A as Algorithm<C::Instant>>::NegativeDecision> {
138        self.algorithm
139            .test_and_update(&self.state, self.clock.now())
140    }
141
142    /// Tests if `n` cells can be accommodated at the clock's current
143    /// reading. If (and only if) all cells in the batch can be
144    /// accomodated, the `MultiDecider` updates the internal state to
145    /// account for all cells and returns `Ok(())`.
146    ///
147    /// If the entire batch of cells would not be conforming but the
148    /// rate limiter has the capacity to accomodate the cells at any
149    /// point in time, `check_n_at` returns error
150    /// [`NegativeMultiDecision::BatchNonConforming`](../../enum.NegativeMultiDecision.html#variant.BatchNonConforming),
151    /// holding the number of cells the rate limiter's negative
152    /// outcome result.
153    ///
154    /// If `n` exceeds the bucket capacity, `check_n_at` returns
155    /// [`NegativeMultiDecision::InsufficientCapacity`](../../enum.NegativeMultiDecision.html#variant.InsufficientCapacity),
156    /// indicating that a batch of this many cells can never succeed.
157    pub fn check_n(
158        &mut self,
159        n: u32,
160    ) -> Result<(), NegativeMultiDecision<<A as Algorithm<C::Instant>>::NegativeDecision>> {
161        self.algorithm
162            .test_n_and_update(&self.state, n, self.clock.now())
163    }
164}
165
166/// An object that allows incrementally constructing rate Limiter
167/// objects.
168pub struct Builder<C, A>
169where
170    C: clock::Clock,
171    A: Algorithm<C::Instant> + Sized,
172{
173    capacity: NonZeroU32,
174    cell_weight: NonZeroU32,
175    time_unit: Duration,
176    end_result: PhantomData<A>,
177    clock: C,
178}
179
180impl<C, A> Builder<C, A>
181where
182    C: clock::Clock,
183    A: Algorithm<C::Instant> + Sized,
184{
185    /// Sets the "weight" of each cell being checked against the
186    /// bucket. Each cell fills the bucket by this much.
187    pub fn cell_weight(
188        &mut self,
189        weight: NonZeroU32,
190    ) -> Result<&mut Builder<C, A>, InconsistentCapacity> {
191        if self.cell_weight > self.capacity {
192            return Err(InconsistentCapacity::new(self.capacity, self.cell_weight));
193        }
194        self.cell_weight = weight;
195        Ok(self)
196    }
197
198    /// Sets the "unit of time" within which the bucket drains.
199    ///
200    /// The assumption is that in a period of `time_unit` (if no cells
201    /// are being checked), the bucket is fully drained.
202    pub fn per(&mut self, time_unit: Duration) -> &mut Builder<C, A> {
203        self.time_unit = time_unit;
204        self
205    }
206
207    /// Sets the clock used by the bucket.
208    pub fn using_clock(&mut self, clock: C) -> &mut Builder<C, A> {
209        self.clock = clock;
210        self
211    }
212
213    /// Builds a rate limiter of the specified type.
214    pub fn build(&self) -> Result<DirectRateLimiter<A, C>, InconsistentCapacity> {
215        Ok(DirectRateLimiter {
216            state: <A as Algorithm<C::Instant>>::BucketState::default(),
217            algorithm: <A as Algorithm<C::Instant>>::construct(
218                self.capacity,
219                self.cell_weight,
220                self.time_unit,
221            )?,
222            clock: self.clock.clone(),
223        })
224    }
225}