Documentation
¶
Overview ¶
Package autoscale provides a provider-agnostic replica autoscale controller. It evaluates each opted-in app's session saturation on a fixed interval and drives the incremental scale primitives to converge the replica count on a target average sessions-per-replica, within the app's configured bounds and the runtime ceiling. It never scales worker hosts and never touches apps that have not opted in.
Index ¶
Constants ¶
const ( ActionScaleUp = "autoscale_scale_up" ActionScaleDown = "autoscale_scale_down" )
Action constants for autoscale audit events.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AuditRecorder ¶
type AuditRecorder interface {
LogAuditEvent(p db.AuditEventParams)
}
AuditRecorder is the subset of *db.Store the controller needs to record scale events. db.Store satisfies it; main.go passes store directly.
type AutoscaleMetrics ¶
type AutoscaleMetrics interface {
RecordAutoscaleScale(direction string) // direction: "up" or "down"
}
AutoscaleMetrics records autoscale scale events for Prometheus. *metrics.Registry satisfies it. Kept as an interface so the autoscale package does not import Prometheus.
type Config ¶
type Config struct {
// ScanInterval is how often the controller evaluates opted-in apps.
ScanInterval time.Duration
// Cooldown is the minimum time between successive scale actions on one app.
Cooldown time.Duration
// DrainGrace bounds how long ScaleDown waits for sessions to finish.
DrainGrace time.Duration
// RejectWindow is the look-back window for the pool-saturated reject signal.
RejectWindow time.Duration
// DefaultTarget is the fallback target fraction when an app's own target is 0.
DefaultTarget float64
// DefaultCap is the fallback per-replica session cap when an app's own cap is 0.
DefaultCap int
// RuntimeMax is the runtime-wide replica ceiling.
RuntimeMax int
}
Config holds the controller's resolved runtime settings.
type Controller ¶
type Controller struct {
// contains filtered or unexported fields
}
Controller evaluates and converges replica counts. Its internal state is owned solely by the Run loop goroutine, so it needs no lock. The per-app cooldown is persisted (apps.last_autoscale_at, read via the app list each tick) so it survives process restart and failover, rather than living in process memory.
func New ¶
func New(cfg Config, lister Lister, signal Signal, scaler Scaler, recorder AuditRecorder, cooldown CooldownStore, log *slog.Logger) *Controller
New builds a controller. log may be nil, in which case the default logger is used.
func (*Controller) Run ¶
func (c *Controller) Run(ctx context.Context)
Run evaluates opted-in apps every ScanInterval until ctx is cancelled. Each tick reads the current time from the DB clock so the cooldown is measured against the same clock that stamped the last action, regardless of which instance is active. A DB-clock read failure skips the tick (the DB is the same one ListAutoscaleApps needs, so it would fail too).
func (*Controller) SetMetrics ¶
func (c *Controller) SetMetrics(m AutoscaleMetrics)
SetMetrics wires a recorder for autoscale Prometheus metrics. Called once at startup before Run; nil-safe so tests that do not need metrics can skip it. Mirrors lifecycle.Watcher.SetMetrics and fargate.Runtime.SetMetrics.
type CooldownStore ¶ added in v0.8.0
type CooldownStore interface {
NowEpoch() (int64, error)
SetAppLastAutoscaleAt(slug string, epoch int64) error
}
CooldownStore persists the per-app autoscale cooldown so it survives process restart and failover to a standby control-plane instance, and supplies the DB clock the cooldown is measured against. *db.Store satisfies it. The read side of the cooldown is the persisted db.App.LastAutoscaleAt the Lister returns each tick; SetAppLastAutoscaleAt is the write side. NowEpoch returns the DB clock so the armed timestamp and the cooldown check use one clock (immune to wall-clock skew between instances), not the local clock of whichever instance is active.
type Scaler ¶
type Scaler interface {
ScaleUp(slug string) (bool, error)
ScaleDown(slug string, grace time.Duration) (bool, error)
}
Scaler drives the incremental scale primitives. ScaleUp grows the pool by one replica; ScaleDown gracefully removes one. Both return (false, nil) for the benign no-op cases (ceiling reached, floor reached, app not running).