ratelimit

package
v0.3.3 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package ratelimit supplies typed, atomic fixed-window admission decisions.

Index

Constants

View Source
const KeyFeature keyspace.Feature = "ratelimit"

KeyFeature is the physical address family owned by this adapter.

View Source
const MaxWindow = 24 * time.Hour

MaxWindow bounds live policy retention and retry durations.

Variables

This section is empty.

Functions

func ValidateClear

func ValidateClear(ctx context.Context, key Key) error

ValidateClear shares the adapter input boundary for ClearRateLimit.

func ValidateOperation

func ValidateOperation(ctx context.Context, key Key, limit Limit, cost uint32) error

ValidateOperation checks context cancellation, key validity, policy and cost before an adapter operation.

Types

type Backend

type Backend interface {
	// RateLimit atomically decides whether cost fits the key's fixed window, consuming
	// capacity only on admission.
	RateLimit(context.Context, Key, Limit, uint32) (Decision, error)
}

Backend owns one authority's clock and atomic fixed-window decisions. A live bucket with a different policy is converted to the requested one: admitted usage still counts (capped at the new capacity) and a new window starts on the new policy's phase. Denials consume nothing; a denied take persists only a conversion that ends later than the live bucket, so carried usage is never released early. Windows use Key.WindowOffset. A backward clock step inside a live bucket is clamped to that bucket's start instead of reopening quota or failing. Implementations honor cancellation, bound metadata reads and never implicitly retry a mutation or fall back to another authority. The caller owns the adapter lifecycle; network errors may leave consumption unconfirmed.

type Config

type Config struct {
	Namespace                                   keyspace.Namespace
	MaxKeyBytes, MaxDeclarations, MaxConcurrent int
	Timeout                                     time.Duration
}

Config bounds declaration count, logical key bytes and entire active operations, including key resolvers/codecs. Timeout uses real context deadlines. A burst beyond MaxConcurrent waits in FIFO order for at most admission.Wait(Timeout), within that same deadline, and then fails with fault.Overloaded.

func DefaultConfig

func DefaultConfig(namespace keyspace.Namespace) Config

DefaultConfig returns default key, declaration, concurrency and timeout bounds for namespace.

func (Config) Validate

func (c Config) Validate() error

Validate checks the namespace and positive resource bounds.

type Decision

type Decision struct {
	Allowed                bool
	Limit, Remaining       uint32
	ResetAfter, RetryAfter time.Duration
}

Decision reports the authority's decision at execution time. Denials consume no capacity. Durations are relative to that decision, not the client clock. An error always returns a zero Decision; never infer admission from an error.

func (Decision) Validate

func (d Decision) Validate(limit Limit, cost uint32) error

Validate checks adapter output against the exact policy and request cost.

func (Decision) ValidatePeek

func (d Decision) ValidatePeek(limit Limit, cost uint32) error

ValidatePeek checks non-consuming adapter output: Remaining is the capacity left now, Allowed reports whether cost fits it, and RetryAfter is zero when allowed or equals ResetAfter when not.

type Declaration

type Declaration[K any] struct {
	// contains filtered or unexported fields
}

Declaration preserves its resource key type and immutable policy. Reuse one declaration for a shared quota; distinct names intentionally isolate quotas.

func Define

func Define[K any](name Name, codec keyspace.Codec[K], limit Limit) Declaration[K]

Define declares a typed key codec and rate policy; Validate checks the declaration.

func (Declaration[K]) Bind

func (d Declaration[K]) Bind(store *Store) (Limiter[K], error)

Bind rejects a different declaration with the same name, even with identical K.

func (Declaration[K]) Limit

func (d Declaration[K]) Limit() Limit

Limit returns the declared policy, or a zero limit for an undefined declaration.

func (Declaration[K]) Name

func (d Declaration[K]) Name() Name

Name returns the declaration name, or an empty name for an undefined declaration.

func (Declaration[K]) Validate

func (d Declaration[K]) Validate() error

Validate checks the declaration name, key codec and rate policy.

type InspectBackend

type InspectBackend interface {
	// PeekRateLimit reports the decision for cost without consuming capacity or changing
	// expiry.
	PeekRateLimit(context.Context, Key, Limit, uint32) (Decision, error)
	// ClearRateLimit removes the key's bucket and reports whether state existed.
	ClearRateLimit(context.Context, Key) (bool, error)
}

InspectBackend is an optional Backend capability. PeekRateLimit reports the decision cost would receive now, using the same policy conversion and clock rules, without consuming capacity or changing expiry; see Decision.ValidatePeek. ClearRateLimit removes the key's bucket, including unreadable state at its address, so the next request starts a fresh window; true means state existed. Both follow Backend's cancellation, validation and no-retry rules.

type Key

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

Key is an opaque adapter address, distinct from cache and lease keys.

func NewKey

func NewKey(namespace keyspace.Namespace, name Name, logical string) (Key, error)

NewKey is for adapters and tests. Applications use a typed Limiter.

func (Key) Namespace

func (k Key) Namespace() keyspace.Namespace

Namespace returns the key's application namespace.

func (Key) String

func (k Key) String() string

