Documentation
¶
Overview ¶
Package weir is a shared HTTP cache engine that sits between an HTTP server and an origin. It implements RFC 9111 caching and adds defenses against origin stampedes, origin and storage outages, and cache-key manipulation.
The design is specified in docs/ at the repository root; start with docs/README.md. Implementation progress is tracked in docs/progress/.
Index ¶
- Variables
- func RetryAfter(err error) (time.Duration, bool)
- func StatusCode(err error) int
- func TrackingParams() []string
- type BreakerConfig
- type BreakerState
- type BypassConfig
- type CacheGroupsConfig
- type CacheInfo
- type ClientConfig
- type CoalesceConfig
- type Config
- type Engine
- func (e *Engine) Close(ctx context.Context) error
- func (e *Engine) Serve(ctx context.Context, req *Request, origin Origin) (*Response, error)
- func (e *Engine) SetMode(m Mode, ttl time.Duration) error
- func (e *Engine) Warm(ctx context.Context, reqs iter.Seq[*Request], origin Origin) (WarmStats, error)
- type EngineStats
- type Event
- type EventKind
- type ForwardConfig
- type ForwardMode
- type FreshnessConfig
- type FwdReason
- type KeyConfig
- type LimiterConfig
- type LimitsConfig
- type MissRateConfig
- type Mode
- type NegativeConfig
- type Observer
- type Origin
- type OriginError
- type OriginFunc
- type Purge
- type PurgeMode
- type Request
- type RequestError
- type Response
- type RetryError
- type StaleReason
- type StorableConfig
- type TimeoutsConfig
- type VaryMode
- type WarmConfig
- type WarmStats
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidConfig is returned by New for any invalid Config field. ErrInvalidConfig = errors.New("weir: invalid config") // ErrInvalidRequest means the request failed input validation (FR-VAL). // Serve returns it wrapped in *RequestError. ErrInvalidRequest = errors.New("weir: invalid request") // ErrShed means no origin slot was free within the queue budget and // nothing stale could be served. Serve returns it wrapped in *RetryError. ErrShed = errors.New("weir: origin capacity exhausted") // ErrCircuitOpen means the breaker is open and nothing stale could be // served. Serve returns it wrapped in *RetryError. ErrCircuitOpen = errors.New("weir: origin circuit open") // ErrOriginTimeout means the origin did not answer within Timeouts.Origin // (Timeouts.Background for background refresh and Warm). ErrOriginTimeout = errors.New("weir: origin timeout") // ErrMustRevalidate means a must-revalidate entry could not be validated. ErrMustRevalidate = errors.New("weir: must-revalidate response could not be validated") // ErrOnlyIfCached means an only-if-cached request found no usable stored // response. ErrOnlyIfCached = errors.New("weir: only-if-cached and no stored response") // ErrOrigin means the origin returned a transport error. Serve returns it // wrapped in *OriginError. ErrOrigin = errors.New("weir: origin error") // ErrClosed means the engine is closed. ErrClosed = errors.New("weir: engine closed") // ErrUpgradeNotSupported means a CONNECT or protocol upgrade request // reached Serve (FR-UPG-1). Adapters route those around the engine. ErrUpgradeNotSupported = errors.New("weir: connect and protocol upgrades not supported") // ErrEagerUnsupported means an eager purge wrote its epoch but the store // cannot scrub, so matching records were not deleted (FR-PRG-8). ErrEagerUnsupported = errors.New("weir: store cannot scrub; epoch written, delete skipped") )
Sentinel errors. Compare with errors.Is; the engine returns them wrapped. StatusCode maps each to the status an adapter should send (01 §4).
Functions ¶
func RetryAfter ¶
RetryAfter returns the Retry-After hint when err classifies as shed or circuit open through a *RetryError, rounded up to whole seconds and never negative. A hint wrapped inside an *OriginError belongs to the origin and is not returned.
func StatusCode ¶
StatusCode maps err to the status an adapter should send (01 §4). Unknown errors, and nil, map to 502.
The outermost recognized error in the chain decides, walking in the same order as errors.Is. An *OriginError maps to 502 whatever it wraps: an origin that returns weir errors (another engine) or its own context errors must not turn an origin failure into 400, 499, 503 or 504.
func TrackingParams ¶
func TrackingParams() []string
TrackingParams returns Key.QueryDrop patterns for the click and campaign identifiers that ad networks and mail tools append to links. They rarely change a response, and each distinct value would otherwise mint a key. Weir drops nothing by default (D30); opt in with
weir.Config{Key: weir.KeyConfig{QueryDrop: weir.TrackingParams()}}
Dropped parameters never reach the origin (FR-KEY-5), so leave out any name the origin reads. Each call returns a new slice that the caller may extend or trim.
Types ¶
type BreakerConfig ¶
type BreakerConfig struct {
Window time.Duration // 0: 10s (10 buckets)
MinRequests int // 0: 20
FailureRatio float64 // 0: 0.5; (0, 1]
OpenFor time.Duration // 0: 5s
MaxOpenFor time.Duration // 0: max(60s, OpenFor); at least OpenFor
HalfOpenProbes int // 0: 1
CountStatus500 bool
Disable bool
}
BreakerConfig tunes the origin circuit breaker (FR-BRK).
type BreakerState ¶
type BreakerState uint8
BreakerState is the origin circuit breaker's state.
const ( BreakerClosed BreakerState = iota BreakerHalfOpen BreakerOpen )
BreakerState values.
type BypassConfig ¶
type BypassConfig struct {
Cookies []string
Headers []string // canonicalized
ReportStrippedCookies time.Duration // FR-OBS-5; 0: 5m; negative disables
}
BypassConfig lists request cookies and headers that skip the cache.
type CacheGroupsConfig ¶
type CacheGroupsConfig struct{ Ignore bool }
CacheGroupsConfig controls RFC 9875 cache groups. The zero value honors them.
type CacheInfo ¶
type CacheInfo struct {
Hit bool // answered without contacting the origin for this request
Fwd FwdReason // why the request went forward; FwdNone when Hit
Stale StaleReason // non-zero when a stale response was served
FwdStatus int // origin status when Fwd != FwdNone (304 on revalidation)
Stored bool // the forwarded response was stored
Collapsed bool // this request waited on another request's flight
TTL time.Duration // remaining freshness at send time; negative when stale
Detail string // implementation detail for Cache-Status, e.g. "negative"
}
CacheInfo describes how Serve produced a response. It feeds the Cache-Status header (FR-SRV-9) and events.
type ClientConfig ¶
type ClientConfig struct{ HonorRevalidation bool }
ClientConfig controls client request directives. HonorRevalidation lets no-cache and max-age=0 reach the origin (D5).
type CoalesceConfig ¶
type CoalesceConfig struct {
LeaderMaxAge time.Duration // 0: min(10s, Timeouts.Origin); at most Timeouts.Origin
FollowerMaxWait time.Duration // 0: min(10s, Timeouts.Origin); at most Timeouts.Origin
HitForMissTTL time.Duration // 0: 30s
}
CoalesceConfig bounds request coalescing (FR-COA).
type Config ¶
type Config struct {
Store store.Store // nil: memory store sized per FR-MEM-1
Key KeyConfig
Forward ForwardConfig
Bypass BypassConfig
Storable StorableConfig
Freshness FreshnessConfig
Coalesce CoalesceConfig
Limiter LimiterConfig
Breaker BreakerConfig
Negative NegativeConfig
MissRate MissRateConfig
CacheGroups CacheGroupsConfig
Client ClientConfig
Timeouts TimeoutsConfig
Warm WarmConfig
Limits LimitsConfig
CacheStatus string // Cache-Status member name (FR-SRV-9); "": "Weir"
NoCacheStatus bool // omit the Cache-Status header (T-27)
Observer Observer
Logger *slog.Logger // nil: discard
Rand func() float64 // [0, 1); safe for concurrent use; nil: rand.Float64
}
Config configures an Engine (01 §6, 04 §1.1). The zero value is valid and yields the defaults; every boolean is written so that false is the default. New copies it, so later changes to the caller's slices have no effect.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine makes shared-cache decisions per request (04 §6.1). It is safe for concurrent use (FR-LCY-3).
func New ¶
New validates cfg, applies defaults and returns a ready engine. It returns an error wrapping ErrInvalidConfig for any invalid field (FR-LCY-1).
func (*Engine) Close ¶
Close rejects new Serve calls with ErrClosed, waits for engine goroutines until ctx is done, then cancels the rest and waits for them (FR-LCY-2). It returns ctx's error when the grace period ran out. It closes the store only if the engine created it. Later calls wait for the first until their own ctx is done and return nil or ctx's error.
func (*Engine) Serve ¶
Serve answers one request. It never returns (nil, nil). On a nil error the caller owns resp.Body and must close it. A non-nil error means no response could be produced; StatusCode(err) gives the status an adapter should send.
func (*Engine) SetMode ¶
SetMode switches the incident mode for ttl, after which it reverts to ModeNormal (FR-MODE-1, D33). ttl must be in (0, 24 h]. Modes are not persisted. It returns an error wrapping ErrInvalidConfig for a bad mode or ttl. Expiry is noticed, and its EvMode emitted, by the first Serve after the ttl runs out.
func (*Engine) Warm ¶
func (e *Engine) Warm(ctx context.Context, reqs iter.Seq[*Request], origin Origin) (WarmStats, error)
Warm fetches each request through the normal path at background priority and stores what is storable (FR-WRM-1, T6.4). Warm.Concurrency workers fetch at once; each waits for a limiter slot outside the foreground reserve until ctx ends. A request with a fresh entry, or whose key another request's flight fetches and stores, counts as skipped (FR-WRM-2). Requests Weir would never store (unsafe methods, Range, only-if-cached) are not sent and count as not stored. Warm stops at the first ctx cancellation and returns ctx's error. Once Close starts, it takes no new request, lets running fetches finish within Close's grace, and returns ErrClosed.
type EngineStats ¶
type EngineStats struct {
Inflight int // origin fetches holding limiter slots
Queued int // fetches waiting for a slot
BreakerState BreakerState // BreakerClosed, BreakerHalfOpen or BreakerOpen
StoreBytes int64 // -1 when the store does not implement store.Sizer
}
EngineStats is a point-in-time snapshot for gauges, polled by exporters.
type Event ¶
type Event struct {
Kind EventKind
Time time.Time
Partition string // truncated to 256 bytes; empty for engine-wide events; never a metric label
Duration time.Duration // fetch, wait, or open duration where relevant
Status int
Reason string // fixed vocabulary per kind, safe as a metric label
Info CacheInfo // for EvRequest
}
Event is one engine event. Reason uses a fixed vocabulary per Kind and never contains request data.
type EventKind ¶
type EventKind uint8
EventKind identifies an event. Its String form is safe as a metric label.
const ( EvRequest EventKind = iota + 1 // every Serve return EvFetchStart // before every Origin.Fetch EvFetchEnd // after every Origin.Fetch EvCoalesceJoin // a request joined an existing flight EvCoalesceTimeout // a follower wait expired EvShed // the limiter refused EvStaleServed // a stale response was served EvRefreshDropped // a background refresh was not started EvBreakerState // breaker transition EvStoreError // the store guard saw store.ErrUnavailable EvStoreBreaker // the store guard opened or closed EvKeyRejected // request validation failed EvNotStored // storability failed EvVaryOverflow // the variant cap was reached EvNegativeServed // a negative entry was used EvPurge // Purge or invalidation wrote epochs EvMissRateAnomaly // a window closed with an anomalous partition EvEvict // the memory store evicted EvMode // the incident mode changed (FR-MODE-1) )
EventKind values (04 §9.2).
type ForwardConfig ¶
type ForwardConfig struct {
Mode ForwardMode
Allow []string // canonicalized; added to the trace headers
NoTraceHeaders bool // D29: do not forward traceparent, tracestate, X-Request-Id
}
ForwardConfig controls which request headers reach the origin (FR-FWD).
type ForwardMode ¶
type ForwardMode uint8
ForwardMode selects the forwarding policy.
const ( ForwardStrict ForwardMode = iota ForwardAll // logs a warning at New )
ForwardMode values.
type FreshnessConfig ¶
type FreshnessConfig struct {
Jitter float64 // 0: 0.10 unless NoJitter; [0, 0.5]
NoJitter bool
JitterMinLifetime time.Duration // 0: 10s
EarlyRefreshBeta float64 // 0: 1.0
NoEarlyRefresh bool
HeuristicFraction float64 // 0: 0.1
HeuristicMax time.Duration // 0: 1h
DefaultTTL time.Duration // 0: none
DefaultStaleWhileRevalidate time.Duration // 0: none
DefaultStaleIfError time.Duration // 0: none
Keep time.Duration // 0: 5m; only entries with a validator
}
FreshnessConfig tunes lifetimes, jitter and early refresh (FR-FRS).
type FwdReason ¶
type FwdReason uint8
FwdReason is why a request went to the origin. Values mirror the fwd parameter of RFC 9211 §2.2.
const ( FwdNone FwdReason = iota // not forwarded FwdBypass // a bypass rule matched (fwd=bypass) FwdMethod // the method is not cacheable (fwd=method) FwdURIMiss // no stored response for the URI (fwd=uri-miss) FwdVaryMiss // stored responses exist, none match Vary (fwd=vary-miss) FwdRequest // the request's directives forced forwarding (fwd=request) FwdStale // the stored response was stale (fwd=stale) )
FwdReason values.
type KeyConfig ¶
type KeyConfig struct {
QueryDrop []string // exact names or "prefix*"
QueryKeep []string // allowlist; empty means keep all
QuerySort bool
NormalizePath bool
Headers []string // canonicalized with http.CanonicalHeaderKey
Cookies []string // order is the forwarded order
Vary VaryMode
VaryAllow []string // canonicalized; required for sensitive Vary names
MaxVaryHeaders int // 0: 8
MaxVariants int // 0: 8
AcceptEncoding []string // empty: ["gzip"]; lowercased
}
KeyConfig controls which request parts form the cache key (FR-KEY).
type LimiterConfig ¶
type LimiterConfig struct {
MaxConcurrent int // 0: 64
MaxQueue int // 0: 1024
MaxQueueWait time.Duration // 0: 2s
MaxPerPartition int // 0: 16; clamped to MaxConcurrent
ReserveForeground int // 0: MaxConcurrent/4, at least 1 when MaxConcurrent >= 2
MaxPerHost int // 0: off (M14)
MaxUpload int // D25; 0: MaxConcurrent/4, at least 1
}
LimiterConfig bounds origin concurrency (FR-LIM).
type LimitsConfig ¶
type LimitsConfig struct {
MaxPathBytes int // 0: 8192
MaxQueryBytes int // 0: 8192
MaxQueryParams int // 0: 256
MaxKeyedHeaderBytes int // 0: 1024
MaxGroups int // 0: 32
MaxGroupBytes int // 0: 128
}
LimitsConfig bounds request input (FR-VAL-1, FR-VAL-3, FR-STO-10).
type MissRateConfig ¶
type MissRateConfig struct {
Window time.Duration // 0: 10s
TopK int // 0: 64
MinMisses int // 0: 500
MinRatio float64 // 0: 0.9
Throttle bool
Disable bool
}
MissRateConfig tunes the miss-rate detector (FR-MIS).
type NegativeConfig ¶
NegativeConfig tunes negative caching of origin errors (FR-NEG).
type Observer ¶
type Observer interface{ Observe(Event) }
Observer receives engine events (04 §9). Observe is called synchronously on the request path, so implementations must be fast and safe for concurrent use.
type Origin ¶
Origin produces responses for forwarded requests. Fetch must honor ctx: return promptly once ctx is done, and make body reads fail after that. Close depends on it (FR-LCY-2). Fetch may be called after the request that triggered it has finished (background refresh), and concurrently.
type OriginError ¶
type OriginError struct {
Err error
}
OriginError wraps a transport error from Origin.Fetch. It matches ErrOrigin under errors.Is and unwraps to Err.
func (*OriginError) Is ¶
func (e *OriginError) Is(target error) bool
Is reports whether target is ErrOrigin.
func (*OriginError) Unwrap ¶
func (e *OriginError) Unwrap() error
Unwrap returns the origin's error.
type OriginFunc ¶
OriginFunc adapts a function to the Origin interface.
type Purge ¶
type Purge struct {
Mode PurgeMode // PurgeSoft (zero value) or PurgeHard
All bool
URLs []string // absolute http(s) URLs; parsed and rewritten exactly like requests
Origin string // "scheme://host[:port]"; required when Groups is non-empty
Groups []string
Eager bool // M15: with PurgeHard, also delete matching records now (store.Scrubber)
}
Purge describes what Engine.Purge invalidates (FR-PRG-1). Invalid input rejects the whole call before any epoch is written.
type Request ¶
type Request struct {
Method string // exactly as received; methods are case-sensitive
Scheme string // "http" or "https"
Host string // authority (host[:port]) the client addressed
Path string // origin-form path, still percent-encoded, as received
RawQuery string // query without the leading '?', as received
Header http.Header // never mutated by Weir
Body io.ReadCloser // nil for GET and HEAD; passed through for other methods
}
Request is one client request as the adapter received it (01 §4).
type RequestError ¶
type RequestError struct {
Reason string
}
RequestError reports why a request failed validation. It matches ErrInvalidRequest under errors.Is.
func (*RequestError) Unwrap ¶
func (e *RequestError) Unwrap() error
Unwrap returns ErrInvalidRequest.
type Response ¶
type Response struct {
StatusCode int
Header http.Header
Body io.ReadCloser // never nil on responses returned by Serve
Cache CacheInfo // set by Serve; ignored on responses returned by an Origin
}
Response is a response from an Origin or from Serve. The receiver of a Response from Serve owns Body and must close it.
type RetryError ¶
type RetryError struct {
Err error // ErrShed or ErrCircuitOpen
After time.Duration // RetryAfter rounds it up to whole seconds
}
RetryError carries a Retry-After hint for shed and circuit-open errors.
type StaleReason ¶
type StaleReason uint8
StaleReason is why a stale response was served.
const ( StaleNone StaleReason = iota // the response was not stale StaleWhileRevalidate // within stale-while-revalidate StaleIfError // the origin failed, within stale-if-error StaleShed // the limiter shed the fetch StaleCircuitOpen // the breaker was open StaleCoalesceTimeout // the wait on another request's flight timed out )
StaleReason values.
type StorableConfig ¶
type StorableConfig struct {
Statuses []int // nil: default set; never 206, 304, 500, 502, 503, 504
MaxObjectBytes int64 // 0: 1 MiB, body plus headers
StripSetCookie bool
StreamTypes []string // Content-Type media types streamed like text/event-stream (FR-STR-1)
}
StorableConfig limits what may be stored (FR-STO).
type TimeoutsConfig ¶
type TimeoutsConfig struct {
Origin time.Duration // 0: 30s
Background time.Duration // 0: 30s; background refresh and Warm (FR-TMO-1)
Store time.Duration // 0: 50ms (remote stores only)
StreamIdle time.Duration // 0: 60s (D26)
}
TimeoutsConfig bounds origin and store calls (FR-TMO).
type WarmConfig ¶
type WarmConfig struct{ Concurrency int } // 0: min(4, MaxConcurrent-ReserveForeground); at most that
WarmConfig tunes Engine.Warm.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
weirproxy
command
Command weirproxy is a caching reverse proxy built from weir and weirhttp with default settings:
|
Command weirproxy is a caching reverse proxy built from weir and weirhttp with default settings: |
|
internal
|
|
|
breaker
Package breaker is the engine's origin circuit breaker: a failure ratio over a rolling window with a minimum volume, counting gateway failures only (docs/04-lld.md §8.3, docs/02-architecture.md ADR-7).
|
Package breaker is the engine's origin circuit breaker: a failure ratio over a rolling window with a minimum volume, counting gateway failures only (docs/04-lld.md §8.3, docs/02-architecture.md ADR-7). |
|
coalesce
Package coalesce holds the flight table that lets concurrent misses for one key share a single origin fetch (docs/04-lld.md §8.1, docs/03-hld.md §3.3).
|
Package coalesce holds the flight table that lets concurrent misses for one key share a single origin fetch (docs/04-lld.md §8.1, docs/03-hld.md §3.3). |
|
httpcc
Package httpcc parses and evaluates HTTP caching fields (RFC 9111): Cache-Control directives, lifetimes and entry evaluation (docs/04-lld.md §4).
|
Package httpcc parses and evaluates HTTP caching fields (RFC 9111): Cache-Control directives, lifetimes and entry evaluation (docs/04-lld.md §4). |
|
keys
Package keys validates requests, normalizes the inputs that reach the cache key and rewrites the forwarded request to match (docs/04-lld.md §3).
|
Package keys validates requests, normalizes the inputs that reach the cache key and rewrites the forwarded request to match (docs/04-lld.md §3). |
|
limiter
Package limiter bounds concurrent origin fetches: a global cap, a cap per partition, a foreground reserve and a bounded FIFO queue (docs/04-lld.md §8.2, docs/02-architecture.md ADR-8).
|
Package limiter bounds concurrent origin fetches: a global cap, a cap per partition, a foreground reserve and a bounded FIFO queue (docs/04-lld.md §8.2, docs/02-architecture.md ADR-8). |
|
testorigin
Package testorigin is a programmable weir.Origin for engine and load tests (docs/07-testing-strategy.md §3).
|
Package testorigin is a programmable weir.Origin for engine and load tests (docs/07-testing-strategy.md §3). |
|
Package store defines the storage contract the engine caches through: the Store interface, the Entry record, purge epochs and the optional capability interfaces.
|
Package store defines the storage contract the engine caches through: the Store interface, the Entry record, purge epochs and the optional capability interfaces. |
|
memory
Package memory is the default in-process store: sharded, byte-weighted S3-FIFO (ADR-6).
|
Package memory is the default in-process store: sharded, byte-weighted S3-FIFO (ADR-6). |
|
storetest
Package storetest is the store conformance suite (05 §8).
|
Package storetest is the store conformance suite (05 §8). |
|
Package weirhttp adapts a weir.Engine to net/http: middleware and a handler that serve requests through the engine, conversions between net/http and weir types, and an Origin backed by an http.RoundTripper.
|
Package weirhttp adapts a weir.Engine to net/http: middleware and a handler that serve requests through the engine, conversions between net/http and weir types, and an Origin backed by an http.RoundTripper. |