Documentation
¶
Overview ¶
Package state abstracts the gateway's shared runtime state — rate-limit windows, circuit-breaker state, and list caches — behind interfaces with two providers: in-process (default) and Redis (set REDIS_URL or server.redisUrl), which makes a fleet of gateway instances behave as one.
Index ¶
- type Breaker
- type Budget
- type BudgetResult
- type Limiter
- type ListCache
- type MemOnce
- type MemStore
- func (s *MemStore) Delete(_ context.Context, key string)
- func (s *MemStore) Get(_ context.Context, key string) ([]byte, bool)
- func (s *MemStore) GetMany(_ context.Context, keys []string) map[string][]byte
- func (s *MemStore) Set(_ context.Context, key string, value []byte, ttl time.Duration)
- func (s *MemStore) SetMany(_ context.Context, entries map[string][]byte, ttl time.Duration)
- type Memory
- func (*Memory) Breaker(_ string, threshold int, halfOpenAfter time.Duration) Breaker
- func (*Memory) Budget(_ string, period Period, limit int64) Budget
- func (*Memory) Close() error
- func (*Memory) Limiter(_ string, rpm int) Limiter
- func (*Memory) ListCache(_ string) ListCache
- func (*Memory) Once(_ string) Once
- func (*Memory) Store(_ string) Store
- type Once
- type Period
- type Provider
- type Redis
- func (r *Redis) Breaker(scope string, threshold int, halfOpenAfter time.Duration) Breaker
- func (r *Redis) Budget(scope string, period Period, limit int64) Budget
- func (r *Redis) Close() error
- func (r *Redis) Limiter(scope string, rpm int) Limiter
- func (r *Redis) ListCache(scope string) ListCache
- func (r *Redis) OnDegraded(fn func(kind string))
- func (r *Redis) Once(scope string) Once
- func (r *Redis) Store(scope string) Store
- type Store
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Breaker ¶
type Breaker interface {
Allow(ctx context.Context) bool
Record(ctx context.Context, success bool)
State(ctx context.Context) breaker.State
}
Breaker is a shared circuit breaker.
type Budget ¶ added in v1.7.0
type Budget interface {
// Add records n units of consumption and reports whether they fit. A
// rejected Add records nothing — an over-budget caller does not dig the
// hole deeper by retrying.
Add(ctx context.Context, n int64) BudgetResult
// Used reports the window's state without consuming any of it. A budget
// that can only reject is one an operator discovers at 100%; this is what
// makes 80% visible.
Used(ctx context.Context) BudgetResult
}
Budget admits consumption against a fixed, calendar-aligned allowance that resets at the period boundary.
Unlike Limiter, which smooths a rate, a Budget accumulates: what is spent stays spent until the window rolls over.
type BudgetResult ¶ added in v1.7.0
type BudgetResult struct {
// Allowed reports whether the consumption fits the allowance.
Allowed bool
// Used and Limit describe the window after this check. Limit is 0 for an
// unlimited budget.
Used, Limit int64
// Resets is when the window rolls over. It is not a retry delay: the
// answer to an exhausted monthly budget is "not until the 1st", and a
// caller that backs off by this amount would sleep for a fortnight.
Resets time.Time
// Degraded reports that shared state was unreachable, so this decision
// was made per-instance rather than fleet-wide. Budgets fail open — a
// dependency blip must not refuse all service — but silence would let a
// fleet run unbudgeted without anyone noticing, so the caller is expected
// to surface this in audit and metrics.
Degraded bool
}
A BudgetResult is the outcome of a budget check.
type Limiter ¶
type Limiter interface {
// Allow reports whether one more request fits and, when it does not,
// how long to wait before retrying.
Allow(ctx context.Context) (ok bool, retryAfter time.Duration)
}
Limiter admits requests against a shared budget.
type ListCache ¶
type ListCache interface {
// GetOrFill returns the cached bytes for key, or runs fill and caches
// its result for ttl. ttl <= 0 bypasses caching.
GetOrFill(ctx context.Context, key string, ttl time.Duration, fill func(context.Context) ([]byte, error)) ([]byte, error)
// Invalidate drops entries whose key has the given prefix.
Invalidate(ctx context.Context, prefix string)
}
ListCache caches serialized list results with a TTL, invalidated by prefix.
type MemOnce ¶
type MemOnce struct {
// contains filtered or unexported fields
}
MemOnce is the in-process single-use recorder. Exported so the Redis provider can fall back to it during an outage.
func (*MemOnce) TryOnce ¶
TryOnce reports whether key is fresh, recording it for ttl. Expired records read as absent and are reclaimed by the bounded map itself — no full scan per call. A non-positive ttl records the key until evicted (bounded.Map semantics); callers wanting single-use enforcement pass the credential's real remaining lifetime, as the EMA path does.
type MemStore ¶ added in v1.5.0
type MemStore struct {
// contains filtered or unexported fields
}
MemStore is the in-process record store. Exported so the Redis store can mirror into it and fall back on it during an outage.
func NewMemStore ¶ added in v1.5.0
func NewMemStore() *MemStore
NewMemStore returns an empty in-process record store.
type Memory ¶
type Memory struct{}
Memory is the in-process provider; state is per gateway instance.
type Once ¶
type Once interface {
// TryOnce records key for ttl and reports whether this was the first
// use. false means the key was already recorded — a replay.
TryOnce(ctx context.Context, key string, ttl time.Duration) bool
}
Once records single-use keys (e.g. token replay protection).
type Period ¶ added in v1.7.0
type Period string
A Period is a calendar-aligned budget window.
Calendar alignment is the whole point, and the reason a budget cannot be a Limiter with a longer window: that limiter is a two-bucket sliding window, so an exhausted caller is readmitted gradually as the trailing window elapses. A budget must instead reset at a boundary an operator can predict, explain to a customer, and reconcile against.
Boundaries are UTC. A local-time month would move under a DST transition and differ between instances in different zones, which for a fleet-wide counter means two instances disagreeing about which month it is.
The supported budget periods.
type Provider ¶
type Provider interface {
// Limiter returns a rate limiter for scope admitting rpm requests per
// minute. rpm <= 0 returns an unlimited limiter.
Limiter(scope string, rpm int) Limiter
// Budget returns an accumulating allowance for scope over a
// calendar-aligned period. limit <= 0 returns an unlimited budget.
Budget(scope string, period Period, limit int64) Budget
// Breaker returns a circuit breaker for scope.
Breaker(scope string, threshold int, halfOpenAfter time.Duration) Breaker
// ListCache returns the list cache for scope.
ListCache(scope string) ListCache
// Once returns the single-use recorder for scope.
Once(scope string) Once
// Store returns the shared record store for scope.
Store(scope string) Store
// Close releases provider resources.
Close() error
}
Provider constructs the gateway's shared-state primitives.
type Redis ¶
type Redis struct {
// contains filtered or unexported fields
}
Redis is the shared-state provider backed by a Redis instance; a fleet of gateways pointed at the same Redis behaves as one gateway for rate limits, circuit breakers, and list caches.
func (*Redis) OnDegraded ¶ added in v1.11.0
OnDegraded registers fn to be told each time a rate-limit or breaker decision was made per-instance because Redis was unreachable (kind: "limiter" | "breaker"). The gateway wires it into fold_state_degraded_total — fail-open is deliberate, but a fleet whose limits are momentarily per-instance must be visible, exactly as budgets already are. Safe to call before or after limiters exist.
type Store ¶ added in v1.5.0
type Store interface {
// Get returns the record for key. Absent — including when the backing
// store is unreachable — reads as (nil, false).
Get(ctx context.Context, key string) ([]byte, bool)
// GetMany returns the records present among keys, omitting absent ones.
GetMany(ctx context.Context, keys []string) map[string][]byte
// Set writes key for ttl (ttl <= 0 never expires).
Set(ctx context.Context, key string, value []byte, ttl time.Duration)
// SetMany writes every entry with the same ttl in one round trip.
SetMany(ctx context.Context, entries map[string][]byte, ttl time.Duration)
// Delete removes key.
Delete(ctx context.Context, key string)
}
Store holds small opaque records, each with its own TTL. Unlike ListCache it is not a fill-through cache: absence is meaningful to the caller and writes are explicit. Used for records a fleet must agree on rather than each instance observing for itself — task ownership, today.
The batch forms exist because the federated task list resolves ownership for a whole page at once; issuing one round trip per task would make list latency scale with the size of the federation's task set.