Documentation
¶
Overview ¶
Package checkout is the synthetic reference system: a three-stage payment lifecycle (auth, capture, settle) with seeded traffic on an hour-of-week curve, fault-injection knobs for the degradation loci, and an in-memory ledger that serves as ground truth for the engine.
Capture and settle are capacity-limited FIFO stages (per-minute worker capacity, fixed service delay); a queue-consumer-stall fault sets capacity to zero for its window, so a real backlog forms and drains at the stage's capacity afterwards. The api faults reject (5xx) or lose (latency abandonment) arrivals; an upstream blackout suppresses arrivals entirely, with an optional recovered fraction re-arriving after the outage. The ledger is omniscient: it records abandoned transactions and suppressed demand that real telemetry could never see — the asymmetry the engine's evidence labels are about.
The simulation is discrete-event on a virtual clock (minute resolution, single-threaded): identical Config yields a byte-identical ledger on the same toolchain and architecture. Arrival sampling consumes math.Exp, which Go does not promise bit-identical across platforms, so golden expected values must be generated by CI (ubuntu/amd64), never on a laptop. All fixtures produced here are synthetic by construction — never derived from real data.
Index ¶
Constants ¶
const ( NoEnterprise = -1.0 // EnterpriseFraction: all-SMB traffic InstantStage = -1 // CaptureDelayMin / SettleDelayMin: zero delay )
Explicit-zero sentinels. The zero value of each tuning knob means "use the default", so a scenario that wants a literal zero spells it with these. Any other negative value is a configuration error and Run panics.
Variables ¶
This section is empty.
Functions ¶
func DefaultCurve ¶
func DefaultCurve() [168]float64
DefaultCurve is a realistic hour-of-week arrival-rate curve (mean arrivals per minute), Monday 00:00 UTC = index 0. Shape: weekday business-hours peak (~6/min), evening shoulder, deep night trough (~0.4/min), weekends at roughly 60% of weekday volume. The absolute numbers only need to be plausible — the engine is judged against the ledger this curve generates, not against the real world.
Types ¶
type Config ¶
type Config struct {
Seed uint64
Start time.Time // aligned down to the minute
End time.Time
// Curve is the hour-of-week arrival-rate curve (mean arrivals per
// minute for each of the 168 hours, Monday 00:00 = index 0). Nil means
// DefaultCurve.
Curve *[168]float64
// EnterpriseFraction is the probability a customer is enterprise
// (default 0.1).
EnterpriseFraction float64
// Customers is the size of the synthetic customer pool (default 3000).
Customers int
// Stage service delays (defaults: capture 2 min, settle 5 min).
CaptureDelayMin int
SettleDelayMin int
// Stage worker capacities in transactions per minute (default 20 —
// comfortably above the default curve's 6/min peak, so the baseline
// has no contention). A consumer-stall fault forces 0 for its window.
// There is no literal-zero form: a consumer that is down is a
// queue-consumer-stall fault over the window, never a capacity of 0
// (which would make the FIFO cursor spin forever).
CaptureCapacityPerMin int
SettleCapacityPerMin int
// Faults active during the run. Each is validated; see FaultSpec.
Faults []FaultSpec
}
Config drives one simulation run. The zero values of the tuning knobs are replaced by defaults in Run (see the sentinels above for literal zeros).
type FaultKind ¶
type FaultKind string
FaultKind names the four degradation loci: API-level failures, API-level latency (abandonment), queue consumer stalls, and upstream blackouts (traffic never enters).
type FaultSpec ¶
type FaultSpec struct {
Kind FaultKind `yaml:"kind"`
From time.Time `yaml:"from"`
To time.Time `yaml:"to"`
// Rate: api-5xx failure probability, or api-latency abandonment
// probability, in (0, 1]. Semantics when faults overlap: each active
// fault rolls independently per transaction, so two 0.5 faults
// compound to ~0.75 — overlapping specs model overlapping causes.
// Abandonment short-circuits the 5xx roll (an abandoned request never
// reached the api), which makes rng consumption outcome-dependent;
// deterministic under a seed either way.
Rate float64 `yaml:"rate,omitempty"`
// Queue: which stage a queue-consumer-stall halts.
Queue Queue `yaml:"queue,omitempty"`
// Blackout recovery: the fraction of suppressed demand that returns
// after the outage, spread uniformly over the RecoveryWithin window.
// Zero fraction means the demand is simply gone.
RecoveredFraction float64 `yaml:"recovered_fraction,omitempty"`
RecoveryWithin time.Duration `yaml:"recovery_within,omitempty"`
}
FaultSpec is one active degradation over [From, To) — the declarative form used both in Go and as a row in the scenario YAML. Fields beyond the window apply per kind and are validated by Validate.
type GoldenWindow ¶
GoldenWindow declares the incident window a scenario's golden expected values are computed over, and the capture SLA used for breach counting. Explicit in the file — deriving it from fault windows would let a scenario edit silently move the goalposts.
type Ledger ¶
type Ledger struct {
Txns []Txn
}
Ledger is the ground truth: every synthetic transaction ever created, in creation order — including abandoned ones telemetry never saw.
type Result ¶
type Result struct {
Ledger Ledger
Suppressed []SuppressedMinute
Config Config
}
Result of a run: the ground-truth ledger, suppressed-demand record, and the effective config.
func Run ¶
Run executes the simulation and returns the ground-truth ledger. Identical Config (including Seed) yields a byte-identical ledger on the same toolchain and architecture — see the package doc for why golden values must come from CI. Run panics on invalid tuning knobs: a broken scenario must never produce plausible-looking ground truth.
type Scenario ¶
type Scenario struct {
Name string
Config Config
Golden GoldenWindow
}
Scenario is a named, file-loadable simulation configuration — the unit the testkit runs and the golden fixtures are written in.
func LoadScenario ¶
LoadScenario reads and validates a scenario YAML file. Every validation failure is an error with the field named — a broken scenario must never produce plausible-looking ground truth.
func ParseScenario ¶
ParseScenario parses scenario YAML bytes; see LoadScenario.
type State ¶
type State string
State is a transaction's lifecycle state in the ledger.
const ( StateCreated State = "created" // arrived, auth not yet attempted StateAuthed State = "authed" // auth ok, inside the capture stage StateCaptured State = "captured" // capture ok, inside the settle stage StateSettled State = "settled" // terminal success StateAuthFail State = "failed_auth" // terminal: rejected at the api (5xx) StateAbandoned State = "abandoned" // terminal: user gave up on api latency; telemetry never saw it StateCapFail State = "failed_capture" // terminal: capture-worker rejected )
type SuppressedMinute ¶
SuppressedMinute records upstream-blackout demand that never entered: Count arrivals that would have happened at Minute. Only the ground truth can know this; the engine's counterfactual leg is judged against it.
type Txn ¶
type Txn struct {
ID string
CustomerID string // pre-hashed form, e.g. "h:c000042"
Segment Segment
AmountMinor int64
Currency string
CreatedAt time.Time
AuthedAt time.Time
CapturedAt time.Time
SettledAt time.Time
State State
// Recovered marks demand that re-arrived after an upstream blackout;
// RecoveredFrom is the originating blackout's From instant, so a
// recovery stays attributed to the incident that suppressed it —
// even when the re-arrival was suppressed again by a later blackout
// on its way back.
Recovered bool
RecoveredFrom time.Time
}
Txn is one synthetic invoice payment — a ledger row. Amounts are int64 minor units; time fields are zero until the stage happens.