Help us shape the future of Go! Take the 2026 Go Developer Survey

metrics

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

README

pkg/controller/metrics

Domain metrics for the HAProxy Template Ingress Controller. Two things live here:

  • A Metrics struct that owns every controller-defined Prometheus metric (instance-based prometheus.Registry, not the global default).
  • A Component event adapter that subscribes to controller events and updates metrics accordingly.

User-facing queries, alerting rules, and dashboard templates live in docs/site/docs/operations/monitoring.md. This README is the authoritative developer reference for which metrics exist and which component owns them.

Complete Metric Catalogue

All names are listed exactly as exported. metrics.go contains the authoritative list; the TestMetrics_AllMetricsRegistered assertion in metrics_test.go covers a representative subset (21 of 52) — extend that slice when you add or rename a metric.

Reconciliation pipeline
Metric Type Labels What it tracks
haptic_reconciliation_total counter — Reconciliation cycles triggered
haptic_render_warnings Gauge reason Current template warnings from the last successful reconciliation; resolved reasons and follower series are removed
haptic_reconciliation_errors_total counter — Reconciliations that failed
haptic_reconciliation_duration_seconds histogram — End-to-end reconciliation wall-clock
haptic_reconciliation_queue_wait_seconds histogram — Time between ReconciliationTriggeredEvent and the pipeline actually picking it up (debounce + queue depth)
haptic_render_total counter cache_state Reconcile renders by cache state: cold re-evaluated every template, warm reused the incremental graph, replay reused the previous output. Counts on every replica: followers render to keep their graph warm for a leadership change
Deployment
Metric Type Labels What it tracks
haptic_deployment_total counter — Deployments dispatched to at least one HAProxy endpoint
haptic_deployment_errors_total counter — Failed endpoint attempts and deployments with no converged or pending pods
haptic_deployment_duration_seconds histogram — Deployment duration, aggregated across all parallel endpoint calls
haptic_haproxy_reloads_total counter — HAProxy reloads triggered by deployments. A reload forks the HAProxy process; reload rate (vs runtime-API updates) is the key capacity/SLO signal
haptic_deploy_apply_total counter pod, mode Applies an agent accepted, by the mode it reported: runtime, file_only, reload, scheduled or noop. The reload-free share of a rollout is the runtime+file_only+noop fraction
haptic_apply_rejected_total counter pod Applies an agent refused. The pod keeps its previous config and its next apply carries the complete file set plus a reload
haptic_agent_version_skew_total counter — Applies degraded to full state plus a reload because the pod's agent speaks another API major or lacks an op kind. Nonzero during a rolling upgrade, zero after it
Fleet convergence & config staleness

Leader-only gauges (only the leader deploys). Followers reset them on leadership loss, so converged < size is 0 < 0 (false) on followers and never false-alerts. These answer "did my change reach every pod, and for how long has it not?" — the noise-free replacement for alerting on haptic_deployment_errors_total now that transient deploy failures self-heal. Fed leader-only from DeploymentCompletedEvent (Total/Succeeded/Failed), reset on LostLeadershipEvent.

Metric Type Labels What it tracks
haptic_haproxy_fleet_size gauge — HAProxy pods the last deployment targeted (event.Total)
haptic_haproxy_fleet_converged gauge — HAProxy pods now at the desired config (event.Succeeded). Alert on haptic_haproxy_fleet_converged < haptic_haproxy_fleet_size
haptic_last_full_sync_timestamp_seconds gauge — Unix seconds of the last full-fleet sync (Succeeded == Total > 0); seeded to controller start time. Staleness = time() - <this>
haptic_deployment_consecutive_failures gauge — Consecutive deploys that did not fully converge; +1 on any Failed > 0, reset to 0 on a full sync
Runtime operations

What the controller asked the fleet to run without a reload. A change the render couldn't express as a runtime op is a reload.

Metric Type Labels What it tracks
haptic_runtime_backend_ops_total counter op Backend lifecycle operations the fleet applied at runtime, by op kind
haptic_runtime_server_ops_total counter op Server lifecycle operations the fleet applied at runtime, by op kind
haptic_runtime_map_divergence_total counter map Runtime maps whose post-apply read-back disagreed with the desired content, forcing a reload fallback

