Rate limiting
distkit's rate limiting is trypema 2, re-exported in full under distkit::trypema. Enable the trypema feature to pull it in:
[dependencies]
distkit = { version = "0.7", features = ["trypema"] }
If you're only doing rate limiting, you can also depend on trypema directly - the distkit re-export is for projects that want it alongside the other primitives.

What it offers
- Sliding-window limiting with semantic, unit-aware rate, window, and bucket types.
- Three independently constructed providers - local (in-process), Redis (one atomic Redis operation per call), and hybrid (local fast-path with periodic Redis sync).
- Two strategies -
absolute(deterministic allow/reject) andsuppressed(probabilistic shedding as you approach capacity). - Live reads and conditional updates across every provider and strategy.
Local rate limiting
The local provider is synchronous and does not contact Redis.
use distkit::trypema::{
BucketSize, RateLimit, RateLimitDecision, RateLimiterBuilder, WindowSize,
local::LocalRateLimiterProvider,
};
let provider = LocalRateLimiterProvider::builder()
.window_size(WindowSize::minutes_or_panic(1))
.bucket_size(BucketSize::milliseconds_or_panic(100))
.build()
.unwrap();
let rate = RateLimit::per_second_or_panic(10.0);
match provider.absolute().inc("user_123", &rate, 1) {
RateLimitDecision::Allowed => { /* process the request */ }
RateLimitDecision::Rejected { retry_after, .. } => {
eprintln!("rate limited, retry in {retry_after:?}");
}
RateLimitDecision::Suppressed { .. } => unreachable!(),
}
All provider builders implement RateLimiterBuilder. build() returns an Arc and starts stale-state cleanup by default. Use .disable_cleanup() while building to opt out, or call the provider's idempotent start_cleanup_loop() and stop_cleanup_loop() methods after construction.
Redis rate limiting
Use the Redis provider when multiple processes must operate on shared rate-limit state. Each operation performs one Redis round-trip.
use distkit::trypema::{
BucketSize, RateLimit, RateLimitDecision, RateLimiterBuilder, WindowSize,
redis::{RedisKey, RedisRateLimiterProvider},
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let connection = redis::Client::open("redis://127.0.0.1:6379/")?
.get_connection_manager()
.await?;
let provider = RedisRateLimiterProvider::builder(connection)
.prefix(RedisKey::try_from("my-service")?)
.window_size(WindowSize::minutes(1)?)
.bucket_size(BucketSize::milliseconds(100)?)
.build()?;
let key = RedisKey::try_from("user_123")?;
let rate = RateLimit::per_second(50.0)?;
let decision = provider.absolute().inc(&key, &rate, 1).await?;
assert!(matches!(decision, RateLimitDecision::Allowed));
Ok(())
}
Hybrid rate limiting
Use the hybrid provider when distributed visibility matters but per-request Redis I/O is too expensive. Admission uses a local fast path and a background worker periodically synchronizes with Redis.
use distkit::trypema::{
RateLimit, RateLimitDecision, RateLimiterBuilder,
hybrid::{HybridRateLimiterProvider, SyncInterval},
redis::RedisKey,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let connection = redis::Client::open("redis://127.0.0.1:6379/")?
.get_connection_manager()
.await?;
let provider = HybridRateLimiterProvider::builder(connection)
.prefix(RedisKey::try_from("my-service")?)
.sync_interval(SyncInterval::milliseconds(10)?)
.build()?;
let key = RedisKey::try_from("user_123")?;
let rate = RateLimit::per_second(50.0)?;
let decision = provider.absolute().inc(&key, &rate, 1).await?;
assert!(matches!(decision, RateLimitDecision::Allowed));
Ok(())
}
The hybrid synchronization worker always runs independently of optional stale-state cleanup. Redis and hybrid providers require Redis 7.2+; the Distkit trypema feature enables Trypema's redis-tokio integration.
Migrating from Trypema 1
distkit::trypema users must construct one provider directly and import the RateLimiterBuilder trait.| Trypema 1 | Trypema 2 |
|---|---|
RateLimiter and RateLimiterOptions | LocalRateLimiterProvider, RedisRateLimiterProvider, or HybridRateLimiterProvider |
LocalRateLimiterOptions / RedisRateLimiterOptions | Provider-specific builders |
WindowSizeSeconds | WindowSize |
RateGroupSizeMs | BucketSize |
SuppressionFactorCacheMs | SuppressionFactorCachePeriod |
SyncIntervalMs | SyncInterval |
RateLimit::try_from(10.0) | RateLimit::per_second(10.0) |
Rejected { retry_after_ms, .. } | Rejected { retry_after: Duration, .. } |
The trypema feature name and distkit::trypema module path do not change. For live reads, conditional updates, suppression tuning, and the complete configuration surface, see the Trypema documentation.

