ratelimit

package module
v0.0.0-...-c20ef0a Latest Latest
Warning

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

Go to latest
Published: Aug 23, 2026 License: MIT Imports: 8 Imported by: 0

README

rate-limit

Transport-neutral inbound rate limiting for Go applications. The package provides deterministic token-bucket, fixed-window, bounded sliding-counter, and concurrency-lease policies with memory, native Valkey, and native PostgreSQL backends.

This library owns inbound request, RPC, queue-admission, and application operation limits. It does not own authorization, billing quotas, WAF rules, queue acknowledgement, outbound retries, or HTTP transport pacing. Outbound request policy remains in http-client. Applications MAY compose a distributed concurrency lease around a complete provider operation when the limit must span all replicas; that lease is application admission, not HTTP retry or pacing.

Five-minute memory quickstart

policy, _ := ratelimit.NewPolicy(ratelimit.PolicySpec{
    ID: "login",
    Revision: "v1",
    Algorithm: ratelimit.TokenBucket,
    Capacity: 10,
    Burst: 2,
    Period: time.Minute,
    MaxCost: 2,
    FailureMode: ratelimit.FailClosed,
})
key, _ := ratelimit.NewKey(ratelimit.KeySpec{
    Namespace: "http",
    Version: "v1",
    Subject: ratelimit.Subject{Kind: "principal", Value: "user-42"},
    Hash: true,
})
backend, _ := memory.New(memory.Options{MaxKeys: 100_000, Shards: 64})
service, _ := ratelimit.NewService(backend)
decision, err := service.Admit(ctx, ratelimit.Request{
    Policy: policy,
    Key: key,
    Cost: 1,
    Now: time.Now().UTC(),
})

Memory is bounded and process-local. It must not be presented as a cluster-wide limit.

Five-minute Valkey quickstart

client, _ := valkeygo.NewClient(valkeygo.ClientOption{
    InitAddress: []string{"127.0.0.1:6379"},
})
defer client.Close()
backend, _ := valkey.Open(ctx, client, valkey.Options{
    Prefix: "my-service-rate-limit",
    Timeout: 100 * time.Millisecond,
    Clock: valkey.ServerClock,
})
service, _ := ratelimit.NewService(backend)

Valkey 9 or newer with maxmemory-policy=noeviction is required. Each state key uses an opaque SHA-256 hash tag, all mutation is atomic Lua, scripts recover from NOSCRIPT through valkey-go, and state has a bounded TTL.

Packages

  • Root: policy, request, decision, error, batch, service, and lease contracts.
  • memory: bounded sharded process-local backend.
  • valkey: native valkey-go scripts and cluster-safe keys.
  • postgres: native pgx transactions, cleanup, and migrations ownership.
  • ratelimithttp, ratelimitrpc, ratelimitqueue: inbound adapters.
  • ratelimitprincipal: narrow authentication-compatible principal adapter.
  • ratelimitlog and ratelimittelemetry: slog and OpenTelemetry observations.
  • ratelimittest: deterministic clocks, reference models, and conformance.

Documentation

Start at docs/README.md. The API and adoption guides cover algorithms, consistency, failure behavior, transports, migrations, operations, security, performance, and troubleshooting.

Local verification

make unit
make check

Exact production coverage and live integration need disposable services:

VALKEY_ADDRESS=127.0.0.1:6379 \
POSTGRES_URL='postgres://postgres:postgres@127.0.0.1:5432/rate_limit?sslmode=disable' \
make check

No core admission call sleeps or retries. See CONTRIBUTING.md for the full local gate stack.

License

MIT. See LICENSE.

Ecosystem

Use the Golib documentation portal to choose companion packages, supported stacks, recipes, and operations guidance.

Documentation

Overview

Package ratelimit defines transport-neutral admission policies, requests, decisions, observations, batching, and concurrency leases.

The package performs no sleeping or retry orchestration. Backends implement atomic state changes, while Service applies the policy's explicit failure mode and emits bounded observations.

Index

Constants

View Source
const (
	// MaxPolicyIDBytes bounds persisted and observed policy identifiers.
	MaxPolicyIDBytes = 64
	// MaxPolicyRevisionBytes bounds persisted and observed revision identifiers.
	MaxPolicyRevisionBytes = 64
	// MaxConcurrencyLeases bounds active lease entries for one policy and key.
	MaxConcurrencyLeases = 1024
)
View Source
const MaxBatchSize = 256

MaxBatchSize bounds memory, backend work, and observation fan-out per call.

View Source
const MaxLeaseIDBytes = 128

MaxLeaseIDBytes bounds caller-generated concurrency lease identifiers.

View Source
const MaxObservers = 16

MaxObservers bounds synchronous observation fan-out per decision.

