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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! # 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
//!
//! **Stable (v1.0).** The public API is frozen until 2.0. The limiter and resilience surface:
//! 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`]; adaptive concurrency — an `AdaptiveLimiter` that discovers
//! the right in-flight limit from outcome feedback (`adaptive` feature); provider
//! integration — response-header parsers with limiter sync (`provider-headers`
//! feature) and LLM tier `presets` (`provider-llm` feature); and observability —
//! metrics and tracing events, feature-gated and zero-cost when off (`metrics`,
//! `tracing` features).
//!
//! The waiting surface runs on either [`tokio`](crate#feature-flags) or
//! [`smol`](crate#feature-flags) — the async code is runtime-agnostic, and you
//! choose the timer backend by feature. With `std` off, the pure algorithm core
//! ([`Backoff`], [`Jitter`], [`Decision`]) compiles `no_std`. Correctness is held
//! by property tests for every limiter invariant, a `loom` model check of the
//! lock-free slot accounting, and fuzzed parsers.
//!
//! ```
//! # #[cfg(feature = "runtime")]
//! # 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 — the limiter surface. With it off the crate is `no_std` and exposes the pure algorithm core ([`Backoff`], [`Jitter`], [`Decision`]) plus [`VERSION`]. |
//! | `tokio` | yes | tokio timer backend for the waiting [`acquire`](Throttle::acquire) surface. Implies `std`. |
//! | `smol` | no | smol timer backend, as an alternative to `tokio`. (async-std is unsupported — it is discontinued.) |
//!
//! The `circuit-breaker`, `adaptive`, `provider-headers` / `provider-llm`, and
//! `metrics` / `tracing` features are documented in `docs/API.md`, which carries
//! the full feature matrix.
// `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`.
// `backoff` and `decision` are the `no_std`-capable algorithm core: pure types
// and math, no clock or async. The rest of the surface requires `std`.
// Loom-aware atomics indirection for the adaptive limiter's slot accounting.
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!;