Each agent also exports its own view on the pod's agent-metrics port; monitoring.md lists those and maps every metric this cutover removed to its replacement.

Config validation
Metric Type Labels What it tracks
haptic_validation_total counter — HAProxy's verdicts on rendered configurations: render gate checks and failed render validations (leader)
haptic_validation_errors_total counter — Rendered configurations HAProxy refused or couldn't check (leader)
haptic_config_validated gauge status, reason This replica's view of the HAProxyTemplateConfig Validated condition: every known status and reason is exported, the current one at 1, all 0 before the first verdict. Carried across reinitializations
haptic_config_validation_total counter result This replica's verdicts on its configuration, startup load gate included: valid, invalid, inconclusive (each retry counts). Carried across reinitializations
haptic_haproxy_checks_total counter validator, result haproxy -c processes run on this replica
haptic_haproxy_checks_reused_total counter validator, result Checks answered by an identical input set's verdict
haptic_config_rejected_total counter validator HAProxyTemplateConfig loads refused by the config-validation gate, labelled by the validator that rejected it (basic, template, jsonpath, validationtests), or coordinator when one timed out
haptic_config_pinned gauge — 1 while the render gate holds back renders HAProxy refused twice in a row. Leader-only; 0 on followers
Watched resources
Metric Type Labels What it tracks
haptic_resource_count gauge type Current size of each watched-resource store (including haproxy-pods)
HAProxy pod discovery
Metric Type Labels What it tracks
haptic_haproxy_pods_rejected_total counter reason HAProxy pods refused admission by the discovery component. Persistent non-zero growth typically means the controller cannot talk to the deployed HAProxy pods (e.g. bundled HAProxy major.minor differs from the chart's haproxyVersion).
Event bus
Metric Type Labels What it tracks
haptic_event_subscribers gauge — Live subscribers on the EventBus. Drops during normal ops usually indicate a crash
haptic_events_published_total counter — Total publishes
haptic_events_dropped_critical_total counter — Drops where the buffered event was marked critical
haptic_events_dropped_by_subscriber_total counter subscriber, event_type Drops attributed to each subscriber/event-type pair (the second label lets dashboards split by which event type the subscriber couldn't keep up with)
haptic_events_dropped_observability_total gauge — Drops to the observability subscribers (commentator, debug buffer); expected to be low but non-zero on bursts
haptic_component_mailbox_depth gauge component Events queued in a component's coalescing mailbox. Each queued event pins the render it carries, so a depth that grows under churn is memory as much as latency; the mailbox only logs from 256
Webhook
Metric Type Labels What it tracks
haptic_webhook_requests_total counter gvk, result Admission requests by kind and allow/deny/error
haptic_webhook_request_duration_seconds histogram — Per-request wall-clock
haptic_admission_render_slot_wait_seconds histogram — Admission render's wait for a render slot
haptic_webhook_validation_total counter gvk, result Validation-only tally (no timing) — handy for ratios
Leader election
Metric Type Labels What it tracks
haptic_leader_election_is_leader gauge — 1 if this replica holds the lease, else 0
haptic_leader_election_transitions_total counter — Leadership changes observed
haptic_leader_election_time_as_leader_seconds_total counter — Cumulative seconds spent as leader
Build info
Metric Type Labels What it tracks
haptic_build_info gauge (always 1) version, haproxy_version, go_version Static metadata for dashboards

Component

Component subscribes to reconciliation, deployment, validation, resource, event-bus, webhook, leader-election, and parser events, and records them inside the internal handleEvent dispatcher. A few callers record directly: the render paths and the render gate, and the startup load gate (RecordLoadGate), which runs before the event bus starts.

haptic_config_validated follows the events the StatusUpdater writes the Validated condition from, on every replica. A ConfigValidatedEvent without a candidate generation (the running config's startup announcement or a leadership replay) sets True only while that config is the newest one parsed and no verdict has replaced it, so a replay never hides a newer False or Unknown.

import (
    "github.com/haproxy-haptic/HAPTIC/pkg/controller/metrics"
    pkgmetrics "github.com/haproxy-haptic/HAPTIC/pkg/metrics"

    "github.com/prometheus/client_golang/prometheus"
)

registry := prometheus.NewRegistry()          // instance-based, swapped per controller iteration
m := metrics.NewMetrics(registry)              // single arg — Registerer only
m.SetBuildInfo(version, haproxyVersion, goVersion) // populate the haptic_build_info gauge

comp := metrics.New(m, bus)                    // (*Metrics, *EventBus) — subscribes during construction
go comp.Start(ctx)                             // runs the event loop

// Serve on the port pkg/metrics exposes
pkgmetrics.NewServer(":9090", registry).Start(ctx)

The Metrics struct is intentionally safe to use stand-alone (CLI validation, tests) without the Component — call the typed update methods directly when you already have a value.

Why an instance-based registry?

The controller's reinitialisation loop creates a fresh EventBus on every config change. Using the global default registry would leak prior-iteration collectors into the new one and produce duplicate-registration panics. NewMetrics always takes a caller-provided registry, which the controller swaps per iteration; Prometheus sees a clean slate with the same metric names each time.

Critical EventBus drop counters are the exception: the controller carries their totals into each new registry. A critical drop triggers the registry swap itself, so resetting those counters would hide the failure before the next Prometheus scrape.

Resource-count tracking

ResourceCount is a gauge with a type label. The component seeds it from IndexSynchronizedEvent (absolute counts per resource type) and then applies deltas from ResourceIndexUpdatedEvent (created − deleted), skipping events marked IsInitialSync. The running totals live in a map[string]int on the component struct, so gauge values match reality across churn without re-listing from the API server.

Dropping or Renaming a Metric

  • The TestMetrics_AllMetricsRegistered assertion covers a representative subset of the exported names. Update that slice when you add, rename, or remove one — and ideally extend it so every name is guarded.
  • Dashboards and alert rules in docs/site/docs/operations/monitoring.md reference names too; keep that file in sync or link the dashboard PR to the metric PR.

Testing

go test ./pkg/controller/metrics/...          # unit tests
go test ./pkg/controller/metrics/... -race    # race detector

component_test.go asserts that publishing each event type produces exactly the expected metric update — no surprise side-effects, no accidental double counting.

See Also

License

Apache-2.0 — see root LICENSE.

Documentation

Index

Constants

View Source
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

func New(metrics *Metrics, eventBus *busevents.EventBus) *Component

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()

func (*Component) Metrics

func (c *Component) Metrics() *Metrics

Metrics returns the underlying Metrics instance for direct access.

This allows other components (like webhook) to record metrics directly without going through the event bus.

func (*Component) Start

func (c *Component) Start(ctx context.Context) error

Start begins the metrics event processing loop.

This method blocks until the context is cancelled. It also periodically updates the observability drop metric from EventBus stats.

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

func (m *Metrics) AddEventDrops(subscriberName, eventType string, count uint64)

AddEventDrops restores process-lifetime critical-drop totals into a new per-iteration registry.

func (*Metrics) AddTimeAsLeader

func (m *Metrics) AddTimeAsLeader(seconds float64)

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

func (m *Metrics) CarryConfigValidation(previous *Metrics) error

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

func (m *Metrics) RecordAdmissionRenderSlotWait(wait time.Duration)

RecordAdmissionRenderSlotWait records an admission render's wait for a slot.

func (*Metrics) RecordAgentApply

func (m *Metrics) RecordAgentApply(pod, mode string)

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

func (m *Metrics) RecordApplyRejected(pod string)

RecordApplyRejected records one apply a pod's agent refused.

func (*Metrics) RecordConfigRejected

func (m *Metrics) RecordConfigRejected(validatorName string)

RecordConfigRejected increments the config-rejection counter for the given validator (the one whose check failed).

func (*Metrics) RecordDeployment

func (m *Metrics) RecordDeployment(durationSeconds float64, success bool)

RecordDeployment records a deployment attempt.

Parameters:

  • durationSeconds: Time spent deploying (use time.Since(start).Seconds())
  • success: Whether the deployment completed successfully

func (*Metrics) RecordDeploymentReloads

func (m *Metrics) RecordDeploymentReloads(reloads int)

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

func (m *Metrics) RecordEventDrop(subscriberName, eventType string)

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

func (m *Metrics) RecordHAProxyCheck(validatorName, result string, reused bool)

RecordHAProxyCheck records one check a validator asked HAProxy for; reused means an earlier verdict answered it without running `haproxy -c`.

func (*Metrics) RecordHAProxyPodRejected

func (m *Metrics) RecordHAProxyPodRejected(reason string)

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

func (m *Metrics) RecordLoadGate(passed bool)

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

func (m *Metrics) RecordQueueWait(seconds float64)

RecordQueueWait records how long a reconciliation event waited in the coordinator queue before processing started.

func (*Metrics) RecordReconciliation

func (m *Metrics) RecordReconciliation(durationSeconds float64, success bool)

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

func (m *Metrics) RecordRender(cacheState string)

RecordRender records one reconcile render by the cache state it ran from.

func (*Metrics) RecordRenderFailure

func (m *Metrics) RecordRenderFailure(graphWarm bool)

RecordRenderFailure records a reconcile render that produced no output; graphWarm is whether it had a committed graph to build on.

func (*Metrics) RecordRuntimeBackendFallback

func (m *Metrics) RecordRuntimeBackendFallback(reason string)

RecordRuntimeBackendFallback records one runtime backend batch a pod reloaded instead of running, by reason.

func (*Metrics) RecordRuntimeBackendOp

func (m *Metrics) RecordRuntimeBackendOp(op string)

RecordRuntimeBackendOp records one backend lifecycle op a pod executed.

func (*Metrics) RecordRuntimeMapDivergence

func (m *Metrics) RecordRuntimeMapDivergence(mapName string)

RecordRuntimeMapDivergence records one runtime map that failed its post-apply read-back and so forced a reload fallback.

func (*Metrics) RecordRuntimeServerOp

func (m *Metrics) RecordRuntimeServerOp(op string)

RecordRuntimeServerOp records one server lifecycle op a pod executed.

func (*Metrics) RecordValidation

func (m *Metrics) RecordValidation(success bool)

RecordValidation records a validation attempt.

Parameters:

  • success: Whether the validation passed

func (*Metrics) RecordWebhookRequest

func (m *Metrics) RecordWebhookRequest(gvk, result string, durationSeconds float64)

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

func (m *Metrics) RecordWebhookValidation(gvk, result string)

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

func (m *Metrics) SetBuildInfo(version, haproxyVersion, goVersion string)

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

func (m *Metrics) SetConfigPinned(pinned bool)

SetConfigPinned sets whether the render gate is holding renders.

func (*Metrics) SetEventSubscribers

func (m *Metrics) SetEventSubscribers(count int)

SetEventSubscribers sets the number of active event subscribers.

Parameters:

  • count: The current number of event subscribers

func (*Metrics) SetFleetConvergence

func (m *Metrics) SetFleetConvergence(total, succeeded, failed int)

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

func (m *Metrics) SetIsLeader(isLeader bool)

SetIsLeader sets whether this replica is the leader.

Parameters:

  • isLeader: true if this replica is the leader, false otherwise

func (*Metrics) SetObservabilityDrops

func (m *Metrics) SetObservabilityDrops(count uint64)

SetObservabilityDrops sets the observability drop gauge from EventBus statistics. This should be called periodically since observability drops don't trigger callbacks.

func (*Metrics) SetRenderProfiles

func (m *Metrics) SetRenderProfiles(count int)

SetRenderProfiles records how many distinct backend profiles the most recent reconcile render emitted.

func (*Metrics) SetRenderWarnings

func (m *Metrics) SetRenderWarnings(counts map[string]int)

SetRenderWarnings replaces the warning counts, removing resolved reasons.

func (*Metrics) SetResourceCount

func (m *Metrics) SetResourceCount(resourceType string, count int)

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

Jump to

Keyboard shortcuts

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