View Source
const (
	// MaxSubjectBytes is the maximum unencoded subject length accepted by NewKey.
	MaxSubjectBytes = 256
)

Variables

View Source
var (
	// ErrRejected indicates a valid admission request exceeded its limit.
	ErrRejected = errors.New("rate limit rejected")
	// ErrInvalidPolicy indicates policy construction failed validation.
	ErrInvalidPolicy = errors.New("invalid rate limit policy")
	// ErrInvalidKey indicates key derivation received unsafe or oversized input.
	ErrInvalidKey = errors.New("invalid rate limit key")
	// ErrInvalidRequest indicates an admission or lease request is malformed.
	ErrInvalidRequest = errors.New("invalid rate limit request")
	// ErrUnavailable indicates the selected backend cannot currently decide.
	ErrUnavailable = errors.New("rate limit backend unavailable")
	// ErrDeadline indicates cancellation or deadline expiry interrupted a decision.
	ErrDeadline = errors.New("rate limit deadline exceeded")
	// ErrOverflow indicates bounded integer arithmetic could not represent a result.
	ErrOverflow = errors.New("rate limit arithmetic overflow")
	// ErrCorrupt indicates persisted state violates backend invariants.
	ErrCorrupt = errors.New("rate limit state corrupt")
	// ErrUnsupported indicates the backend cannot guarantee an operation's semantics.
	ErrUnsupported = errors.New("rate limit operation unsupported")
	// ErrLeaseNotFound indicates a concurrency lease does not exist or expired.
	ErrLeaseNotFound = errors.New("rate limit lease not found")
	// ErrLeaseNotOwned indicates a lease belongs to different policy or backend state.
	ErrLeaseNotOwned = errors.New("rate limit lease not owned")
)

Functions

This section is empty.

Types

type Algorithm

type Algorithm string

Algorithm identifies the admission algorithm used by a policy.

const (
	// TokenBucket refills capacity continuously using integer arithmetic.
	TokenBucket Algorithm = "token_bucket"
	// FixedWindow resets capacity at deterministic period boundaries.
	FixedWindow Algorithm = "fixed_window"
	// SlidingWindow estimates a rolling window using bounded segments.
	SlidingWindow Algorithm = "sliding_window"
	// Concurrency admits weighted work while an explicit lease is held.
	Concurrency Algorithm = "concurrency"
)

type Atomicity

type Atomicity string

Atomicity defines the guarantee requested for a batch.

const (
	// AtomicityPerItem evaluates each item independently and in input order.
	AtomicityPerItem Atomicity = "per_item"
	// AtomicityAllOrNothing requests a guarantee not provided by Service.Batch.
	AtomicityAllOrNothing Atomicity = "all_or_nothing"
)

type Backend

type Backend interface {
	// Name returns a stable implementation identifier for decisions and leases.
	Name() string
	// Admit atomically consumes capacity or returns ErrRejected.
	Admit(context.Context, Request) (Decision, error)
}

Backend atomically evaluates admission requests within its documented scope.

type BatchDecision

type BatchDecision struct {
	// Decisions contains one result per input request.
	Decisions []Decision
	// Atomicity reports the guarantee applied to Decisions.
	Atomicity Atomicity
}

BatchDecision preserves input order and documents actual atomicity.

type BatchRequest

type BatchRequest struct {
	// Requests contains between one and MaxBatchSize attempts.
	Requests []Request
	// Atomicity must be AtomicityPerItem.
	Atomicity Atomicity
}

BatchRequest groups bounded admission attempts with explicit atomicity.

type Consistency

type Consistency string

Consistency describes the scope in which a backend enforces a policy.

const (
	// ConsistencyProcessLocal limits independently inside one process.
	ConsistencyProcessLocal Consistency = "process_local"
	// ConsistencyStrong requires atomic coordination through shared state.
	ConsistencyStrong Consistency = "strong"
)

type Decision

type Decision struct {
	// Allowed reports whether the operation may proceed.
	Allowed bool
	// Remaining is the immediately available whole-unit capacity.
	Remaining uint64
	// Limit is the configured Capacity plus Burst.
	Limit uint64
	// Reset is the earliest useful replenishment or lease expiry time.
	Reset time.Time
	// RetryAfter is the minimum suggested delay after rejection.
	RetryAfter time.Duration
	// Reason is a stable machine-readable classification.
	Reason Reason
	// Backend identifies the implementation that made the decision.
	Backend string
	// PolicyRevision identifies the admission semantics used.
	PolicyRevision string
}

Decision describes the complete, observable result of admission.

type FailureMode

type FailureMode uint8

FailureMode controls the decision returned when a backend cannot decide.

