Keys
Every rate limiting operation in Trypema targets a specific key -- a string that identifies the resource being limited. Each key maintains completely independent rate limiting state: its own sliding window, counters, and rate limit.
What to use as a key
Keys typically represent the entity you want to throttle. Common patterns:
| Pattern | Example key | Use case |
|---|---|---|
| User ID | "user_123" | Per-user API rate limits |
| IP address | "192.168.1.100" | Per-IP abuse prevention |
| Endpoint | "POST_/api/upload" | Per-endpoint throttling |
| Composite | "user_123_POST_/api/upload" | Per-user per-endpoint limits |
You can use any string you like. Trypema does not interpret the key -- it is just an identifier.
Local provider keys
For the local provider, keys are arbitrary &str values. Any non-empty string is valid:
use trypema::{RateLimit, RateLimitDecision};
// Assume `rl` is a configured RateLimiter (see Quickstart)
// These are all valid local keys:
// rl.local().absolute().inc("user_123", &rate, 1);
// rl.local().absolute().inc("192.168.1.100", &rate, 1);
// rl.local().absolute().inc("any string at all", &rate, 1);
let rate = RateLimit::try_from(10.0).unwrap();
let _ = rate;
Redis and Hybrid provider keys
The Redis and Hybrid providers use a validated newtype called RedisKey. This is because Redis keys are constructed from the user key, and certain characters or lengths would break the internal key schema.
Validation rules
- Must not be empty
- Must be <= 255 bytes
- Must not contain
:(colon) -- the colon is used internally as a key separator
Creating a RedisKey
use trypema::redis::RedisKey;
// Valid keys
let key = RedisKey::try_from("user_123".to_string()).unwrap();
let key2 = RedisKey::try_from("api_v2_endpoint".to_string()).unwrap();
// Invalid: contains ':'
assert!(RedisKey::try_from("user:123".to_string()).is_err());
// Invalid: empty string
assert!(RedisKey::try_from("".to_string()).is_err());
If validation fails, RedisKey::try_from returns Err(TrypemaError::InvalidRedisKey(...)).
Default prefix
In Redis, all keys are namespaced under a configurable prefix (default: "trypema"). The full Redis key pattern is:
{prefix}:{user_key}:{rate_type}:{suffix}
For example, a key "user_123" using the absolute strategy with the default prefix becomes Redis keys like trypema:user_123:absolute:h, trypema:user_123:absolute:t, etc.
This is why the : character is not allowed in user keys -- it would break the key structure.
Sticky rate limits
The first inc() call for a given key stores the rate limit for that key's lifetime in the limiter. Subsequent inc() calls for the same key use the stored limit and ignore the rate_limit argument.
This prevents races where concurrent callers might specify different limits for the same key. If you need to change a key's rate limit, you must let the old entry expire (or be cleaned up) and start fresh.
use trypema::RateLimit;
let rate_10 = RateLimit::try_from(10.0).unwrap();
let rate_20 = RateLimit::try_from(20.0).unwrap();
// First call for "user_123" stores 10 req/s
// rl.local().absolute().inc("user_123", &rate_10, 1);
// This call still uses 10 req/s — rate_20 is ignored
// rl.local().absolute().inc("user_123", &rate_20, 1);
Next steps
- Rate Limits -- how rate limits and window capacity work
- Sliding Windows -- time windows and bucket coalescing
- Decisions -- understand the three decision types

