Documentation
¶
Index ¶
- Constants
- type Component
- type Metrics
- func (m *Metrics) AddEventDrops(subscriberName, eventType string, count uint64)
- func (m *Metrics) AddTimeAsLeader(seconds float64)
- func (m *Metrics) CarryConfigValidation(previous *Metrics) error
- func (m *Metrics) RecordAdmissionRenderSlotWait(wait time.Duration)
- func (m *Metrics) RecordAgentApply(pod, mode string)
- func (m *Metrics) RecordAgentVersionSkew()
- func (m *Metrics) RecordApplyRejected(pod string)
- func (m *Metrics) RecordConfigRejected(validatorName string)
- func (m *Metrics) RecordDeployment(durationSeconds float64, success bool)
- func (m *Metrics) RecordDeploymentReloads(reloads int)
- func (m *Metrics) RecordEvent()
- func (m *Metrics) RecordEventDrop(subscriberName, eventType string)
- func (m *Metrics) RecordHAProxyCheck(validatorName, result string, reused bool)
- func (m *Metrics) RecordHAProxyPodRejected(reason string)
- func (m *Metrics) RecordHTTPContentRejected()
- func (m *Metrics) RecordHTTPContentRevoked()
- func (m *Metrics) RecordLeadershipTransition()
- func (m *Metrics) RecordLoadGate(passed bool)
- func (m *Metrics) RecordQueueWait(seconds float64)
- func (m *Metrics) RecordReconciliation(durationSeconds float64, success bool)
- func (m *Metrics) RecordRender(cacheState string)
- func (m *Metrics) RecordRenderFailure(graphWarm bool)
- func (m *Metrics) RecordRuntimeBackendFallback(reason string)
- func (m *Metrics) RecordRuntimeBackendOp(op string)
- func (m *Metrics) RecordRuntimeMapDivergence(mapName string)
- func (m *Metrics) RecordRuntimeServerOp(op string)
- func (m *Metrics) RecordValidation(success bool)
- func (m *Metrics) RecordWebhookRequest(gvk, result string, durationSeconds float64)
- func (m *Metrics) RecordWebhookValidation(gvk, result string)
- func (m *Metrics) ResetFleetConvergence()
- func (m *Metrics) SetBuildInfo(version, haproxyVersion, goVersion string)
- func (m *Metrics) SetConfigPinned(pinned bool)
- func (m *Metrics) SetEventSubscribers(count int)
- func (m *Metrics) SetFleetConvergence(total, succeeded, failed int)
- func (m *Metrics) SetIsLeader(isLeader bool)
- func (m *Metrics) SetObservabilityDrops(count uint64)
- func (m *Metrics) SetRenderProfiles(count int)
- func (m *Metrics) SetRenderWarnings(counts map[string]int)
- func (m *Metrics) SetResourceCount(resourceType string, count int)
Constants ¶
const ComponentName = "metrics"
ComponentName is the unique identifier for the metrics component.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Component ¶
type Component struct {
// contains filtered or unexported fields
}
Component is an event-driven metrics collector.
Subscribes to controller events and updates metrics via the Metrics struct. This is an event adapter that bridges domain events to Prometheus metrics.
IMPORTANT: Instance-based, created fresh per application iteration. When the iteration ends (context cancelled), the component stops and the metrics it was updating become eligible for garbage collection.
func New ¶
New creates a new metrics component that listens to events.
Parameters:
- metrics: The Metrics instance to update (created with metrics.NewMetrics)
- eventBus: The EventBus to subscribe to for events
Usage:
registry := prometheus.NewRegistry() metrics := metrics.NewMetrics(registry) component := metrics.New(metrics, eventBus) go component.Start(ctx) eventBus.Start()
type Metrics ¶
type Metrics struct {
RetainedConfigActive prometheus.Gauge
// Reconciliation metrics
ReconciliationDuration prometheus.Histogram
ReconciliationTotal prometheus.Counter
ReconciliationErrors prometheus.Counter
// Deployment metrics
DeploymentDuration prometheus.Histogram
DeploymentTotal prometheus.Counter
DeploymentErrors prometheus.Counter
// HAProxyReloadsTotal is populated from DeploymentCompletedEvent. Reloads
// are the canonical capacity/SLO signal: a reload momentarily forks the
// HAProxy process, so a high reload rate (vs runtime ops) is what to
// capacity-plan and alert on.
HAProxyReloadsTotal prometheus.Counter
// Fleet-convergence + config-staleness gauges. Populated LEADER-ONLY from
// DeploymentCompletedEvent (only the leader deploys), so followers hold the
// reset values below and never false-alert. Unlike alerting on
// rate(haptic_deployment_errors_total) — which the self-healing scheduler
// makes noisy, since transient deploy failures now self-heal — these give a
// true "is the fleet converged, and for how long has it not been" signal:
// - FleetSize / FleetConverged: an operator alerts on converged < size.
// - LastFullSyncTimestamp: staleness is time() - this gauge.
// - DeploymentConsecutiveFailures: the noise-free replacement for alerting
// on the error counter (resets to 0 the moment the fleet fully converges).
HAProxyFleetSize prometheus.Gauge
HAProxyFleetConverged prometheus.Gauge
LastFullSyncTimestamp prometheus.Gauge
DeploymentConsecutiveFailures prometheus.Gauge
// Per-pod apply counters. AgentApplyTotal is labelled by the mode the agent
// reported, so the reload-free share of a deployment is a query away;
// ApplyRejectedTotal counts NACKs, each of which costs that pod a
// full-state reload on the next apply.
AgentApplyTotal *prometheus.CounterVec
ApplyRejectedTotal *prometheus.CounterVec
// AgentVersionSkewTotal counts applies to pods whose agent reported a
// contract this controller does not fully speak. Never a refusal, but
// sustained growth means a rollout is stuck half-upgraded.
AgentVersionSkewTotal prometheus.Counter
// Runtime op counters, recorded from what the pods ACKed. Labelled by op
// kind: which lifecycle operation dominates is the actionable part.
RuntimeBackendOpsTotal *prometheus.CounterVec
RuntimeServerOpsTotal *prometheus.CounterVec
// RuntimeBackendFallbackTotal counts runtime backend batches a pod could
// not run and reloaded instead, by reason. A `name_collision` is a leftover
// backend still holding the name a new one wants (A5): HAProxy exposes no
// way to read a backend's shape, so the apply reloads rather than risk
// publishing traffic onto the wrong section. Steady growth means deferred
// backend deletes are lagging behind fresh routes reusing their names.
RuntimeBackendFallbackTotal *prometheus.CounterVec
// RuntimeMapDivergence counts runtime maps whose post-apply read-back
// still disagreed with the desired content, each costing that sync its
// reload-free lane (issue #48). Labelled by map because which one
// degrades is the actionable part. The reload fallback is convergent, so
// occasional counts during pod churn are expected; steady growth in
// STEADY STATE means endpoint changes are reloading HAProxy, defeating
// the runtime lane.
RuntimeMapDivergence *prometheus.CounterVec
// ValidationTotal and ValidationErrors count HAProxy's verdicts on rendered
// configurations; the configuration itself is judged in ConfigValidation*.
ValidationTotal prometheus.Counter
ValidationErrors prometheus.Counter
// ConfigValidated is this replica's Validated condition: one series per
// known status and reason, the current one at 1.
ConfigValidated *prometheus.GaugeVec
// ConfigValidationTotal counts this replica's verdicts on its configuration.
ConfigValidationTotal *prometheus.CounterVec
// HAProxyChecksTotal counts the `haproxy -c` runs each validator asked for,
// on every replica; HAProxyChecksReusedTotal counts the ones answered by an
// identical input set's verdict instead of a process.
HAProxyChecksTotal *prometheus.CounterVec
HAProxyChecksReusedTotal *prometheus.CounterVec
// Resource metrics
ResourceCount *prometheus.GaugeVec
RejectedWatchedInputs prometheus.Gauge
// Event metrics
EventSubscribers prometheus.Gauge
EventsPublished prometheus.Counter
EventsDroppedCritical prometheus.Counter // Drops from critical subscribers (alert-worthy)
EventsDroppedBySubscriber *prometheus.CounterVec // Drops by subscriber and event type
EventsDroppedObservability prometheus.Gauge // Drops from observability subscribers (polled, expected)
// Queue wait metrics - time events spend waiting in channels before processing
ReconciliationQueueWait prometheus.Histogram
// Webhook metrics
WebhookRequestsTotal *prometheus.CounterVec
WebhookRequestDuration prometheus.Histogram
WebhookValidationTotal *prometheus.CounterVec
AdmissionRenderSlotWait prometheus.Histogram
// Leader election metrics
LeaderElectionIsLeader prometheus.Gauge
LeaderElectionTransitionsTotal prometheus.Counter
LeaderElectionTimeAsLeaderSeconds prometheus.Counter
// Discovery metrics — surfaces rejection of HAProxy pods that the
// controller refuses to talk to (most commonly version-incompatible).
// Without this counter, a misconfigured cluster (e.g., controller image
// bundling HAProxy 3.3 while chart deploys 3.2) presents only as
// "deployment.skipped" in the pipeline status, masking a real fault.
HAProxyPodsRejectedTotal *prometheus.CounterVec
// ConfigRejectedTotal counts HAProxyTemplateConfig loads refused by the
// config-validation gate, labelled by the validator that rejected it
// (basic / template / jsonpath / validationtests), or "coordinator" when the
// scatter-gather itself failed — a validator timed out or didn't respond —
// rather than a specific validator refusing the config. Persistent growth
// means the controller is repeatedly refusing a new config and continuing to
// serve the last-good one — the operator's config never took effect.
ConfigRejectedTotal *prometheus.CounterVec
// HTTPContentRevokedTotal counts accepted HTTP content taken back because
// HAProxy refused a render containing it (ADR-0030).
HTTPContentRevokedTotal prometheus.Counter
// HTTPContentRejectedTotal counts pending content refused before acceptance.
HTTPContentRejectedTotal prometheus.Counter
// ConfigPinned is 1 while the render gate is holding renders it has
// already refused once: nothing new reaches the fleet, which keeps serving
// the last config HAProxy accepted, until the operator's input changes.
ConfigPinned prometheus.Gauge
// ControllerReinitializationsTotal counts iteration restarts after the
// first. An iteration restart stops the leader-only components and rebuilds
// them from a freshly resolved configuration, so nothing propagates
// endpoint changes to HAProxy until it completes — measured at 54-65s on
// the bundled chart, dominated by the load gate re-running validationTests.
// Growth means something keeps changing the effective configuration, most
// often a CRD appearing or disappearing.
ControllerReinitializationsTotal prometheus.Counter
// RenderProfiles is the number of distinct backend profiles the most recent
// reconcile render emitted (`defaults haptic-be-*`). Profile cardinality is
// what backends collapse onto, so it tracks the config's structural size
// independently of the raw backend count.
RenderProfiles prometheus.Gauge
// RenderWarnings counts current template warnings by reason.
RenderWarnings *prometheus.GaugeVec
// RenderTotal counts reconcile renders by the cache state they ran from.
// Every replica renders: the leader to deploy, a follower to keep its
// incremental graph warm for the next leadership change.
RenderTotal *prometheus.CounterVec
// RenderErrors counts the reconcile renders in RenderTotal that produced no output.
RenderErrors *prometheus.CounterVec
// Build info metric
BuildInfo *prometheus.GaugeVec
// contains filtered or unexported fields
}
Metrics holds all controller-specific Prometheus metrics.
IMPORTANT: Create one instance per application iteration. When the iteration ends (e.g., on config reload), metrics are garbage collected. This prevents stale state from surviving across reinitialization cycles.
func NewMetrics ¶
func NewMetrics(registry prometheus.Registerer) *Metrics
New creates all controller metrics and registers them with the provided registry.
IMPORTANT: Pass an instance-based registry (prometheus.NewRegistry()), NOT prometheus.DefaultRegisterer. Metrics are scoped to the registry's lifetime. When the registry is garbage collected (iteration ends), metrics are freed.
This is critical for supporting application reinitialization on configuration changes without leaking metrics or accumulating stale state.
Example:
registry := prometheus.NewRegistry() // Create per iteration metrics := metrics.NewMetrics(registry) // Metrics tied to iteration // ... use metrics ... // When iteration ends, both registry and metrics are GC'd
func (*Metrics) AddEventDrops ¶
AddEventDrops restores process-lifetime critical-drop totals into a new per-iteration registry.
func (*Metrics) AddTimeAsLeader ¶
AddTimeAsLeader adds time spent as leader to the cumulative counter.
Parameters:
- seconds: Time spent as leader in seconds
func (*Metrics) CarryConfigValidation ¶ added in v0.7.0
CarryConfigValidation continues previous's config-validation series in m. A reinitialization swaps in a fresh registry, and the valid verdict that triggered it would otherwise never be scraped. previous may be nil.
func (*Metrics) RecordAdmissionRenderSlotWait ¶
RecordAdmissionRenderSlotWait records an admission render's wait for a slot.
func (*Metrics) RecordAgentApply ¶
RecordAgentApply records one apply a pod's agent accepted, by the mode it reported.
func (*Metrics) RecordAgentVersionSkew ¶
func (m *Metrics) RecordAgentVersionSkew()
RecordAgentVersionSkew records one pod whose agent speaks a contract this controller does not fully share.
func (*Metrics) RecordApplyRejected ¶
RecordApplyRejected records one apply a pod's agent refused.
func (*Metrics) RecordConfigRejected ¶
RecordConfigRejected increments the config-rejection counter for the given validator (the one whose check failed).
func (*Metrics) RecordDeployment ¶
RecordDeployment records a deployment attempt.
Parameters:
- durationSeconds: Time spent deploying (use time.Since(start).Seconds())
- success: Whether the deployment completed successfully
func (*Metrics) RecordDeploymentReloads ¶
RecordDeploymentReloads records how many pods a completed deployment reloaded — the headline capacity/SLO signal (see the metric help text). Zero is skipped so a reload-free deployment doesn't perturb the counter.
func (*Metrics) RecordEvent ¶
func (m *Metrics) RecordEvent()
RecordEvent records an event publication. Call this for every event published to the EventBus.
func (*Metrics) RecordEventDrop ¶
RecordEventDrop records an event drop due to full subscriber buffer. This increments the critical total and the per-subscriber counter. Call this from the drop callback registered with EventBus.SetDropCallback().
Parameters:
- subscriberName: The name of the subscriber that dropped the event
- eventType: The event type that was dropped
func (*Metrics) RecordHAProxyCheck ¶
RecordHAProxyCheck records one check a validator asked HAProxy for; reused means an earlier verdict answered it without running `haproxy -c`.
func (*Metrics) RecordHAProxyPodRejected ¶
RecordHAProxyPodRejected increments the rejection counter for a stable reason label (e.g. "version_mismatch_older", "version_check_failed"). Discovery publishes HAProxyPodRejectedEvent; the metrics component translates each event into a counter increment.
func (*Metrics) RecordHTTPContentRejected ¶
func (m *Metrics) RecordHTTPContentRejected()
func (*Metrics) RecordHTTPContentRevoked ¶
func (m *Metrics) RecordHTTPContentRevoked()
RecordHTTPContentRevoked counts one revoked HTTP content acceptance.
func (*Metrics) RecordLeadershipTransition ¶
func (m *Metrics) RecordLeadershipTransition()
RecordLeadershipTransition records a leadership state change. Call this whenever leadership is gained or lost.
func (*Metrics) RecordLoadGate ¶ added in v0.7.0
RecordLoadGate records the startup load gate's verdict on the configuration an iteration starts from. A pass leaves the reported state alone: the iteration's own events establish it.
func (*Metrics) RecordQueueWait ¶
RecordQueueWait records how long a reconciliation event waited in the coordinator queue before processing started.
func (*Metrics) RecordReconciliation ¶
RecordReconciliation records a completed reconciliation cycle.
Parameters:
- durationSeconds: Time spent in reconciliation (use time.Since(start).Seconds())
- success: Whether the reconciliation completed successfully
func (*Metrics) RecordRender ¶
RecordRender records one reconcile render by the cache state it ran from.
func (*Metrics) RecordRenderFailure ¶
RecordRenderFailure records a reconcile render that produced no output; graphWarm is whether it had a committed graph to build on.
func (*Metrics) RecordRuntimeBackendFallback ¶
RecordRuntimeBackendFallback records one runtime backend batch a pod reloaded instead of running, by reason.
func (*Metrics) RecordRuntimeBackendOp ¶
RecordRuntimeBackendOp records one backend lifecycle op a pod executed.
func (*Metrics) RecordRuntimeMapDivergence ¶
RecordRuntimeMapDivergence records one runtime map that failed its post-apply read-back and so forced a reload fallback.
func (*Metrics) RecordRuntimeServerOp ¶
RecordRuntimeServerOp records one server lifecycle op a pod executed.
func (*Metrics) RecordValidation ¶
RecordValidation records a validation attempt.
Parameters:
- success: Whether the validation passed
func (*Metrics) RecordWebhookRequest ¶
RecordWebhookRequest records a webhook admission request.
Parameters:
- gvk: The GVK of the resource being validated (e.g., "v1.ConfigMap")
- result: The result of the request ("allowed", "denied", or "error")
- durationSeconds: Time spent processing the request
func (*Metrics) RecordWebhookValidation ¶
RecordWebhookValidation records a webhook validation result.
Parameters:
- gvk: The GVK of the resource being validated
- result: The validation result ("allowed", "denied", or "error")
func (*Metrics) ResetFleetConvergence ¶
func (m *Metrics) ResetFleetConvergence()
ResetFleetConvergence clears the fleet-convergence gauges when this replica is no longer the leader. DeploymentCompletedEvent is leader-only, so a follower must not keep reporting the values it set while it was leader:
- fleet_size / fleet_converged / consecutive_failures → 0 (so converged < fleet_size becomes 0 < 0, i.e. false, and can't false-alert)
- last_full_sync → now, so a former leader doesn't accrue growing staleness
- config_pinned → 0, since the render gate's latch is per leadership term
func (*Metrics) SetBuildInfo ¶
SetBuildInfo sets the build info metric with version labels. Call once at startup with the version information for this binary.
Parameters:
- version: The controller version (e.g., "0.1.0-alpha.10")
- haproxyVersion: The HAProxy major.minor version (e.g., "3.2")
- goVersion: The Go runtime version (e.g., "go1.27.0")
func (*Metrics) SetConfigPinned ¶
SetConfigPinned sets whether the render gate is holding renders.
func (*Metrics) SetEventSubscribers ¶
SetEventSubscribers sets the number of active event subscribers.
Parameters:
- count: The current number of event subscribers
func (*Metrics) SetFleetConvergence ¶
SetFleetConvergence records the outcome of a completed fleet deployment onto the fleet-convergence + config-staleness gauges. It is O(1) and must be called LEADER-ONLY (DeploymentCompletedEvent is leader-only).
Given the deploy's total / succeeded / failed instance counts it:
- sets haptic_haproxy_fleet_size = total
- sets haptic_haproxy_fleet_converged = succeeded
- on a full sync (succeeded == total && total > 0): resets the consecutive- failure count to 0 and stamps haptic_last_full_sync_timestamp_seconds with now
- otherwise, on any failure (failed > 0): increments the consecutive-failure count
A full sync is checked first so that succeeded == total always wins over a stray failed > 0 (the two are mutually exclusive for a well-formed result).
func (*Metrics) SetIsLeader ¶
SetIsLeader sets whether this replica is the leader.
Parameters:
- isLeader: true if this replica is the leader, false otherwise
func (*Metrics) SetObservabilityDrops ¶
SetObservabilityDrops sets the observability drop gauge from EventBus statistics. This should be called periodically since observability drops don't trigger callbacks.
func (*Metrics) SetRenderProfiles ¶
SetRenderProfiles records how many distinct backend profiles the most recent reconcile render emitted.
func (*Metrics) SetRenderWarnings ¶
SetRenderWarnings replaces the warning counts, removing resolved reasons.
func (*Metrics) SetResourceCount ¶
SetResourceCount sets the count for a specific resource type.
Parameters:
- resourceType: The type of resource (e.g., "ingresses", "services")
- count: The current number of resources of this type