metrics

package
v1.5.7 Latest Latest
Warning

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

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

Documentation

Overview

Package metrics is ctrlplane's in-process time-series store for per-instance resource samples (CPU / memory / network) and optional application-layer metrics (request rate, latency P95 scraped from /metrics endpoints).

The package is intentionally lightweight: an in-memory ring buffer per instance, a 10s polling loop, and SSE-friendly fan-out for live consumers. Historical buckets are downsampled at query time. Samples are lost on restart — operators who need durable metrics push samples into Prometheus / VictoriaMetrics / etc. via the observability extension.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AttachToEvents

func AttachToEvents(svc Service, bus event.Bus) event.Subscription

AttachToEvents wires Track / Untrack to instance lifecycle events so the poller starts when an instance comes up and stops when it goes away. Returns the bus subscription so the caller can detach at shutdown if needed.

We track on InstanceCreated + InstanceStarted so a restart cycle (Stop → Start) re-attaches the poller without a fresh Create. Untrack on Deleted only — Stopped + Suspended keep the ring buffer around so the UI keeps showing the last-known sparkline even while the workload is paused.

Types

type Config

type Config struct {
	// PollInterval is how often the poller takes a sample per
	// tracked instance. Default 10s.
	PollInterval time.Duration

	// RetentionCapacity is the per-instance ring-buffer capacity.
	// At 10s interval, 60480 samples = 7d. Default 60480.
	RetentionCapacity int

	// WatchBuffer is the per-subscriber channel buffer. Default 64.
	WatchBuffer int
}

Config tunes the service.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns sensible defaults for dev + small prod.

type RangeQuery

type RangeQuery struct {
	Since      time.Time
	Until      time.Time
	Resolution time.Duration
}

RangeQuery describes a query window. Resolution=0 means "auto" — the store picks a bucket size that targets ~120 points across the window so sparkline rendering stays fluid regardless of range.

type Sample

type Sample struct {
	At time.Time `json:"at"`

	// Resource — always populated when poll succeeds.
	CPUPercent            float64 `json:"cpu_percent"`
	MemoryUsedMB          int     `json:"memory_used_mb"`
	MemoryLimitMB         int     `json:"memory_limit_mb"`
	NetworkInBytesPerSec  float64 `json:"network_in_bytes_per_sec"`
	NetworkOutBytesPerSec float64 `json:"network_out_bytes_per_sec"`

	// Application — populated only when the workload exposes a
	// /metrics endpoint AND ctrlplane is configured to scrape it.
	// Zero values mean "no signal", not "zero rate".
	RequestsPerSec float64 `json:"requests_per_sec,omitempty"`
	LatencyP95Ms   float64 `json:"latency_p95_ms,omitempty"`
}

Sample is one point-in-time measurement for a single instance. Resource fields (CPU/Memory/Network) come from the provider's Resources() call. Application fields (RequestsPerSec, LatencyP95Ms) come from optional /metrics scraping — they're zero-valued when no scraper is configured for the workload.

type Sampler

type Sampler interface {
	Sample(ctx context.Context, instanceID id.ID) (*Sample, error)
}

Sampler is what the poller uses to take a one-shot reading. The concrete impl wraps instance.Service so we can resolve the provider per-instance without metrics depending on the full instance package.

func NewInstanceSampler

func NewInstanceSampler(instances instance.Service) Sampler

NewInstanceSampler wires a Sampler that drives off instance.Service.

type Series

type Series []Sample

Series is a sorted slice of Samples. Callers should treat the slice as immutable — buckets returned from Range are copies.

type Service

type Service interface {
	// Range returns downsampled samples for the given instance and
	// window. Returns nil with no error when no samples are stored
	// (instance never tracked, or tracking just started).
	Range(ctx context.Context, instanceID id.ID, q RangeQuery) (Series, error)

	// Watch returns a channel that receives every newly-pushed
	// sample for instanceID until ctx cancels. Buffered; drops on
	// slow consumers.
	Watch(ctx context.Context, instanceID id.ID) (<-chan Sample, error)

	// Latest returns the most recent sample, ok=false when none.
	Latest(instanceID id.ID) (Sample, bool)

	// Track / Untrack are lifecycle hooks. Track starts the poller
	// for the given instance; Untrack stops it and discards stored
	// samples. Idempotent.
	Track(instanceID id.ID)
	Untrack(instanceID id.ID)
}

Service is the public read/subscribe surface for instance metrics. Writes happen through the poller, which the service owns and runs internally — callers never push samples directly.

func NewService

func NewService(sampler Sampler, cfg Config) Service

NewService wires the metrics service. The Sampler is invoked once per PollInterval per tracked instance; failures are silently dropped (transient container restarts shouldn't show up as poller errors in the request path).

Jump to

Keyboard shortcuts

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