1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//! # throttle-net
//!
//! Outbound throttling and resilience. Where `rate-net` protects your service
//! from being overwhelmed (inbound), throttle-net protects your service from
//! *overwhelming* the downstreams it calls — and from being banned by them. The
//! defining operation is therefore to **wait**, not to reject: you pace your own
//! outbound work rather than dropping someone else's request.
//!
//! throttle-net does not reimplement token-bucket accounting. It consumes
//! [`better-bucket`](https://crates.io/crates/better-bucket) for that and reads
//! time from [`clock-lib`](https://crates.io/crates/clock-lib), then builds the
//! waiting, cost-aware, composable surface on top. It is the outbound companion
//! to [`rate-net`](https://crates.io/crates/rate-net).
//!
//! ## Status
//!
//! **Pre-1.0 (v0.5).** The limiter and resilience surface so far: the [`Limiter`]
//! trait, the [`Throttle`] token bucket and the exact [`SlidingWindowLog`], each
//! with a waiting cost-aware [`acquire`](Throttle::acquire); the composites —
//! [`Hybrid`] (must pass all), [`MultiLimiter`] (multi-dimensional budgets),
//! [`PerKey`] (independent per-key state, bounded memory), and [`Layered`]
//! (global / per-key / per-endpoint scopes); standalone [`Retry`]/[`Backoff`]
//! with jittered backoff and `Retry-After` parsing; the resilience layer —
//! a `CircuitBreaker` that wraps any limiter and fails fast (`circuit-breaker`
//! feature), and a deadline-aware, priority [`Queue`]; and adaptive concurrency —
//! an `AdaptiveLimiter` that discovers the right in-flight limit from outcome
//! feedback via AIMD or Vegas (`adaptive` feature). Provider presets and
//! observability land across the rest of the 0.x series. The public API is
//! frozen at 1.0.
//!
//! ```
//! # #[cfg(feature = "tokio")]
//! # async fn run() -> Result<(), throttle_net::ThrottleError> {
//! use throttle_net::Throttle;
//!
//! // 100 requests per second, bursting up to 100.
//! let throttle = Throttle::per_second(100);
//!
//! // Pace an outbound call: returns as soon as a token is free.
//! throttle.acquire().await?;
//! // ... call the downstream ...
//! # Ok(())
//! # }
//! ```
//!
//! When you would rather not wait, ask without blocking:
//!
//! ```
//! # #[cfg(feature = "std")] {
//! use throttle_net::Throttle;
//!
//! let throttle = Throttle::per_second(100);
//! if throttle.try_acquire() {
//! // a token was free — send now
//! }
//! # }
//! ```
//!
//! ## Design goals
//!
//! - **Wait by default.** The Tier-1 [`acquire`](Throttle::acquire) paces the
//! caller; [`try_acquire`](Throttle::try_acquire) is there when you need the
//! non-blocking answer.
//! - **Cost-aware.** Not every request weighs one unit. `acquire_with_cost(n)`
//! takes `n` tokens at once — the basis for the multi-dimensional LLM budgets
//! that arrive with the rest of v0.2.
//! - **Lock-free accounting.** Each acquire is a single atomic compare-and-swap
//! in `better-bucket`; no lock sits on the path.
//! - **Runtime-free core, lazy refill.** Tokens accrue from a monotonic clock on
//! access; there is no background timer thread, and the synchronous core has no
//! async-runtime dependency.
//! - **Composable.** Every limiter is one [`Limiter`]; composites combine them
//! without the call site changing.
//!
//! ## Feature flags
//!
//! | Feature | Default | Description |
//! |---------|---------|-------------|
//! | `std` | yes | Standard library. Gates the limiter surface. With it off the crate is `no_std` and exposes only [`VERSION`]. |
//! | `tokio` | yes | The waiting [`acquire`](Throttle::acquire) surface, driven by tokio's timer. Implies `std`. |
//!
//! See `docs/API.md` for the full feature matrix as later phases land.
// `no_std` for the library build when `std` is off, but always link `std` under
// `test` so the unit-test harness and dev-dependencies have what they need.
// The limiter surface requires the standard library (the clock-driven token
// bucket and the domain error type). With `std` off the crate is `no_std` and
// exposes only `VERSION`.
pub use crate;
pub use crate;
pub use crate;
pub use crateDecision;
pub use crateThrottleError;
pub use crate;
pub use crate;
pub use crate;
pub use crateLimiter;
pub use crate;
pub use cratePerKey;
pub use crate;
pub use crate;
pub use crate;
pub use crateSlidingWindowLog;
pub use crateThrottle;
// The clock seam is part of the public API: [`Throttle::with_clock`] and the
// per-key/composite `with_clock` methods take any [`Clock`], and tests drive a
// [`ManualClock`]. Re-exported so callers need not depend on `clock-lib` directly.
pub use ;
/// The version of this crate, from `Cargo.toml`.
///
/// # Examples
///
/// ```
/// assert!(!throttle_net::VERSION.is_empty());
/// ```
pub const VERSION: &str = env!;