canary

package
v0.8.8 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package canary decides when a staged canary release advances, promotes or rolls back, and runs the level-triggered reconciler that applies it.

Index

Constants

View Source
const (
	// MaxWeight is the largest share ingress gives a canary.
	MaxWeight = canarypolicy.MaxWeight
	// Full is the stage value that means promote.
	Full = canarypolicy.Full
)
View Source
const (
	EnvStages       = canarypolicy.EnvStages
	EnvInterval     = canarypolicy.EnvInterval
	EnvMaxErrorRate = canarypolicy.EnvMaxErrorRate
	EnvMaxP95Ratio  = canarypolicy.EnvMaxP95Ratio
	EnvMinRequests  = canarypolicy.EnvMinRequests
	EnvP95FloorMS   = canarypolicy.EnvP95FloorMS
	EnvTick         = canarypolicy.EnvTick
)

The APP_CANARY_* variable names.

Variables

View Source
var (
	// DefaultPolicy returns the env-tuned default policy.
	DefaultPolicy = canarypolicy.DefaultPolicy
	// DefaultLimits returns the env-tuned global limits.
	DefaultLimits = canarypolicy.DefaultLimits
	// ParseStages parses a comma separated stage list.
	ParseStages = canarypolicy.ParseStages
	// ValidateStages checks a stage schedule.
	ValidateStages = canarypolicy.ValidateStages
	// OSEnv reads the process environment.
	OSEnv = canarypolicy.OSEnv
)

Functions

func Breach

func Breach(in Input) (bool, string)

Breach reports whether the canary is worse than the baseline beyond the policy, with a reason. Too few canary requests is never a breach.

Types

type Action

type Action int

Action is what the reconciler should do for one canary this tick.

const (
	// Hold leaves the canary as it is.
	Hold Action = iota
	// Advance moves to the next stage.
	Advance
	// Promote deploys the canary image to the stable app.
	Promote
	// Rollback removes the canary because it breached a threshold.
	Rollback
)

func (Action) String

func (a Action) String() string

type Actions

type Actions interface {
	Promote(ctx context.Context, rel store.CanaryRelease) error
	Rollback(ctx context.Context, rel store.CanaryRelease, reason string) error
}

Actions are the side effects a decision can trigger. Both must be idempotent: a tick that fails half way is simply repeated.

type Decision

type Decision struct {
	Action Action
	Reason string
}

Decision is the verdict for one tick and the reason shown to the operator.

func Decide

func Decide(in Input) Decision

Decide is the pure stage machine: roll back on a breach, hold until the stage has run its interval with enough traffic, then advance or promote.

type Dials

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

Dials is the shared record of which upstream is which release.

func DefaultDials

func DefaultDials() *Dials

DefaultDials is the process-wide record the ingress controller writes and the reconciler reads, mirroring ingress.DefaultRequestStats.

func NewDials

func NewDials() *Dials

NewDials returns an empty record.

func (*Dials) Delete

func (d *Dials) Delete(app string)

Delete forgets app, used when its canary is not receiving traffic.

func (*Dials) Get

func (d *Dials) Get(app string) (Pair, bool)

Get returns app's upstreams.

func (*Dials) Set

func (d *Dials) Set(app string, p Pair)

Set records app's current upstreams.

type Input

type Input struct {
	Policy Policy
	Limits Limits
	// Stage is the index into Policy.Stages currently serving.
	Stage int
	// Elapsed is how long this stage has been observed.
	Elapsed  time.Duration
	Canary   Sample
	Baseline Sample
}

Input is everything Decide needs; it holds no clock or I/O.

type Limits

type Limits = canarypolicy.Limits

Limits are the global judgement knobs.

type Pair

type Pair struct {
	Stable string
	Canary string
}

Pair is the ingress upstream addresses (host:port) of an app's stable release and its canary, as the ingress controller last routed them.

type Policy

type Policy = canarypolicy.Policy

Policy is the stage schedule and thresholds of one canary.

type Progress

type Progress struct {
	Stage          int     `json:"stage"`
	StageCount     int     `json:"stage_count"`
	ElapsedSecs    int     `json:"elapsed_secs"`
	IntervalSecs   int     `json:"interval_secs"`
	CanaryRequests uint64  `json:"canary_requests"`
	CanaryErrRate  float64 `json:"canary_error_rate"`
	CanaryP95MS    float64 `json:"canary_p95_ms"`
	StableRequests uint64  `json:"stable_requests"`
	StableErrRate  float64 `json:"stable_error_rate"`
	StableP95MS    float64 `json:"stable_p95_ms"`
	Next           string  `json:"next"`
	Breaching      bool    `json:"breaching"`
}

Progress is the live view of a staged canary for the dashboard, CLI and MCP.

type Reconciler

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

Reconciler converges staged canaries toward their desired stage. Desired state is the release row; the per-stage observation window is the only in-memory state and a restart just reopens it.

func NewReconciler

func NewReconciler(s Store, stats Stats, dials *Dials, actions Actions, limits Limits, now func() time.Time, logger *slog.Logger) *Reconciler

NewReconciler builds a reconciler. now may be nil for the wall clock.

func (*Reconciler) Progress

func (r *Reconciler) Progress(rel store.CanaryRelease) (Progress, bool)

Progress returns the current observation of rel, false when it is not being observed (not staged, paused, or not yet receiving traffic).

func (*Reconciler) Reconcile

func (r *Reconciler) Reconcile(ctx context.Context) error

Reconcile evaluates every staged canary once.

func (*Reconciler) Run

func (r *Reconciler) Run(ctx context.Context)

Run reconciles every tick until ctx is done.

type Sample

type Sample struct {
	Requests uint64
	Errors   uint64
	Latency  [telemetry.LatencyBucketCount]uint64
}

Sample is one release's traffic over a window.

func SampleFrom

func SampleFrom(u telemetry.UpstreamStat) Sample

SampleFrom converts an ingress upstream counter.

func (Sample) ErrorRate

func (s Sample) ErrorRate() float64

ErrorRate is the 5xx fraction, 0 for an empty window.

func (Sample) P95

func (s Sample) P95() float64

P95 is the 95th percentile latency in milliseconds, 0 for an empty window.

func (Sample) Sub

func (s Sample) Sub(earlier Sample) Sample

Sub returns the traffic since earlier, clamping at zero if a counter reset.

type Stats

type Stats interface {
	UpstreamTotals() map[string]telemetry.UpstreamStat
}

Stats reads cumulative per-upstream counters; ingress.RequestStats satisfies it.

type Store

type Store interface {
	ListCanaryReleases(ctx context.Context) ([]store.CanaryRelease, error)
	SetCanaryRelease(ctx context.Context, c store.CanaryRelease) error
}

Store is the persistence the reconciler needs; *store.DB satisfies it.

Jump to

Keyboard shortcuts

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