Concepts

Keys

How rate limiting keys work in Trypema — string identifiers for the local provider and validated RedisKey for Redis/Hybrid.

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:

PatternExample keyUse 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