The sampler

A single daemon thread ticks every five seconds and is the heartbeat of the
operational plane. On each tick it assembles one unified sample containing
aggregate QoS rates, session-state counts, the reply-path state, controller
health, controller counters, and per-port counters. That sample is then handed to
every registered metric emitter in turn.

Two design rules govern the sampler and both are visible in the API's behaviour.

An emitter failure never kills the tick. Each emitter is invoked inside its
own exception guard, so a fault in one metric family cannot stop the others from
publishing.

A failed tick holds gauges rather than zeroing them. If the controller is
unreachable for a tick, gauges retain their last known value and counters simply
do not increment. Rendered over time this produces a flat line, which reads
correctly as "sampling paused". Zeroing would produce a cliff, which reads
incorrectly as "traffic stopped". Integrators building alarms on these series
should treat a flat line as a possible collection outage and cross-check
up{job="bng"} or GET /api/bng/health.

Counters present a second subtlety. The controller exposes absolute cumulative
values; the Prometheus counter type only accepts increments. Each emitter
therefore computes a delta against the previous observation. A negative delta
is never emitted as a negative value
— it is recognised as a controller restart,
the baseline is silently reset, and a dedicated counter,
bng_metric_baseline_reset_total{family=…}, is incremented so the reset is
visible in monitoring. The first observation of any series seeds the baseline and
emits nothing.