ratelimit

package
v0.9.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 5, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

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

View Source
var ErrExceeded = errors.New("limit: budget exceeded")

ErrExceeded is the sentinel wrapped by *KeyQuotaExhausted.

Functions

func RegisterScripts

func RegisterScripts(m *kv.Mem)

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

type KeyQuotaExhausted struct {
	Rule       Rule
	RetryAfter time.Duration
}

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 New

func New(s kv.Store, log *slog.Logger, clock func() time.Time) *Limiter

New creates a Limiter. If clock is nil, time.Now is used.

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)

func (*Limiter) Reserve

func (l *Limiter) Reserve(ctx context.Context, scope string, rules []Rule) (*Reservation, error)

Reserve checks all rules and increments counters atomically via one RunScript call. On violation, all increments from this call are rolled back and *ExceededError is returned. scope is the Redis Cluster hash-tag anchor (e.g. "policy:prod-policy").

type Observations

type Observations struct {
	Tokens    map[string]int64
	Cancelled bool
}

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"
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL