Documentation
¶
Overview ¶
Package concurrency is the autoscaling setup shared by the migrate, move and sync runners: given the Aurora probe for every target server, Engage decides whether autoscaling engages, derives the thread and flush bounds (Derive), and overrides the configured thread counts when it does. On an instance too small to scale it selects small-instance mode instead, which fixes every pool at one worker and shrinks the copy chunks. Either way, on Aurora the thread counts come from the instance, not from --threads and --write-threads, except where autoscaling falls back (a failed probe or a non-Aurora sibling target).
The three runners used to each derive these with their own copy of the rules. What stays with each runner is genuinely topology-specific: which servers it watches and how many targets share each one. Fitting the bounds to a runner's connection pool is the runner's, after Engage (dbconn.ReadBoundsForPool).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Plan ¶
type Plan struct {
Engaged bool
// SmallInstance is set instead of Engaged when a target is below
// autoscale.MinVCPUs (autoscale.IsSmallInstance). The controllers stay
// off; Engage has already set the flags' thread counts and chunk size, and
// FlushConcurrency holds the feed's width. FlushBatchSize stays zero, the
// change package's default. The thread bounds stay zero: callers size
// fixed pools from the flags, as they do for a disengaged plan.
SmallInstance bool
// ReadStart and MaxReadThreads bound the read side: the copier's read
// workers and the checksum's workers.
ReadStart, MaxReadThreads int
// WriteStart and MaxWriteThreads bound each target's apply workers.
WriteStart, MaxWriteThreads int
// FlushConcurrency and FlushBatchSize shape each change feed's drain. They
// are a starting point, not autoscaled: the drain has its own AIMD
// controller keyed on lock contention.
FlushConcurrency, FlushBatchSize int
}
Plan is the outcome of Engage. When neither Engaged nor SmallInstance is set every other field is zero: the configured thread counts stand, the controllers stay off, and the change feed keeps the change package's flush defaults.
func Derive ¶
Derive sizes a plan from the targets' capacity. ok is false when there is no target or any target is below autoscale.MinVCPUs (Engage selects small-instance mode for those before it calls Derive).
Every bound comes from the smallest target, not the sum of target capacity: skewed input can route every row to one shard, and the controllers scale all shards in lockstep on the busiest server's signal. A single-target run is the degenerate case of the same rules (one server, one shard, one source), so migrate, move and sync derive identical numbers for identical servers.
- Read side: autoscale.ReadBounds, divided by the shards on one server (a checksum reads every target for each chunk, so schemas on one server share its read budget) and capped by the client ceiling.
- Write side: autoscale.WriteStart, divided the same way, with the ceiling from throttler.ResolveMaxWriteThreads. Both are capped by the client ceiling split across targets, since each target runs its own workers.
- Flush: autoscale.FlushBounds, divided by sources × shards on one server (every feed's flush fans out to the busiest server) and capped by the client ceiling split across sources, but never below autoscale.MinFlushConcurrency — the width every run had before this was derived, so engaging never narrows a drain. With one source the floor never exceeds the client ceiling, because autoscale.ClientCeiling is at least autoscale.ClientThreadsPerCore (16); so for migrate and sync this is the old ceiling-capped width.
redoAware and commitLatencyEnabled decide whether the write ceiling may exceed the start (see throttler.ResolveMaxWriteThreads).
func Engage ¶
Engage is the autoscaling setup shared by migrate, move and sync. It decides whether autoscaling engages for these targets and, when it does, overrides flags.Threads and flags.WriteThreads with the plan's starting sizes.
When any target is below autoscale.MinVCPUs (autoscale.IsSmallInstance) Engage selects small-instance mode instead: Threads and WriteThreads become autoscale.SmallInstanceThreads, TargetChunkSize is lowered to autoscale.SmallInstanceTargetChunkBytes, and the plan carries autoscale.SmallInstanceFlushConcurrency. Any one target is enough, because the counts are shared by every target and the smallest must not run out of memory or be steered by a signal too coarse to steer it. That holds even when another target is not Aurora or its probe failed, which would otherwise disable autoscaling. SkipAutoscaling turns off both: Engage returns a zero plan and changes nothing.
Otherwise autoscaling engages only when every target supplies a usable Aurora load signal; anything else leaves the configured counts alone and returns a disengaged plan. All targets or none, because the controllers scale every target in lockstep on one composite signal. A failed probe warns (an operator has something to fix: a locked-down perf_schema or an under-granted user); a target that is simply not Aurora is an ordinary configuration and logs at Info.
Bounds are derived once, at startup, so a target instance resize needs a restart to be picked up.
The only error is a failed vCPU read on a target already confirmed Aurora.
func (Plan) Copier ¶
func (p Plan) Copier() copier.AutoscaleConfig
Copier returns the copier's autoscaling config for this plan. The zero value when the plan did not engage.
type Request ¶
type Request struct {
// Targets holds one entry per distinct target server. Autoscaling engages
// only when every one supplies an Aurora load signal. Small-instance mode is
// selected instead when any one confirmed as Aurora is below
// autoscale.MinVCPUs, whatever the others are.
Targets []Target
// Sources is how many change feeds fan their flushes out to the targets.
// Zero means one.
Sources int
// VCPUs reads a target's vCPU count. Nil means throttler.AuroraVCPUs; tests
// replace it because CI has no Aurora instance.
VCPUs func(context.Context, *sql.DB) (int, error)
// ClientCeiling bounds derived counts by this host's CPU. Zero means
// autoscale.ClientCeiling().
ClientCeiling int
Logger *slog.Logger
}
Request is everything Engage needs beyond the flags.
type Target ¶
type Target struct {
// DB reads the server's vCPU count.
DB *sql.DB
// Aurora is the probe that built this server's load throttlers. Sizing
// from the same result is what makes the signal autoscaling scales against
// the one throttling the run.
Aurora throttler.AuroraResult
// Shards is how many targets live on this server. Zero means one.
Shards int
// Name labels log lines. Empty for single-target runners.
Name string
}
Target is one distinct target server a runner watches for load. Targets that share a server (schemas on one host) are one Target with Shards > 1: they share its capacity, so they must share its budget.
type Topology ¶
type Topology struct {
// VCPUs holds one entry per distinct target server.
VCPUs []int
// ShardsPerHost is the most targets on one server. Zero means one.
ShardsPerHost int
// Targets is the total number of targets. Zero means one.
Targets int
// Sources is the number of change feeds. Zero means one.
Sources int
}
Topology describes the servers a plan is sized for.