governor/_guide.rs
1//! # A more in-depth guide to `governor`
2//!
3//! Governor is a fork/rewrite/rebranding of the
4//! [`ratelimit_meter`](https://crates.io/crates/ratelimit_meter) and
5//! [`ratelimit_futures`](https://crates.io/crates/ratelimit_futures)
6//! crates. Many of the things that worked there still work here, and
7//! this guide's aim is to help you accomplish them.
8//!
9//! # Constructing a rate limiter
10//!
11//! If you're used to `ratelimit_meter` parlance, both "direct" and
12//! "keyed" rate limiters are supported. Direct rate limiters keep only a
13//! single state (such as the rate of outflowing e-mail
14//! conversations). Keyed rate limiters on the other hand have one rate
15//! limiting state per key: e.g., the flow of API requests made by each
16//! customer.
17//!
18//! Construction of rate limiters is designed to be mostly infallible via
19//! types that ensure you can't pass the wrong parameters, mostly around
20//! non-zero integers. Since that kind of checking is a little tedious to
21//! do by hand, `governor` makes heavy use of the
22//! [`NonZeroU32`][std::num::NonZeroU32] type.
23//!
24//! To conveniently construct these nonzero numbers yourself, use the
25//! [`nonzero!`](../../nonzero_ext/macro.nonzero.html) macro.
26//!
27//! #### Quotas
28//!
29//! Each rate limiter has a quota: A rate of elements (could be API
30//! requests, emails, phone calls... anything really) per unit of time (second,
31//! minute, hour). Specify these in a [`Quota`][crate::Quota] object like so:
32//!
33//! ```rust
34//! # use nonzero_ext::*;
35//! use governor::Quota;
36//! Quota::per_second(nonzero!(20u32));
37//! ```
38//!
39//! #### Constructing a direct rate limiter
40//!
41//! To make a direct rate limiter, you have to construct a quota, as
42//! above; and then use this to construct the rate limiter itself. In
43//! `std` mode, this is easily accomplished like so:
44//!
45//! ```rust
46//! # #[cfg(feature = "std")] fn main() {
47//! # use nonzero_ext::*;
48//! # use governor::{RateLimiter, Quota};
49//! RateLimiter::direct(Quota::per_second(nonzero!(50u32)));
50//! # } #[cfg(not(feature = "std"))] fn main() {}
51//! ```
52//!
53//! In `no_std` mode, there are is no default monotonic (or system)
54//! clock available. To effectively limit rates, you will have to
55//! either use the provided "fake" clock (which must be manually
56//! advanced, and is mainly useful for tests), or implement the
57//! `Clock` trait for your platform. Once that decision is made,
58//! constructing a rate limiter with an explicit clock works like
59//! this:
60//!
61//! ```rust
62//! # use nonzero_ext::*;
63//! # use governor::{clock::FakeRelativeClock, RateLimiter, Quota};
64//! let clock = FakeRelativeClock::default();
65//! RateLimiter::direct_with_clock(Quota::per_second(nonzero!(50u32)), &clock);
66//! ```
67//!
68//! #### Constructing a keyed rate limiter
69//!
70//! For a keyed rate limiter, you have to specify the type of the key:
71//! Otherwise they function exactly as their direct counterpart. They are
72//! stored in a hash-table like state store. The default in `std` mode is
73//! provided by the [`dashmap`](https://docs.rs/dashmap) crate:
74//!
75//! ```rust
76//! # use nonzero_ext::*;
77//! # use governor::{RateLimiter, Quota};
78//! let lim = RateLimiter::keyed(Quota::per_second(nonzero!(50u32)));
79//! lim.check_key(&"cus_1").unwrap(); // one key
80//! lim.check_key(&"cus_2").unwrap(); // another!
81//! ```
82//!
83//! You can supply your own keyed state store implementation if you
84//! wish. That requires implementing the
85//! [KeyedStateStore][crate::state::keyed::KeyedStateStore] trait, and optionally the
86//! [ShrinkableKeyedStateStore][crate::state::keyed::ShrinkableKeyedStateStore] trait.
87//!
88//! # Type signatures for rate limiters
89//!
90//! Rate limiters tend to be long-lived, and need to be stored
91//! somewhere - sometimes in struct fields, or even just to pass in
92//! function arguments. The [`crate::RateLimiter`] type signatures
93//! tend to be pretty unwieldy for that, so this crate exports a pair
94//! of handy type aliases, [`crate::DefaultDirectRateLimiter`] and
95//! [`crate::DefaultKeyedRateLimiter`].
96//!
97//! Here's an example for embedding a direct rate limiter in a struct:
98//!
99//! ```rust
100//! # use governor::DefaultDirectRateLimiter;
101//! struct MyApiClient {
102//! limiter: DefaultDirectRateLimiter,
103//! }
104//! ```
105//!
106//! If you need to provide a different clock, or a different
107//! implementation of the keyed state, you will still have to fall
108//! back to the regular type.
109//!
110//! # Data ownership and references to rate limiters
111//!
112//! `governor`'s rate limiter state is not hidden behind an [interior
113//! mutability](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html)
114//! pattern, and so it is perfectly valid to have multiple references
115//! to a rate limiter in a program. Since its state lives in
116//! [`AtomicU64`][std::sync::atomic::AtomicU64] integers (which do not
117//! implement [`Clone`]), the rate limiters themselves can not be
118//! cloned.
119//!
120//! # Usage in multiple threads
121//!
122//! Sharing references to a rate limiter across threads is completely
123//! OK (rate limiters are Send and Sync by default), but there is a
124//! problem: A rate limiter's lifetime might be up before a thread
125//! ends, which would invalidate the reference.
126//!
127//! So, to use a rate limiter in multiple threads without lifetime
128//! issues, there are two equally valid strategies:
129//!
130//! #### `crossbeam` scoped tasks
131//!
132//! The `crossbeam` crate's
133//! [scopes](https://docs.rs/crossbeam/0.7.3/crossbeam/thread/struct.Scope.html#method.spawn)
134//! allow code to guarantee that a thread spawned in a scope
135//! terminates before the scope terminates. This allows using
136//!
137//! stack-allocated variables. Here is an example test using crossbeam
138//! scopes:
139//!
140//! ```rust
141//! # use crossbeam;
142//! # use nonzero_ext::*;
143//! # use governor::{clock::FakeRelativeClock, RateLimiter, Quota};
144//! # use std::time::Duration;
145//!
146//! let mut clock = FakeRelativeClock::default();
147//! let lim = RateLimiter::direct_with_clock(Quota::per_second(nonzero!(20u32)), &clock);
148//! let ms = Duration::from_millis(1);
149//!
150//! crossbeam::scope(|scope| {
151//! for _i in 0..20 {
152//! scope.spawn(|_| {
153//! assert_eq!(Ok(()), lim.check());
154//! });
155//! }
156//! })
157//! .unwrap();
158//! ```
159//!
160//! #### Wrapping the limiter in an [`Arc`][std::sync::Arc]
161//!
162//! The other method uses only the standard library: Wrapping the rate
163//! limiter in an [`Arc`][std::sync::Arc] will keep the limiter alive
164//! for as long as there exist references to it - perfect for passing
165//! to threads.
166//!
167//! In this example, note that we're cloning the
168//! [`Arc`][std::sync::Arc]; the rate limiter stays identical (rate
169//! limiters do not implement [`Clone`]), only its references are
170//! duplicated (and refcounts incremented atomically).
171//!
172//! Note also the placement of the clone: As we're creating a `move`
173//! closure, a binding that can be moved into the closure must be set
174//! up outside it. Rustc will be upset at you if you try to clone the
175//! Arc too early outside the closure, or even inside it. See the
176//! [`Arc`][std::sync::Arc] docs for some more usage examples.
177//!
178//! ```rust
179//! # #[cfg(feature = "std")] fn main() {
180//! # use nonzero_ext::*;
181//! # use governor::{RateLimiter, Quota};
182//! # use std::time::Duration;
183//! # use std::sync::Arc;
184//! # use std::thread;
185//! let bucket = Arc::new(RateLimiter::direct(Quota::per_second(nonzero!(20u32))));
186//! for _i in 0..20 {
187//! let bucket = Arc::clone(&bucket);
188//! thread::spawn(move || {
189//! assert_eq!(Ok(()), bucket.check());
190//! })
191//! .join()
192//! .unwrap();
193//! }
194//! # } #[cfg(not(feature = "std"))] fn main() {}
195//! ```
196//!