ratelimit

package
v0.2.3 Latest Latest
Warning

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

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

Documentation

Overview

Package ratelimit is cadish's in-memory, per-node token-bucket rate limiter — the stateful server-side half of the native `rate_limit` security primitive.

Architecture (WAF v1b / D51):

  • The PURE pipeline gate (internal/pipeline.EvalSecurity) identifies the applicable rate_limit rule for a request and computes the bucket KEY (the resolved real client IP, a header value, or a whole-site constant). It does NO counting — it stays a side-effect-free function safe for concurrent use.
  • This package owns the MUTABLE state: one token bucket per (rule, key). The server consults Allow(key, rule) in the gate seam after `deny`; on a throttle it returns 429 + Retry-After, touching neither cache nor origin.

State is PER-NODE only (spec §2.7 / D51): no distributed counters, no Redis, no gossip. With N nodes behind an LB each counts independently, so the effective limit is ≈ N× the configured rate — mitigate by setting limit = target/N.

The store is SHARDED by key hash so concurrent requests on different keys do not contend on a single global lock, and idle buckets are swept on a background timer so memory stays bounded under a high-cardinality key space (e.g. per-IP buckets during a botnet flood). The clock is injectable so the token math is testable without sleeping.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RetryAfterSeconds

func RetryAfterSeconds(d time.Duration) int

RetryAfterSeconds rounds a Retry-After hint up to whole seconds (minimum 1), the form the HTTP Retry-After header takes for a delta value.

Types

type Decision

type Decision struct {
	OK         bool
	RetryAfter time.Duration
}

Decision is the outcome of an Allow call. OK is false when the request is throttled; RetryAfter is then the time until the next token is available (a hint for the 429 Retry-After header; the server rounds it up to whole seconds).

type Limiter

type Limiter struct {
	// contains filtered or unexported fields
}

Limiter is a sharded, in-memory, per-node token-bucket rate limiter. Construct with New (real clock) or NewWithClock (injectable clock, for tests). Safe for concurrent use. Call Stop to halt the background sweeper.

func New

func New() *Limiter

New builds a Limiter using the wall clock and starts its background sweeper.

func NewWithClock

func NewWithClock(now func() time.Time) *Limiter

NewWithClock builds a Limiter with an injectable time source (tests pass a fake clock so token refill is deterministic without sleeping) and starts the sweeper.

func (*Limiter) Allow

func (l *Limiter) Allow(key string, r Rule) Decision

Allow consumes one token for key under rule and reports whether the request is admitted. A nil Limiter always admits (defensive: a server with no rate_limit rules holds a nil limiter and never calls this).

func (*Limiter) Len

func (l *Limiter) Len() int

Len reports the total live bucket count across all shards (for tests / metrics).

func (*Limiter) Stop

func (l *Limiter) Stop()

Stop halts the background sweeper. Idempotent; safe to call on a nil Limiter (the nil case is a no-op so a non-rate-limited server can hold a nil one).

type Rule

type Rule struct {
	RatePerSec float64
	Burst      int
}

Rule is the immutable rate spec the pure gate hands to the limiter. RatePerSec is the steady refill rate (tokens/second); Burst is the extra capacity above one token (a burst of N lets N requests through in an instant when full). Capacity = max(1, Burst): a request costs one token.

Jump to

Keyboard shortcuts

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