/rust/registry/src/index.crates.io-6f17d22bba15001f/governor-0.8.0/src/state.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! State stores for rate limiters |
2 | | |
3 | | use std::{marker::PhantomData, prelude::v1::*}; |
4 | | |
5 | | pub mod direct; |
6 | | mod in_memory; |
7 | | pub mod keyed; |
8 | | |
9 | | pub use self::in_memory::InMemoryState; |
10 | | |
11 | | use crate::nanos::Nanos; |
12 | | use crate::{clock, Quota}; |
13 | | use crate::{ |
14 | | gcra::Gcra, |
15 | | middleware::{NoOpMiddleware, RateLimitingMiddleware}, |
16 | | }; |
17 | | |
18 | | pub use direct::*; |
19 | | |
20 | | /// A way for rate limiters to keep state. |
21 | | /// |
22 | | /// There are two important kinds of state stores: Direct and keyed. The direct kind have only |
23 | | /// one state, and are useful for "global" rate limit enforcement (e.g. a process should never |
24 | | /// do more than N tasks a day). The keyed kind allows one rate limit per key (e.g. an API |
25 | | /// call budget per client API key). |
26 | | /// |
27 | | /// A direct state store is expressed as [`StateStore::Key`] = [`NotKeyed`]. |
28 | | /// Keyed state stores have a |
29 | | /// type parameter for the key and set their key to that. |
30 | | pub trait StateStore { |
31 | | /// The type of key that the state store can represent. |
32 | | type Key; |
33 | | |
34 | | /// Updates a state store's rate limiting state for a given key, using the given closure. |
35 | | /// |
36 | | /// The closure parameter takes the old value (`None` if this is the first measurement) of the |
37 | | /// state store at the key's location, checks if the request an be accommodated and: |
38 | | /// |
39 | | /// * If the request is rate-limited, returns `Err(E)`. |
40 | | /// * If the request can make it through, returns `Ok(T)` (an arbitrary positive return |
41 | | /// value) and the updated state. |
42 | | /// |
43 | | /// It is `measure_and_replace`'s job then to safely replace the value at the key - it must |
44 | | /// only update the value if the value hasn't changed. The implementations in this |
45 | | /// crate use `AtomicU64` operations for this. |
46 | | fn measure_and_replace<T, F, E>(&self, key: &Self::Key, f: F) -> Result<T, E> |
47 | | where |
48 | | F: Fn(Option<Nanos>) -> Result<(T, Nanos), E>; |
49 | | } |
50 | | |
51 | | /// A rate limiter. |
52 | | /// |
53 | | /// This is the structure that ties together the parameters (how many cells to allow in what time |
54 | | /// period) and the concrete state of rate limiting decisions. This crate ships in-memory state |
55 | | /// stores, but it's possible (by implementing the [`StateStore`] trait) to make others. |
56 | | #[derive(Debug)] |
57 | | pub struct RateLimiter<K, S, C, MW = NoOpMiddleware> |
58 | | where |
59 | | S: StateStore<Key = K>, |
60 | | C: clock::Clock, |
61 | | MW: RateLimitingMiddleware<C::Instant>, |
62 | | { |
63 | | state: S, |
64 | | gcra: Gcra, |
65 | | clock: C, |
66 | | start: C::Instant, |
67 | | middleware: PhantomData<MW>, |
68 | | } |
69 | | |
70 | | impl<K, S, C, MW> RateLimiter<K, S, C, MW> |
71 | | where |
72 | | S: StateStore<Key = K>, |
73 | | C: clock::Clock, |
74 | | MW: RateLimitingMiddleware<C::Instant>, |
75 | | { |
76 | | /// Creates a new rate limiter from components. |
77 | | /// |
78 | | /// This is the most generic way to construct a rate-limiter; most users should prefer |
79 | | /// [`direct`] or other methods instead. |
80 | 0 | pub fn new(quota: Quota, state: S, clock: C) -> Self { |
81 | 0 | let gcra = Gcra::new(quota); |
82 | 0 | let start = clock.now(); |
83 | 0 | RateLimiter { |
84 | 0 | state, |
85 | 0 | clock, |
86 | 0 | gcra, |
87 | 0 | start, |
88 | 0 | middleware: PhantomData, |
89 | 0 | } |
90 | 0 | } Unexecuted instantiation: <governor::state::RateLimiter<core::option::Option<linkerd_identity::Id>, lock_api::mutex::Mutex<parking_lot::raw_mutex::RawMutex, std::collections::hash::map::HashMap<core::option::Option<linkerd_identity::Id>, governor::state::in_memory::InMemoryState>>, governor::clock::with_std::MonotonicClock, governor::middleware::NoOpMiddleware<std::time::Instant>>>::new Unexecuted instantiation: <governor::state::RateLimiter<governor::state::direct::NotKeyed, governor::state::in_memory::InMemoryState, governor::clock::with_std::MonotonicClock, governor::middleware::NoOpMiddleware<std::time::Instant>>>::new |
91 | | |
92 | | /// Consumes the `RateLimiter` and returns the state store. |
93 | | /// |
94 | | /// This is mostly useful for debugging and testing. |
95 | 0 | pub fn into_state_store(self) -> S { |
96 | 0 | self.state |
97 | 0 | } |
98 | | |
99 | | /// Returns a reference to the clock. |
100 | 0 | pub fn clock(&self) -> &C { |
101 | 0 | &self.clock |
102 | 0 | } |
103 | | } |
104 | | |
105 | | impl<K, S, C, MW> RateLimiter<K, S, C, MW> |
106 | | where |
107 | | S: StateStore<Key = K>, |
108 | | C: clock::Clock, |
109 | | MW: RateLimitingMiddleware<C::Instant>, |
110 | | { |
111 | | /// Convert the given rate limiter into one that uses a different middleware. |
112 | 0 | pub fn with_middleware<Outer: RateLimitingMiddleware<C::Instant>>( |
113 | 0 | self, |
114 | 0 | ) -> RateLimiter<K, S, C, Outer> { |
115 | 0 | RateLimiter { |
116 | 0 | middleware: PhantomData, |
117 | 0 | state: self.state, |
118 | 0 | gcra: self.gcra, |
119 | 0 | clock: self.clock, |
120 | 0 | start: self.start, |
121 | 0 | } |
122 | 0 | } |
123 | | } |
124 | | |
125 | | #[cfg(feature = "std")] |
126 | | impl<K, S, C, MW> RateLimiter<K, S, C, MW> |
127 | | where |
128 | | S: StateStore<Key = K>, |
129 | | C: clock::ReasonablyRealtime, |
130 | | MW: RateLimitingMiddleware<C::Instant>, |
131 | | { |
132 | 0 | pub(crate) fn reference_reading(&self) -> C::Instant { |
133 | 0 | self.clock.reference_point() |
134 | 0 | } |
135 | | } |
136 | | |
137 | | #[cfg(all(feature = "std", test))] |
138 | | mod test { |
139 | | use super::*; |
140 | | use crate::Quota; |
141 | | use all_asserts::assert_gt; |
142 | | use nonzero_ext::nonzero; |
143 | | |
144 | | #[test] |
145 | | fn ratelimiter_impl_coverage() { |
146 | | let lim = RateLimiter::direct(Quota::per_second(nonzero!(3u32))); |
147 | | assert_gt!(format!("{:?}", lim).len(), 0); |
148 | | } |
149 | | } |