Concepts

Rate Limits

How rate limits are expressed, how window capacity is computed, and how sticky rate limits work.

Requests per second

Rate limits in Trypema are expressed as requests per second using the RateLimit type. This wraps a positive f64, so non-integer rates are fully supported:

use trypema::RateLimit;

// Integer rate: 10 requests per second
let rate = RateLimit::try_from(10.0).unwrap();
assert_eq!(*rate, 10.0);

// Non-integer rate: 0.5 requests per second (one request every 2 seconds)
let slow_rate = RateLimit::try_from(0.5).unwrap();
assert_eq!(*slow_rate, 0.5);

// Non-integer rate: 5.5 requests per second
let fractional = RateLimit::try_from(5.5).unwrap();
assert_eq!(*fractional, 5.5);

// Invalid: must be greater than 0
assert!(RateLimit::try_from(0.0).is_err());
assert!(RateLimit::try_from(-1.0).is_err());

Window capacity

The rate limit alone does not determine how many requests are allowed. Trypema uses a sliding time window (see Sliding Windows), and the actual capacity is:

window_capacity = window_size_seconds * rate_limit

Example: With a 60-second window and a rate limit of 5.0 req/s:

  • Window capacity = 60 * 5.0 = 300 requests

This means: at any point in time, the limiter looks at the last 60 seconds of activity. If fewer than 300 requests have been recorded in that window, the next request is allowed.

More examples

rate_limitwindow_size_secondsWindow capacity
10.060600 requests
0.56030 requests
100.0101000 requests
5.5120660 requests
Internally, counters are integers (u64). For non-integer rates, the effective capacity is computed using floating-point multiplication and then compared against integer counters, so you should treat the limit as approximate at the edges.

Sticky rate limits

The first inc() call for a key stores the rate limit for that key's lifetime in the limiter. Subsequent calls for the same key ignore the rate_limit argument and use the stored value.

This prevents races where concurrent callers might specify different limits for the same key. If you need to change a key's rate limit, let the old entry expire (or be cleaned up by the cleanup loop) and start fresh.

Unlimited rate (for testing)

If you want to track behaviour without enforcing limits (e.g., in tests), use RateLimit::max():

use trypema::RateLimit;

let unlimited = RateLimit::max();
assert_eq!(*unlimited, f64::MAX);

This creates a rate limit so large that no key will ever hit it in practice.

Next steps