const (
	// FailClosed rejects admission when the backend is unavailable.
	FailClosed FailureMode = iota
	// FailOpen admits non-concurrency work when the backend is unavailable.
	FailOpen
)

type Key

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

Key is a validated, bounded backend key with a safe subject kind.

func NewKey

func NewKey(spec KeySpec) (Key, error)

NewKey validates and derives a namespaced key from spec.

func (Key) String

func (k Key) String() string

String returns the bounded persisted representation.

func (Key) SubjectKind

func (k Key) SubjectKind() string

SubjectKind returns the safe, low-cardinality subject category.

type KeySpec

type KeySpec struct {
	// Namespace isolates applications or package consumers.
	Namespace string
	// Version permits intentional key derivation changes.
	Version string
	// Subject supplies the typed identity.
	Subject Subject
	// Hash irreversibly hashes Subject.Value before storage and observation.
	Hash bool
}

KeySpec describes a namespaced and versioned admission key.

type Lease

type Lease struct {
	// ID is the caller-generated lease identity.
	ID string
	// Key identifies the concurrency state.
	Key Key
	// PolicyID identifies the owning policy.
	PolicyID string
	// PolicyRevision identifies the semantics used when acquiring the lease.
	PolicyRevision string
	// Cost is the held concurrency weight.
	Cost uint64
	// ExpiresAt is the automatic release boundary.
	ExpiresAt time.Time
	// Backend identifies the implementation that created the lease.
	Backend string
}

Lease is proof of weighted concurrency ownership until ExpiresAt or release.

type LeaseBackend

type LeaseBackend interface {
	// Acquire creates or idempotently returns a weighted lease.
	Acquire(context.Context, LeaseRequest) (Lease, Decision, error)
	// Release relinquishes an owned lease without affecting unrelated capacity.
	Release(context.Context, Lease) error
}

LeaseBackend provides atomic acquire and ownership-checked release.

type LeaseRequest

type LeaseRequest struct {
	// Request must use a Concurrency policy.
	Request Request
	// LeaseID is an idempotent, caller-generated bounded identifier.
	LeaseID string
}

LeaseRequest requests weighted concurrency admission under a unique lease ID.

func (LeaseRequest) Validate

func (request LeaseRequest) Validate() error

Validate checks concurrency policy, admission inputs, and lease identity.

type Observation

type Observation struct {
	// PolicyID is the stable policy identity and must not contain credentials.
	PolicyID string
	// SubjectKind is the bounded key category, never the raw subject value.
	SubjectKind string
	// Decision is the completed admission result.
	Decision Decision
	// Err is the stable error returned to the caller, if any.
	Err error
	// Duration is local service processing time.
	Duration time.Duration
}

Observation contains bounded decision metadata for logging and telemetry.

type ObserveFunc

type ObserveFunc func(Observation)

ObserveFunc adapts a function to Observer.

func (ObserveFunc) Observe

func (function ObserveFunc) Observe(observation Observation)

Observe calls function with observation.

type Observer

type Observer interface {
	Observe(Observation)
}

Observer consumes bounded admission observations.

type Policy

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

Policy is an immutable, validated admission policy.

func NewPolicy

func NewPolicy(spec PolicySpec) (Policy, error)

NewPolicy validates spec and returns an immutable policy.

func (Policy) Algorithm

func (p Policy) Algorithm() Algorithm

Algorithm returns the policy algorithm.

func (Policy) Burst

func (p Policy) Burst() uint64

Burst returns additional bounded capacity.

func (Policy) Capacity

func (p Policy) Capacity() uint64

Capacity returns base capacity without burst.

func (Policy) Consistency

func (p Policy) Consistency() Consistency

Consistency returns the required coordination scope.

func (Policy) FailureMode

func (p Policy) FailureMode() FailureMode

FailureMode returns the backend outage behavior.

func (Policy) ID

func (p Policy) ID() string

ID returns the stable policy identity.

func (Policy) LeaseDuration

func (p Policy) LeaseDuration() time.Duration

LeaseDuration returns the concurrency lease duration.

func (Policy) Limit

func (p Policy) Limit() uint64

Limit returns Capacity plus Burst.

func (Policy) MaxCost

func (p Policy) MaxCost() uint64

MaxCost returns the greatest weight allowed for one operation.

func (Policy) Period

func (p Policy) Period() time.Duration

Period returns the refill or window duration.

func (Policy) Revision

func (p Policy) Revision() string

Revision returns the immutable policy revision.

type PolicySpec

