Documentation
¶
Overview ¶
Package ratelimit implements a sliding-window-counter rate limiter with a two-phase Reserve/Commit API. Five strategies are supported: sliding-window, fixed-window, token-bucket, leaky-bucket, session-window. Concurrency is a special gauge meter that works with any strategy.
This package is pure — it has no imports from github.com/wyolet/relay/internal. Relay-specific translation (catalog.ResolvedRule → Rule) lives in internal/ratelimit.
Expected kv ops per request:
- Reserve: 1 RunScript call (all counter checks + increments atomic).
- Commit: 1 RunScript call (concurrency decrement + token post-increment).
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrExceeded = errors.New("limit: budget exceeded")
ErrExceeded is the sentinel wrapped by *KeyQuotaExhausted.
Functions ¶
func RegisterScripts ¶
RegisterScripts registers the Go emulators for limit.reserve and limit.commit on the given MemStore. Called once per Limiter constructed from a MemStore.
Types ¶
type ExceededError
deprecated
type ExceededError = KeyQuotaExhausted
ExceededError is an alias kept for backward compatibility with callers that use the old name. New code should use KeyQuotaExhausted directly.
Deprecated: Use KeyQuotaExhausted.
type KeyQuotaExhausted ¶
KeyQuotaExhausted is returned by Reserve when a budget is violated. The name reflects that exceeding a per-key rule is the primary signal this type carries in the post-Pick path (pipeline issue #89).
func (*KeyQuotaExhausted) Error ¶
func (e *KeyQuotaExhausted) Error() string
func (*KeyQuotaExhausted) Unwrap ¶
func (e *KeyQuotaExhausted) Unwrap() error
type Limiter ¶
type Limiter struct {
// contains filtered or unexported fields
}
Limiter enforces rate-limit rules using pkg/kv for counters. One Limiter is shared across the process; concurrent calls are safe.
func (*Limiter) Commit ¶
func (l *Limiter) Commit(ctx context.Context, res *Reservation, obs Observations) error
Commit finalizes a Reservation via one RunScript call. Tokens are incremented post-hoc; concurrency is always decremented. Calling Commit twice is a no-op.
obs.Tokens is a map[string]int64. Per-meter increments are derived as:
- meter "tokens": sum of all values in obs.Tokens (backward compat)
- meter "tokens.<key>": obs.Tokens["<key>"]
- meter "requests": always 1 (counted at Reserve; not post-hoc)
- meter "concurrency": decremented (not incremented)
type Observations ¶
Observations are passed to Commit to supply post-hoc measurements. Tokens is a map of token-type → count (e.g. {"input": 300, "output": 200}). Legacy callers that only have a total may pass {"tokens": total}. Nil Tokens is treated as all-zero.
type Reservation ¶
type Reservation struct {
ID string
// contains filtered or unexported fields
}
Reservation is returned by a successful Reserve call.
type Rule ¶
type Rule struct {
// Key is the caller-built scope identifier used as the last segment of
// Redis keys. Example: "Route:test-route:rl-basic".
// The pkg prepends "limit:{<scope>}:" and a strategy prefix.
Key string
// Name is for human-readable error messages only (e.g. "requests on rl-basic").
Name string
// Meter is "requests" | "tokens" | "tokens.<suffix>" | "concurrency".
Meter string
// Strategy controls the rate-limit algorithm.
Strategy Strategy
// Amount is the budget (requests, tokens, or concurrency slots).
Amount int64
// Window is the measurement period. For concurrency rules the window is
// used only to set key TTLs.
Window time.Duration
}
Rule is a single rate-limit rule passed to Reserve by the caller. The caller is responsible for building Rule.Key as a stable, unique string that identifies the (scope, rate-limit-name, meter) tuple.
type Strategy ¶
type Strategy string
Strategy names the rate-limiting algorithm for a Rule.
const ( // StrategyTokenBucket is the default. Tokens refill continuously at // amount/window rate; burst = amount. StrategyTokenBucket Strategy = "token-bucket" // StrategySlidingWindow uses a two-bucket weighted interpolation. StrategySlidingWindow Strategy = "sliding-window" // StrategyFixedWindow resets the counter at every floor(now/window) boundary. StrategyFixedWindow Strategy = "fixed-window" // StrategyLeakyBucket drains at a constant rate; excess is rejected. StrategyLeakyBucket Strategy = "leaky-bucket" // StrategySessionWindow anchors the window to the first request after a // reset; the window does not reset in the background. StrategySessionWindow Strategy = "session-window" )