Getting Started
Quickstart (Local)
In-process rate limiting with the local provider.
This page shows the local (in-process) provider. It is synchronous and fast, but state is not shared across processes.
Create a limiter
use std::sync::Arc;
use trypema::{HardLimitFactor, RateGroupSizeMs, RateLimiter, RateLimiterOptions, SuppressionFactorCacheMs, WindowSizeSeconds};
use trypema::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(10).unwrap(),
hard_limit_factor: HardLimitFactor::try_from(1.5).unwrap(),
suppression_factor_cache_ms: SuppressionFactorCacheMs::default(),
},
}));
// Optional but recommended if you have many keys.
rl.run_cleanup_loop();
Local state is per-process. If you run multiple instances and need a shared limit, use the Redis provider.
Absolute strategy
Use absolute when you want deterministic behavior: allowed under capacity, rejected over capacity.
use trypema::{RateLimit, RateLimitDecision};
let rate = RateLimit::try_from(5.0).unwrap();
match rl.local().absolute().inc("user_123", &rate, 1) {
RateLimitDecision::Allowed => {
// proceed
}
RateLimitDecision::Rejected { retry_after_ms, .. } => {
let _ = retry_after_ms;
// backoff
}
RateLimitDecision::Suppressed { .. } => unreachable!(),
}
Read-only check (preview)
If you want to check before doing expensive work, use is_allowed() (does not increment):
use trypema::{RateLimit, RateLimitDecision};
let rate = RateLimit::try_from(5.0).unwrap();
match rl.local().absolute().is_allowed("user_123") {
RateLimitDecision::Allowed => {
// do expensive work, then record the request
let _ = rl.local().absolute().inc("user_123", &rate, 1);
}
RateLimitDecision::Rejected { retry_after_ms, .. } => {
let _ = retry_after_ms;
// deny / delay work
}
RateLimitDecision::Suppressed { .. } => unreachable!(),
}
Batch increments
count lets you represent a batch (e.g. N messages consumed):
use trypema::{RateLimit, RateLimitDecision};
let rate = RateLimit::try_from(100.0).unwrap();
match rl.local().absolute().inc("tenant_42", &rate, 10) {
RateLimitDecision::Allowed => {}
RateLimitDecision::Rejected { retry_after_ms, .. } => {
let _ = retry_after_ms;
}
RateLimitDecision::Suppressed { .. } => unreachable!(),
}
Suppressed strategy
Use suppressed when you want graceful degradation under spikes. Near/over capacity it returns
RateLimitDecision::Suppressed and you must gate on is_allowed.
use trypema::{RateLimit, RateLimitDecision};
let rate = RateLimit::try_from(5.0).unwrap();
match rl.local().suppressed().inc("user_123", &rate, 1) {
RateLimitDecision::Allowed => {
// below capacity
}
RateLimitDecision::Suppressed { is_allowed: true, .. } => {
// suppression active, admitted
}
RateLimitDecision::Suppressed { is_allowed: false, .. } => {
// suppressed: do not proceed
}
RateLimitDecision::Rejected { .. } => {
// over hard limit
}
}
For the suppressed strategy,
is_allowed is the admission signal. Do not treat “variant == Suppressed” as denial.
