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
//! In-process rate limiting provider.
//!
//! The local provider maintains rate limiting state within the current process using
//! thread-safe data structures ([`DashMap`](dashmap::DashMap) and atomics).
//!
//! # Key Characteristics
//!
//! - **Thread-safe:** Safe for concurrent use across multiple threads
//! - **Zero external dependencies:** No network or database required
//! - **Low latency:** Sub-microsecond admission checks (no I/O)
//! - **Process-scoped:** State is not shared across processes
//!
//! # Strategies
//!
//! - [`AbsoluteLocalRateLimiter`]: Strict sliding-window enforcement
//! - [`SuppressedLocalRateLimiter`]: Probabilistic suppression for graceful degradation
//!
//! # When to Use
//!
//! ✅ **Use local provider when:**
//! - Single-process application
//! - Low-latency requirements
//! - No need for distributed coordination
//! - Simple deployment (no Redis/external dependencies)
//!
//! ❌ **Don't use local provider when:**
//! - Multiple application instances need shared limits
//! - Horizontal scaling requires coordinated rate limiting
//! - Rate limits must survive process restarts
//!
//! # Examples
//!
//! ```no_run
//! use trypema::{HardLimitFactor, RateGroupSizeMs, RateLimit, RateLimitDecision, RateLimiter, RateLimiterOptions, SuppressionFactorCacheMs, WindowSizeSeconds};
//! use trypema::local::LocalRateLimiterOptions;
//! # #[cfg(any(feature = "redis-tokio", feature = "redis-smol"))]
//! # use trypema::redis::RedisRateLimiterOptions;
//! # #[cfg(any(feature = "redis-tokio", feature = "redis-smol"))]
//! # use trypema::hybrid::SyncIntervalMs;
//! #
//! # #[cfg(any(feature = "redis-tokio", feature = "redis-smol"))]
//! # fn options() -> RateLimiterOptions {
//! # let window_size_seconds = WindowSizeSeconds::try_from(60).unwrap();
//! # let rate_group_size_ms = RateGroupSizeMs::try_from(10).unwrap();
//! # let hard_limit_factor = HardLimitFactor::default();
//! # let suppression_factor_cache_ms = SuppressionFactorCacheMs::default();
//! # let sync_interval_ms = SyncIntervalMs::default();
//! #
//! # RateLimiterOptions {
//! # local: LocalRateLimiterOptions {
//! # window_size_seconds,
//! # rate_group_size_ms,
//! # hard_limit_factor,
//! # suppression_factor_cache_ms,
//! # },
//! # redis: RedisRateLimiterOptions {
//! # connection_manager: todo!(),
//! # prefix: None,
//! # window_size_seconds,
//! # rate_group_size_ms,
//! # hard_limit_factor,
//! # suppression_factor_cache_ms,
//! # sync_interval_ms,
//! # },
//! # }
//! # }
//! #
//! # #[cfg(not(any(feature = "redis-tokio", feature = "redis-smol")))]
//! # fn options() -> RateLimiterOptions {
//! # let window_size_seconds = WindowSizeSeconds::try_from(60).unwrap();
//! # let rate_group_size_ms = RateGroupSizeMs::try_from(10).unwrap();
//! # let hard_limit_factor = HardLimitFactor::default();
//! # let suppression_factor_cache_ms = SuppressionFactorCacheMs::default();
//! #
//! # RateLimiterOptions {
//! # local: LocalRateLimiterOptions {
//! # window_size_seconds,
//! # rate_group_size_ms,
//! # hard_limit_factor,
//! # suppression_factor_cache_ms,
//! # },
//! # }
//! # }
//!
//! let rl = RateLimiter::new(options());
//!
//! let rate = RateLimit::try_from(10.0).unwrap();
//!
//! // Absolute strategy: strict enforcement
//! let decision = rl.local().absolute().inc("user_123", &rate, 1);
//!
//! // Suppressed strategy: probabilistic suppression
//! let decision = rl.local().suppressed().inc("api_endpoint", &rate, 1);
//! ```
pub use *;
pub use RateLimitSeries as AbsoluteRateLimitSeries;
pub use *;
pub use *;