Documentation
¶
Overview ¶
Package cluster turns N cadish nodes in a region into a sharded / cooperative cache. It implements two interlocking features driven by one `cluster { … }` Cadishfile membership block:
PEER READ-THROUGH (#7): on a LOCAL cache miss, consult peer cadish nodes (same region) before going to origin; if a peer has the object, stream it locally (same tee contract as origin). Modeled as an origin.Origin (internal/origin/peerorigin) composed BEFORE the real origin in a chain.
OWNERSHIP ROUTING (#8): one node OWNS each cache key via a consistent-hash ring over the cluster peers (the SAME lb ring used for upstream sharding). A request landing on a non-owner is reverse-proxied to the owner so the object is cached once per region, not N times.
Both share cluster membership + peer health (reused from internal/lb) and a hop guard (the X-Cadish-Peer header) that prevents forward loops/storms: a request already forwarded to a peer is never re-forwarded.
ZERO COST WHEN ABSENT. Everything here is gated by the presence of a `cluster` membership block; a non-clustered cadish constructs no Membership and behaves exactly as before.
Index ¶
- Constants
- func IsMembershipBlock(d *cadishfile.Directive) bool
- type Config
- type Fallback
- type Membership
- func (m *Membership) Close()
- func (m *Membership) Fallback() Fallback
- func (m *Membership) IntendedOwner(key string) (string, bool)
- func (m *Membership) IsForwardedHop(h http.Header) bool
- func (m *Membership) IsSelf(peerURL string) bool
- func (m *Membership) Mode() Mode
- func (m *Membership) Owner(key string) (string, bool)
- func (m *Membership) OwnsKey(key string) bool
- func (m *Membership) PeerCount() int
- func (m *Membership) PeerOrigin() origin.Origin
- func (m *Membership) Region() string
- func (m *Membership) Self() string
- func (m *Membership) Start(ctx context.Context)
- type Mode
Constants ¶
const HopHeader = "X-Cadish-Peer"
HopHeader marks a request as already forwarded to a peer cadish node. A node that sees it MUST NOT forward again (read-through or owner-route), which is what makes the cluster loop-safe and storm-safe. Its value is the originating region, used to ignore cross-region hops.
Variables ¶
This section is empty.
Functions ¶
func IsMembershipBlock ¶
func IsMembershipBlock(d *cadishfile.Directive) bool
IsMembershipBlock reports whether a `cluster` directive is the membership form (this package) rather than the pre-existing `cluster NAME { to … }` LB-pool form (internal/lb). The membership block has NO name argument and carries a `peers` directive; the LB-pool form has a name and `to` backends. Distinguishing by shape lets both coexist without a keyword clash.
Types ¶
type Config ¶
type Config struct {
// Self is this node's own peer URL (must appear in Peers). It identifies which
// ring node is "us" for ownership decisions and is excluded from peer fetches.
Self string
// Peers are the peer cadish node targets (static URLs and/or dns://, k8s://
// discovery), parsed with the same lb.Target syntax as upstream `to`. At least
// one required; Self must be among them.
Peers []lb.Target
// Region scopes the cluster; it is the value of the X-Cadish-Peer hop header so
// a node ignores hops stamped by a different region.
Region string
// Mode selects #7/#8 coexistence (default read_through).
Mode Mode
// Fallback selects owner-mode behavior on owner-down (default degraded).
Fallback Fallback
// Health is the active peer-health probe spec (reused verbatim from lb). Nil
// disables active probing (peers are then always eligible).
Health *lb.HealthSpec
// Replicas is the consistent-hash virtual-node count per peer (0 ⇒ lb default).
Replicas int
// Pos is the directive's source position.
Pos cadishfile.Pos
}
Config is a parsed `cluster { … }` membership block.
func Parse ¶
func Parse(d *cadishfile.Directive) (Config, error)
Parse builds a Config from a `cluster { … }` membership directive. The block has NO name argument (that form is the lb pool) and accepts:
self URL (this node; ∈ peers) peers URL... (repeatable; ≥1) region NAME mode read_through | owner (default read_through) fallback strict | degraded (default degraded) health METHOD PATH expect CODE interval D window N threshold T replicas N (ring vnodes; tests)
Errors are positioned *cadishfile.ParseError (file:line:col: message).
type Fallback ¶
type Fallback int
Fallback selects owner-mode behavior when the computed owner is unavailable.
const ( // FallbackDegraded (the default) serves the request from the next eligible // ring node, then peer read-through, then local origin — availability over // strict single-ownership. A flapping owner degrades to "cached on a neighbor" // rather than failing. FallbackDegraded Fallback = iota // FallbackStrict serves the request locally (this node's cache→origin) when // the owner is down, accepting a transient duplicate cache entry rather than // chaining proxies. No second hop. FallbackStrict )
type Membership ¶
type Membership struct {
// contains filtered or unexported fields
}
Membership is the live cluster state for one node: the peer pool (an lb.Upstream that consistent-hash-shards a cache key to its owning peer, with reused health / failover / discovery), this node's identity, and the #7/#8 policy. It is built once from a Config and is safe for concurrent use. A non-clustered cadish never constructs one (zero cost when absent).
func New ¶
func New(cfg Config) (*Membership, error)
New builds a Membership from cfg. The peer pool is a shard-by-key lb.Upstream so a cache key routes to its owning peer; the same pool answers Owner() for #8 and backs the read-through PeerOrigin for #7. Background health probing / dns re-resolution start on Start(ctx). New itself does no network I/O beyond lb's initial resolution.
func (*Membership) Close ¶
func (m *Membership) Close()
Close is a no-op placeholder for symmetry (the peer pool stops with its Start context). Present so callers can `defer m.Close()` uniformly.
func (*Membership) Fallback ¶
func (m *Membership) Fallback() Fallback
Fallback reports the owner-mode fallback policy.
func (*Membership) IntendedOwner ¶
func (m *Membership) IntendedOwner(key string) (string, bool)
IntendedOwner returns the ring owner IGNORING health — the topological owner even when it is down. Used to detect "the owner is unavailable" so the caller can apply the strict-vs-degraded fallback.
func (*Membership) IsForwardedHop ¶
func (m *Membership) IsForwardedHop(h http.Header) bool
IsForwardedHop reports whether an inbound request was already forwarded to us by a peer in OUR region (the X-Cadish-Peer loop guard). Such a request must NOT be re-forwarded (read-through or owner-route) — it is served locally. A hop stamped by a DIFFERENT region is foreign (a separate cluster) and is treated as a fresh client request.
func (*Membership) IsSelf ¶
func (m *Membership) IsSelf(peerURL string) bool
IsSelf reports whether a peer base URL is this node.
func (*Membership) Mode ¶
func (m *Membership) Mode() Mode
Mode reports the configured coexistence mode.
func (*Membership) Owner ¶
func (m *Membership) Owner(key string) (string, bool)
Owner returns the base URL of the HEALTHY peer that owns key on the ring, and whether one exists. Walks past unhealthy/ejected peers (lb's health-aware ring walk), so the result is the node a sharded key currently lives on. Used by #8 to decide owner-vs-self.
func (*Membership) OwnsKey ¶
func (m *Membership) OwnsKey(key string) bool
OwnsKey reports whether THIS node is the (healthy) owner of key. When no healthy owner exists it returns false (the caller then applies fallback).
func (*Membership) PeerCount ¶
func (m *Membership) PeerCount() int
PeerCount reports the number of currently-known peer endpoints (post-resolution).
func (*Membership) PeerOrigin ¶
func (m *Membership) PeerOrigin() origin.Origin
PeerOrigin returns the read-through origin (#7) to compose BEFORE the real origin in a chain. Never nil.
func (*Membership) Region ¶
func (m *Membership) Region() string
Region reports the cluster region (the hop-header value).
func (*Membership) Self ¶
func (m *Membership) Self() string
Self reports this node's normalized peer URL.
func (*Membership) Start ¶
func (m *Membership) Start(ctx context.Context)
Start launches the peer pool's background workers (active health probing + dynamic re-resolution), bound to ctx. Idempotent.
type Mode ¶
type Mode int
Mode selects how #7 (read-through) and #8 (ownership routing) coexist.
const ( // ModeReadThrough (the default) is #7 ONLY: opportunistic peer read-through. // On a local miss, the owning peer is asked for the key (hop-guarded); a peer // hit is streamed-and-stored locally, a peer miss falls through to origin. // Requests are never re-routed — every node may serve any key. ModeReadThrough Mode = iota // ModeOwner is #8 PRIMARY with #7 as the fallback: each key has one owner on // the ring. A request landing on a non-owner is reverse-proxied to the owner // so the object is cached once per region. If the owner is down, Fallback // decides between serving locally (strict) or the next ring node (degraded); // the degraded path also tries peer read-through before origin. ModeOwner )