Documentation
¶
Overview ¶
pkg/autoscaler/autoscale_cross_metrics.go
CrossMetricsRegistry — a shared registry of AutoMetrics instances, one per operatorbox. Enables autoscale conditions to reference another operatorbox's live metrics via the cross.metrics.* namespace.
Usage in Katalog:
# The database-backed-app operatorbox scales based on the database
# operatorbox's queue depth — if the DB is overwhelmed, slow down.
operatorBox:
autoscale:
conditions:
when:
- field: cross.managed-database.metrics.queueDepth
greaterThan: "500"
do:
workers: 2 # back off while DB is under load
Resolution:
cross.<crd>.metrics.<field>
→ CrossMetricsRegistry.Get(<crd>).Get("metrics.<field>")
This is read-only. An operatorbox can observe another's metrics but cannot modify them. The same principle as cross-CRD CR state observation.
Thread safety: Register is called once at startup per operatorbox (under Kordinator init). Get is called on every autoscale tick (concurrent reads). sync.Map provides safe concurrent access with no lock contention on reads.
pkg/autoscaler/autoscale_metrics.go
AutoMetrics — live operatorbox metrics read directly from the runtime.
The autoscaler evaluates conditions against these metrics on every tick. All reads are atomic — no locks, no API calls, no informer lookups.
Metric fields are exposed under the "metrics.*" namespace in autoscale conditions and resolve through AutoMetrics.Get(field).
pkg/autoscaler/autoscale_semaphore.go
ResizableSemaphore — a weighted semaphore whose capacity can be changed at runtime.
The worker pool in Orkestra is gated by this semaphore rather than being a fixed goroutine count. All worker goroutines run continuously in a loop; the semaphore controls how many may enter the reconcile section simultaneously.
Increasing capacity: new goroutines can acquire immediately. Decreasing capacity: goroutines currently inside reconcile finish their current work. Goroutines blocked at Acquire wait until capacity is available. In-flight reconciles are never interrupted.
This is the correct primitive for operator autoscaling because:
- Resize is O(1) — no goroutine creation or cancellation
- In-flight reconciles complete cleanly after a scale-down
- The semaphore is the single source of truth for concurrency
pkg/autoscaler/autoscale_worker_info.go
WorkerInfo — the complete picture of an operatorbox's worker state. Returned by the CRD handler endpoint and shown in the Control Center.
The Control Center shows:
Configured: 4 ← CRD declared workers (always the baseline) Effective: 12 ← current semaphore capacity (may be overridden) InFlight: 7 ← goroutines currently reconciling Idle: 5 ← effective - inFlight Max: 12 ← highest workers ever from do: block (pre-allocated) Autoscaler: ✓ enabled, override active
This replaces the misleading "N pending workers" display that showed all pre-allocated goroutines as pending regardless of autoscale state.
pkg/autoscaler/autoscaler.go
Operatorbox autoscaler — evaluates conditions on a ticker and applies or restores worker/queue/resync overrides.
One Autoscaler is created per operatorbox that declares autoscale: in its operatorBox: block. It runs a single goroutine for the lifetime of the operatorbox and stops cleanly when the context is cancelled.
The autoscaler has no persistent state. A restart always begins from the declared CRD baseline. Overrides applied before a restart are lost — the next evaluation tick will re-apply them if conditions are still met.
Index ¶
- Variables
- func IsMetricField(field string) bool
- func OldResolveCrossMetric(registry *CrossMetricsRegistry, field string, source *orktypes.CrossSource) string
- func ResolveCrossMetric(registry *CrossMetricsRegistry, field string, source *orktypes.CrossSource) string
- type AutoMetrics
- type AutoscaleTarget
- type Autoscaler
- type CrossMetricsRegistry
- type ResizableSemaphore
- func (s *ResizableSemaphore) Acquire(ctx context.Context) error
- func (s *ResizableSemaphore) BusyPercent() float64
- func (s *ResizableSemaphore) Capacity() int
- func (s *ResizableSemaphore) Current() int
- func (s *ResizableSemaphore) IdlePercent() float64
- func (s *ResizableSemaphore) InFlight() int
- func (s *ResizableSemaphore) Release()
- func (s *ResizableSemaphore) Resize(newCap int)
- type WorkerInfo
Constants ¶
This section is empty.
Variables ¶
var GlobalCrossMetricsRegistry = &CrossMetricsRegistry{}
GlobalCrossMetricsRegistry is the process-wide registry. Populated during Kordinator startup, read by autoscalers at runtime.
Functions ¶
func IsMetricField ¶
IsMetricField returns true when the field path refers to an autoscale metric. Used by the condition resolver to route metric lookups to AutoMetrics.Get.
func OldResolveCrossMetric ¶ added in v0.4.5
func OldResolveCrossMetric(registry *CrossMetricsRegistry, field string, source *orktypes.CrossSource) string
ResolveCrossMetric resolves a cross.metrics field path of the form:
cross.<crd>.metrics.<field>
Resolution order:
- GlobalCrossMetricsRegistry — zero hops, same-binary CRDs
- source.Endpoint HTTP call — one hop, cross-binary CRDs The endpoint must be the remote operator's /katalog/{crd} URL. The response is expected to carry a "metrics" object with the same field names as AutoMetrics.AsMap().
Returns "" when neither path finds a value.
func ResolveCrossMetric ¶ added in v0.1.9
func ResolveCrossMetric( registry *CrossMetricsRegistry, field string, source *orktypes.CrossSource, ) string
Types ¶
type AutoMetrics ¶
type AutoMetrics struct {
// contains filtered or unexported fields
}
AutoMetrics holds live operatorbox runtime metrics for autoscale evaluation. All counters are atomic int64 values — safe for concurrent read/write from worker goroutines and the autoscaler loop.
func NewAutoMetrics ¶
func NewAutoMetrics(sem *ResizableSemaphore) *AutoMetrics
NewAutoMetrics returns an initialised AutoMetrics for the given semaphore.
func (*AutoMetrics) AsMap ¶
func (m *AutoMetrics) AsMap() map[string]interface{}
func (*AutoMetrics) ErrorRatePercent ¶
func (m *AutoMetrics) ErrorRatePercent() float64
ErrorRatePercent returns the curent error rate i percentage
func (*AutoMetrics) Get ¶
func (m *AutoMetrics) Get(field string) string
Get returns the string representation of a metric field for condition evaluation. Field names follow the metrics.* convention:
metrics.workersBusyPercent → "73.5" metrics.workersIdlePercent → "26.5" metrics.queueDepth → "342" metrics.reconcileDurationP95Ms → "47" metrics.errorRatePercent → "0.2"
Returns "" for unknown fields — evaluates as notExists in condition checks.
func (*AutoMetrics) RecordReconcile ¶
func (m *AutoMetrics) RecordReconcile(duration time.Duration, failed bool)
RecordReconcile records one completed reconcile with its duration and outcome. Called at the end of every reconcile, success or failure.
func (*AutoMetrics) SetQueueDepth ¶
func (m *AutoMetrics) SetQueueDepth(depth int64)
SetQueueDepth updates the current queue depth. Called by the workqueue wrapper on every enqueue and dequeue.
type AutoscaleTarget ¶
type AutoscaleTarget interface {
// ResizeWorkers adjusts the worker pool to the given concurrency.
ResizeWorkers(n int)
// SetQueueDepthLimit adjusts the maximum queue depth.
SetQueueDepthLimit(n int)
// SetResyncInterval adjusts how frequently all CRs are re-enqueued.
SetResyncInterval(d time.Duration)
}
AutoscaleTarget is implemented by the operatorbox runtime components that the autoscaler controls. Kept minimal — autoscaler only calls these three methods, nothing else.
type Autoscaler ¶
type Autoscaler struct {
// contains filtered or unexported fields
}
Autoscaler evaluates conditions and applies/restores overrides for one operatorbox.
func NewAutoscaler ¶
func NewAutoscaler( crdKind string, spec *orktypes.AutoscaleSpec, baseline orktypes.AutoscaleBaseline, target AutoscaleTarget, metrics *AutoMetrics, crossDecls []orktypes.CrossCRDDeclaration, ) *Autoscaler
NewAutoscaler constructs an Autoscaler for one operatorbox. baseline is captured from the CRD's declared configuration before startup. crossDecls is the operatorBox.cross slice — used to resolve source fallback for autoscale conditions that reference cross.<crd>.metrics.* without an explicit source: block on the condition itself.
func (*Autoscaler) Run ¶
func (a *Autoscaler) Run(ctx context.Context)
Run starts the autoscale evaluation loop. Blocks until ctx is cancelled. Intended to be called in a dedicated goroutine.
func (*Autoscaler) Snapshot ¶ added in v0.1.9
func (a *Autoscaler) Snapshot() *autoscalerStateSnapshot
Snapshot returns a point-in-time snapshot of the Autoscaler's state. Called by the CRD handler without holding any lock — reads atomic values.
type CrossMetricsRegistry ¶ added in v0.1.9
type CrossMetricsRegistry struct {
// contains filtered or unexported fields
}
CrossMetricsRegistry holds AutoMetrics for all registered operatorboxes. Keyed by lowercase CRD crd name — the same key used in cross: declarations.
func (*CrossMetricsRegistry) Get ¶ added in v0.1.9
func (r *CrossMetricsRegistry) Get(crd string) *AutoMetrics
Get returns the AutoMetrics for the given crd, or nil if not registered. crd is normalized to lowercase — "ManagedDatabase" and "manageddatabase" are equivalent.
func (*CrossMetricsRegistry) Register ¶ added in v0.1.9
func (r *CrossMetricsRegistry) Register(crd string, metrics *AutoMetrics)
Register registers an operatorbox's AutoMetrics under its crd name. Called once per operatorbox during Kordinator startup. crd is normalized to lowercase for consistent lookup.
type ResizableSemaphore ¶
type ResizableSemaphore struct {
// contains filtered or unexported fields
}
ResizableSemaphore is a counting semaphore whose capacity can be changed at runtime without interrupting goroutines currently holding a token.
func NewResizableSemaphore ¶
func NewResizableSemaphore(capacity int) *ResizableSemaphore
NewResizableSemaphore returns a semaphore with the given initial capacity.
func (*ResizableSemaphore) Acquire ¶
func (s *ResizableSemaphore) Acquire(ctx context.Context) error
Acquire acquires one token, blocking until one is available or ctx is done. Returns ctx.Err() if the context is cancelled while waiting.
func (*ResizableSemaphore) BusyPercent ¶
func (s *ResizableSemaphore) BusyPercent() float64
BusyPercent returns the percentage of capacity currently in use.
func (*ResizableSemaphore) Capacity ¶
func (s *ResizableSemaphore) Capacity() int
Capacity returns the current capacity.
func (*ResizableSemaphore) Current ¶
func (s *ResizableSemaphore) Current() int
Current returns the current number of tokens.
func (*ResizableSemaphore) IdlePercent ¶
func (s *ResizableSemaphore) IdlePercent() float64
IdlePercent returns the percentage of capacity currently available.
func (*ResizableSemaphore) InFlight ¶
func (s *ResizableSemaphore) InFlight() int
InFlight returns the number of tokens currently held (goroutines inside reconcile).
func (*ResizableSemaphore) Release ¶
func (s *ResizableSemaphore) Release()
Release releases one token and notifies a waiter if any are queued.
func (*ResizableSemaphore) Resize ¶
func (s *ResizableSemaphore) Resize(newCap int)
Resize changes the semaphore capacity.
Scale up (newCap > old): immediately notifies waiting goroutines up to the new capacity. Each notification allows one waiter to proceed.
Scale down (newCap < old): the new capacity takes effect immediately for new Acquire calls. Goroutines already holding tokens complete their work normally — they are not interrupted. The effective concurrency converges to newCap as holders release.
Resize is safe to call concurrently with Acquire and Release.
type WorkerInfo ¶ added in v0.1.9
type WorkerInfo struct {
// Configured is the baseline workers declared in the CRD entry.
// This is what the operatorbox returns to after an autoscale override ends.
Configured int `json:"configured"`
// Effective is the current semaphore capacity — the real concurrency limit.
// Equals Configured when no autoscale override is active.
// Equals the override value when autoscale is active.
Effective int `json:"effective"`
// InFlight is the number of goroutines currently inside a reconcile call.
// InFlight <= Effective always.
InFlight int `json:"inFlight"`
// Idle is how many effective worker slots are available right now.
// Idle = Effective - InFlight.
Idle int `json:"idle"`
// Max is the maximum workers the autoscaler can scale to (from do.workers).
// Zero when autoscaler is not declared.
// This is pre-allocated — the goroutine pool is sized to Max at startup.
Max int `json:"max,omitempty"`
// AutoscalerEnabled is true when an autoscale: block is declared.
AutoscalerEnabled bool `json:"autoscalerEnabled"`
// OverrideActive is true when an autoscale override is currently applied.
OverrideActive bool `json:"overrideActive,omitempty"`
// OverrideWorkers is the current override value when OverrideActive is true.
OverrideWorkers int `json:"overrideWorkers,omitempty"`
// QueueDepth is the current number of items in the workqueue.
QueueDepth int64 `json:"queueDepth"`
// QueueDepthConfigured is the baseline queueDepth from the CRD entry.
QueueDepthConfigured int `json:"queueDepthConfigured"`
// QueueDepthEffective is the current queue depth limit (baseline or override).
QueueDepthEffective int `json:"queueDepthEffective"`
// BusyPercent is InFlight/Effective as a percentage. Useful for UI gauges.
BusyPercent float64 `json:"busyPercent"`
// ResyncEffective is the current resync interval (baseline or override).
ResyncEffective string `json:"resyncEffective"`
// ResyncConfigured is the baseline resync from the CRD entry.
ResyncConfigured string `json:"resyncConfigured"`
}
WorkerInfo is the serializable worker state for the CRD handler API. Keyed as "workers" in the CRD detail JSON response.
func BuildWorkerInfo ¶ added in v0.1.9
func BuildWorkerInfo( sem *ResizableSemaphore, metrics *AutoMetrics, configured int, configuredQueueDepth int, configuredResync string, maxWorkers int, autoscalerEnabled bool, autoscalerState *autoscalerStateSnapshot, ) WorkerInfo
BuildWorkerInfo constructs the WorkerInfo from the live operatorbox state. Called by the CRD handler on every /katalog/{crd} request — reads are O(1).