Getting Started

Quickstart (Local)

Start with synchronous, in-process rate limiting and no Redis dependency.

Use local provider when one process owns limiter state.

Three paper-craft rate-limiter topologies showing local state, direct shared Redis state, and local state synchronized to Redis.
Local keeps state in one process; Redis centralizes every call; hybrid periodically synchronizes local state to Redis.

Build provider

use 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(10))
    .build()
    .unwrap();

let rate = RateLimit::per_second_or_panic(5.0);
assert!(matches!(
    provider.absolute().inc("user_123", &rate, 1),
    RateLimitDecision::Allowed
));

build() returns an Arc and starts stale-state cleanup. Use .disable_cleanup() during construction when cleanup is unwanted. After construction, start_cleanup_loop() and stop_cleanup_loop() are idempotent.

Read live state

let absolute_total: u64 = provider.absolute().get("user_123");
let suppressed = provider.suppressed().get("user_123");

assert_eq!(absolute_total, 1);
assert_eq!(suppressed.total, 0);
assert_eq!(suppressed.total_declined, 0);

Unknown keys return zero-valued results without creating state. Reads include only live buckets and may lazily evict expired history. is_allowed(...) checks absolute admission without recording usage; get_suppression_factor(...) reads suppressed pressure.

Next steps