String returns the physical rate-limit address.

func (Key) Validate

func (k Key) Validate() error

Validate checks the key's adapter address.

func (Key) WindowOffset

func (k Key) WindowOffset(window time.Duration) time.Duration

WindowOffset is this key's deterministic window phase in [0, window). Adapters start fixed windows at offset + n*window, so the buckets of many keys do not all reset at the same Unix-epoch boundary. Every authority derives the same offset from the key address, independent of process or client clock. An uninitialized key or a nonpositive window has no phase.

type Limit

type Limit struct {
	Requests uint32
	Window   time.Duration
}

Limit admits Requests units per fixed Window. Each key's windows are shifted by its Key.WindowOffset, so many keys do not reset at the same instant. A fixed window permits bursts on both sides of a boundary; it is not a sliding window.

func PerHour

func PerHour(requests uint32) Limit

PerHour declares a fixed-window capacity per hour.

func PerMinute

func PerMinute(requests uint32) Limit

PerMinute declares a fixed-window capacity per minute.

func PerSecond

func PerSecond(requests uint32) Limit

PerSecond declares a fixed-window capacity per second.

func (Limit) Validate

func (l Limit) Validate() error

Validate requires positive capacity and a whole-millisecond window between one millisecond and MaxWindow.

func (Limit) ValidateCost

func (l Limit) ValidateCost(cost uint32) error

ValidateCost checks the policy and requires cost between one and the window capacity.

type Limiter

type Limiter[K any] struct {
	// contains filtered or unexported fields
}

Limiter consumes capacity using only its declaration's key type. Its zero value is invalid. Take is appropriate for weighted domain work; Allow costs one unit.

func (Limiter[K]) Allow

func (l Limiter[K]) Allow(ctx context.Context, key K) (Decision, error)

Allow attempts to consume one unit for key.

func (Limiter[K]) Attempt

func (l Limiter[K]) Attempt(ctx context.Context, key K, cost uint32, fn func(context.Context) error) (Decision, error)

Attempt takes cost and runs fn only when admitted, returning fn's error. A denial returns the decision without calling fn; an admission error returns it without calling fn. fn runs in the caller's goroutine after the bounded admission operation has finished, with the caller's context. Consumed capacity is not refunded when fn fails, and nothing is retried.

func (Limiter[K]) AvailableIn

func (l Limiter[K]) AvailableIn(ctx context.Context, key K, cost uint32) (time.Duration, error)

AvailableIn reports how long until cost could be admitted: zero when it fits now, otherwise the time until the current window resets. It consumes nothing.

func (Limiter[K]) Clear

func (l Limiter[K]) Clear(ctx context.Context, key K) (bool, error)

Clear removes the key's bucket so its next request starts a fresh window with full capacity. True means stored state existed. It also removes unreadable state at the key's address. Clearing grants new quota; use it deliberately (for example after a successful verification), never as error recovery.

func (Limiter[K]) Limit

func (l Limiter[K]) Limit() Limit

Limit returns the bound declaration's policy.

func (Limiter[K]) Name

func (l Limiter[K]) Name() Name

Name returns the bound declaration's name.

func (Limiter[K]) Peek

func (l Limiter[K]) Peek(ctx context.Context, key K, cost uint32) (Decision, error)

Peek reports the decision cost would receive now without consuming capacity or changing the bucket's expiry. Remaining is the capacity left now. It is an observation, not a reservation: a later Take can be denied. The backend must implement InspectBackend; otherwise Peek fails with fault.Invalid.

func (Limiter[K]) Remaining

func (l Limiter[K]) Remaining(ctx context.Context, key K) (uint32, error)

Remaining reports the capacity left in the key's current window without consuming it. A missing or expired bucket reports the full capacity.

func (Limiter[K]) Take

func (l Limiter[K]) Take(ctx context.Context, key K, cost uint32) (Decision, error)

Take attempts to consume cost units for key.

func (Limiter[K]) TakeWith

func (l Limiter[K]) TakeWith(ctx context.Context, cost uint32, resolve func(context.Context) (K, error)) (Decision, error)

TakeWith resolves a concrete key inside the same bounded operation as encoding and admission. It catches panic/Goexit and waits for callback exit. The resolver must honor cancellation; no callback or uncertain command is automatically retried.

func (Limiter[K]) Validate

func (l Limiter[K]) Validate() error

Validate checks the store binding and declaration.

type Name

type Name string

Name identifies a rate-limit declaration.

type Store

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

Store borrows its Backend. Construction performs no I/O or background work. Calls own callbacks until they actually exit, including after cancellation; an uncooperative callback retains its slot instead of creating detached work.

func NewStore

func NewStore(backend Backend, config Config) (*Store, error)

NewStore validates the backend and config and creates a bounded declaration registry. The caller retains backend lifecycle ownership.

func (*Store) Namespace

func (s *Store) Namespace() keyspace.Namespace

Namespace returns the store's configured application namespace.

Directories

Path Synopsis
Package memory implements explicit single-process fixed-window rate limiting.
Package memory implements explicit single-process fixed-window rate limiting.

Jump to

Keyboard shortcuts

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