type PolicySpec struct {
	// ID is a stable, non-secret policy identifier.
	ID string
	// Revision changes whenever admission semantics change.
	Revision string
	// Algorithm selects the state transition model.
	Algorithm Algorithm
	// Capacity is the base number of units available per period or lease set.
	Capacity uint64
	// Period controls refill or window duration for non-concurrency policies.
	Period time.Duration
	// Burst adds bounded capacity above Capacity.
	Burst uint64
	// MaxCost bounds the weight of one request; zero defaults to Limit.
	MaxCost uint64
	// FailureMode controls backend outage behavior.
	FailureMode FailureMode
	// Consistency declares the required coordination scope.
	Consistency Consistency
	// Lease is the maximum concurrency lease duration.
	Lease time.Duration
}

PolicySpec contains the immutable inputs used to construct a Policy.

type Reason

type Reason string

Reason is a stable, non-sensitive classification of an admission decision.

const (
	// ReasonAllowed means capacity was consumed successfully.
	ReasonAllowed Reason = "allowed"
	// ReasonLimited means insufficient capacity was available.
	ReasonLimited Reason = "limited"
	// ReasonFailOpen means a backend failure was admitted by policy.
	ReasonFailOpen Reason = "fail_open"
	// ReasonBackendUnavailable means fail-closed policy rejected an outage.
	ReasonBackendUnavailable Reason = "backend_unavailable"
)

type Request

type Request struct {
	// Policy contains immutable admission semantics.
	Policy Policy
	// Key identifies the bounded subject state.
	Key Key
	// Cost is the positive number of units requested.
	Cost uint64
	// Now is the caller-supplied current time used by client-clock backends.
	Now time.Time
}

Request is a weighted admission attempt evaluated at an explicit time.

func (Request) Validate

func (r Request) Validate() error

Validate checks that all request inputs are present and bounded by Policy.

type Service

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

Service validates requests, applies failure behavior, and emits observations.

func NewService

func NewService(backend Backend, observers ...Observer) (*Service, error)

NewService constructs an admission service using backend and observers.

func (*Service) Acquire

func (service *Service) Acquire(ctx context.Context, request LeaseRequest) (lease Lease, decision Decision, err error)

Acquire validates and atomically obtains a concurrency lease.

func (*Service) Admit

func (service *Service) Admit(ctx context.Context, request Request) (decision Decision, err error)

Admit evaluates one request without sleeping or retrying.

func (*Service) Batch

func (service *Service) Batch(ctx context.Context, batch BatchRequest) (BatchDecision, error)

Batch evaluates a bounded list with per-item atomicity and no hidden retries.

func (*Service) Release

func (service *Service) Release(ctx context.Context, lease Lease) error

Release relinquishes a lease through the backend that owns it.

type Subject

type Subject struct {
	// Kind is a bounded, low-cardinality subject category.
	Kind string
	// Value is the identity value and should normally be irreversibly hashed.
	Value string
}

Subject is a typed identity input used only to derive a bounded Key.

Directories

Path Synopsis
Package memory provides bounded, sharded, process-local admission state.
Package memory provides bounded, sharded, process-local admission state.
Package postgres provides transactional admission backed by PostgreSQL and pgx.
Package postgres provides transactional admission backed by PostgreSQL and pgx.
Package ratelimithttp provides net/http inbound admission middleware and strict trusted-proxy client IP extraction.
Package ratelimithttp provides net/http inbound admission middleware and strict trusted-proxy client IP extraction.
Package ratelimitlog adapts bounded observations to structured slog records.
Package ratelimitlog adapts bounded observations to structured slog records.
Package ratelimitprincipal adapts authentication principals to hashed admission keys without depending on a concrete authentication package.
Package ratelimitprincipal adapts authentication principals to hashed admission keys without depending on a concrete authentication package.
Package ratelimitqueue provides queue admission middleware that returns a typed deferral without acknowledging, sleeping, or changing retry ownership.
Package ratelimitqueue provides queue admission middleware that returns a typed deferral without acknowledging, sleeping, or changing retry ownership.
Package ratelimitrpc provides transport-neutral JSON-RPC admission middleware for global, principal, method, tenant, and custom subjects.
Package ratelimitrpc provides transport-neutral JSON-RPC admission middleware for global, principal, method, tenant, and custom subjects.
Package ratelimittelemetry adapts bounded observations to OpenTelemetry metrics without using attacker-controlled values as unbounded labels.
Package ratelimittelemetry adapts bounded observations to OpenTelemetry metrics without using attacker-controlled values as unbounded labels.
Package ratelimittest provides deterministic clocks, rational reference models, fixtures, and reusable cross-backend conformance tests.
Package ratelimittest provides deterministic clocks, rational reference models, fixtures, and reusable cross-backend conformance tests.
Package valkey provides atomic distributed admission backed by native Valkey.
Package valkey provides atomic distributed admission backed by native Valkey.

Jump to

Keyboard shortcuts

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