breakers

package
v0.2.5 Latest Latest
Warning

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

Go to latest
Published: Jun 19, 2026 License: AGPL-3.0 Imports: 9 Imported by: 0

Documentation

Overview

Package breakers provides named, dynamically-controllable on/off switches for individual functional surfaces of the rendezvous process. Operators flip a switch by writing a JSON file (<store-dir>/breakers.json); a file watcher reloads the state every 2 s and call sites consult the breaker before serving the affected request type.

Three states per breaker:

closed     — normal operation (default). Allow.
half_open  — log a warning, but allow. Use for canary testing
             or to observe call frequency without disrupting
             traffic.
open       — deny. The serving site returns an appropriate
             error to the caller (typically a 503-equivalent
             with the operator-supplied reason).

Names are flat strings, conventionally dotted paths reflecting the component and action being gated (e.g. "registry.register", "beacon.punch", "snapshot.write"). The Manager is permissive: it accepts any name on the read path; a missing breaker is treated as "closed" (allow). Unknown names in the JSON file are kept but silently unused — write-time tolerance lets operators stage a name before its call site is wired.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Watch

func Watch(
	m *Manager,
	path string,
	interval time.Duration,
	stop <-chan struct{},
)

Watch polls path for mtime+size changes every interval and atomically reloads breakers into the Manager when the file changes.

Operational contract — pinned by tests; identical in spirit to the rate-limit whitelist watcher:

  • Never blocks startup, the registry, the beacon, or any request path. Runs in its own goroutine; the only state it mutates is the Manager, via Replace.
  • Fail-open: missing file is fine (no breakers installed; every name returns Allow=closed via Manager's default-allow). Parse, stat, or apply errors are logged at WARN and leave whatever map is currently installed in place.
  • Idempotent: same file content (unchanged mtime + size) is a no-op.
  • File removal reverts the Manager to an empty map on the next tick (logged INFO).

File format — JSON object, name → {state, reason?}:

{
  "registry.register":       {"state": "closed"},
  "beacon.punch":            {"state": "open",     "reason": "maintenance"},
  "dashboard.public_stats":  {"state": "half_open","reason": "soaking pre-lockdown"}
}

state must be one of "closed", "half_open", "open" (case-insensitive, with the same aliases ParseState accepts). reason is operator-facing free text surfaced in error messages when a breaker is open.

Types

type Breaker

type Breaker struct {
	Name        string    `json:"name"`
	State       State     `json:"-"` // serialised via custom marshalling
	StateString string    `json:"state"`
	Reason      string    `json:"reason,omitempty"`
	UpdatedAt   time.Time `json:"updated_at,omitempty"`
}

Breaker is a snapshot of one named switch's state.

type Manager

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

Manager holds the live breaker map. Safe for concurrent use.

func New

func New() *Manager

New returns an empty Manager. No breakers are pre-registered; Allow on any unknown name returns (true, Closed) — the default- allow semantic ensures a missing JSON file or a misspelled name can never cut off service.

func (*Manager) Allow

func (m *Manager) Allow(name string) (bool, State)

Allow consults the named breaker and returns:

(true, Closed)   — normal operation
(true, HalfOpen) — caller should log a warning, then proceed
(false, Open)    — caller must NOT proceed; return an error

An unknown name is treated as Closed (default-allow) so a request path is never blocked by a typo. The state is returned alongside the bool so the caller can log appropriately without a second lookup.

func (*Manager) Delete added in v0.2.5

func (m *Manager) Delete(name string)

Delete removes a single named breaker. No-op when the name is not registered. Counters are preserved so post-incident review still shows traffic that flowed through the path. UpdatedAt of the file watcher will revert any change on the next reload, so callers that want durability should persist their intent to breakers.json too.

func (*Manager) Reason

func (m *Manager) Reason(name string) string

Reason returns the operator-supplied reason for the named breaker's current state, or "" if no breaker is registered. Use in error messages surfaced to callers when Allow returns false.

func (*Manager) Replace

func (m *Manager) Replace(breakers map[string]*Breaker)

Replace atomically swaps the full breaker map. Used by the file watcher to apply a fresh JSON snapshot in one shot.

func (*Manager) Set

func (m *Manager) Set(name string, state State, reason string)

Set installs or replaces one breaker. Audit-logged via the Manager's updated-at timestamp; callers who want a separate event sink should wrap this method.

func (*Manager) SetClock

func (m *Manager) SetClock(fn func() time.Time)

SetClock overrides the time source (tests).

func (*Manager) Size

func (m *Manager) Size() int

Size returns the current count of registered breakers.

func (*Manager) Snapshot

func (m *Manager) Snapshot() []Breaker

Snapshot returns a copy of every registered breaker. Safe to mutate. Order is insertion-stable only within a single call; callers needing a sorted view should sort the slice themselves.

func (*Manager) StatsSnapshot added in v0.2.5

func (m *Manager) StatsSnapshot() []Stats

StatsSnapshot returns one Stats entry per known name — union of registered breakers and seen-counters. Sorted by name for stable output. A name with counters but no registered breaker reports state="closed" since that's its effective behaviour (the default).

type State

type State int

State enumerates the three configurable states a breaker can be in.

const (
	// Closed means normal operation: Allow returns (true, Closed).
	Closed State = iota
	// HalfOpen means warn-but-allow: Allow returns (true, HalfOpen).
	// Call sites should log a warning so operators can observe.
	HalfOpen
	// Open means deny: Allow returns (false, Open). Call sites should
	// surface the breaker's reason in any error returned to the caller.
	Open
)

func ParseState

func ParseState(s string) (State, error)

ParseState parses a wire-format state string. Accepts the canonical "closed" / "half_open" / "open" plus a few common aliases ("half-open", "halfopen") so the JSON file is forgiving. Unknown values return an error so a typo doesn't silently become Closed.

func (State) String

func (s State) String() string

String returns the wire-format name ("closed", "half_open", "open"), matching the JSON file format.

type Stats added in v0.2.5

type Stats struct {
	Name         string     `json:"name"`
	State        string     `json:"state"`
	Reason       string     `json:"reason,omitempty"`
	UpdatedAt    *time.Time `json:"updated_at,omitempty"`
	AllowedTotal int64      `json:"allowed_total"`
	DeniedTotal  int64      `json:"denied_total"`
	LastDeniedAt *time.Time `json:"last_denied_at,omitempty"`
}

Stats is the per-breaker live snapshot returned via /api/breakers. Allowed + Denied are monotonic since process start (so a Prom/Grafana scraper can rate() them). LastDeniedAt is nil when Denied == 0.

Jump to

Keyboard shortcuts

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