distkit
Rate limiting

Rate limiting

Sliding-window rate limiting via the trypema crate, re-exported under distkit::trypema.

distkit's rate limiting is the trypema crate, re-exported in full under distkit::trypema. Enable the trypema feature to pull it in:

[dependencies]
distkit = { version = "0.5", 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.

Full provider, strategy, and tuning documentation lives on the trypema docs site. This page is a quick on-ramp.

What it offers

  • Sliding-window limiting with configurable window size and rate (rates are f64, so 0.5 req/s is valid).
  • Three providers - local (in-process), redis (distributed, one round-trip per call), and hybrid (local fast-path with periodic Redis sync).
  • Two strategies - absolute (deterministic allow/reject) and suppressed (probabilistic shedding as you approach capacity).

Local rate limiting

use std::sync::Arc;
use distkit::trypema::{
    HardLimitFactor, RateGroupSizeMs, RateLimit, RateLimitDecision,
    RateLimiter, RateLimiterOptions, SuppressionFactorCacheMs, WindowSizeSeconds,
    local::LocalRateLimiterOptions,
};

let rl = Arc::new(RateLimiter::new(RateLimiterOptions {
    local: LocalRateLimiterOptions {
        window_size_seconds: WindowSizeSeconds::try_from(60).unwrap(),
        rate_group_size_ms: RateGroupSizeMs::try_from(100).unwrap(),
        hard_limit_factor: HardLimitFactor::default(),
        suppression_factor_cache_ms: SuppressionFactorCacheMs::default(),
    },
}));

rl.run_cleanup_loop();

let rate = RateLimit::try_from(10.0).unwrap(); // 10 requests per second

match rl.local().absolute().inc("user_123", &rate, 1) {
    RateLimitDecision::Allowed => { /* process the request */ }
    RateLimitDecision::Rejected { retry_after_ms, .. } => {
        eprintln!("rate limited, retry in {retry_after_ms} ms");
    }
    _ => {}
}

Distributed rate limiting

For enforcement shared across processes, add the Redis provider options and call redis() (one round-trip per call) or hybrid() (local fast-path, periodic sync):

use distkit::trypema::{
    redis::{RedisKey, RedisRateLimiterOptions},
    hybrid::SyncIntervalMs,
};

// ... build RateLimiterOptions with both `local` and `redis` set ...

let key = RedisKey::try_from("user_123".to_string())?;
let rate = RateLimit::try_from(50.0)?;

let decision = rl.redis().absolute().inc(&key, &rate, 1).await?;
// or, for the highest-throughput distributed option:
let decision = rl.hybrid().absolute().inc(&key, &rate, 1).await?;

Redis-backed providers require Redis 7.2+. For the complete configuration surface - suppression tuning, hybrid sync intervals, and the strategy internals - see the trypema documentation.