Per-VM compute meter: samples the VM's cgroup cpu.stat every second,
accrues the CPU time actually executed, and flushes one
Hyper.Metering.Usage window per minute — plus a final one at teardown.
The meter is the FireVMM supervisor's last child, so at shutdown it
stops first, taking its final reading before the Daemon removes the
cgroup.
Billing-grade by construction:
- consumption is accrued from counter deltas (
Controls.Accumulator), so a cgroup recreated by a Core restart re-baselines instead of going negative; - the accumulator is baselined at meter start, not at the first
periodic tick: a leaf that already exists is baselined on its current
reading (a restarted meter never re-bills usage a previous incarnation
flushed), and a leaf the jailer has not yet created is baselined at
zero (its counter is born at zero), so a VM that lives and dies inside
one sample interval still has its whole life covered by the final
reading in
terminate/2; - a failed flush keeps the accrued time and retries with the window
extended; the retry is idempotent on
(vm_id, window_start), so an insert that committed but errored client-side is dropped, not double-billed — recorded usage can never be counted twice; - every failure mode (meter crash, node crash, unreadable cgroup) loses at most the unflushed window: metering only ever under-counts, never over-counts.
Observability contract: because empty windows are dropped without a write,
every drop is logged with the meter's lifetime sample/window counters. A
drop on a VM that has already recorded usage is a normal idle window and
logs at debug; a drop on a VM that has never recorded a window means
the VM's entire life produced no usage row — every cgroup read failed, or
the guest genuinely burned no measurable CPU after the start-of-life
baseline — and logs at warning. Per-sample cpu.stat read failures log
at debug (they are expected while the jailer is still creating the leaf)
and are counted.
Summary
Functions
Returns a specification to start this module under a supervisor.
CPU time accrued by vm_id's meter but not yet flushed to the usage table.
Unit.Time.zero() when no meter is reachable (VM stopped, meter mid-restart)
— callers fall back to the flushed record, an under-count of at most one
flush window.
Functions
@spec child_spec(Hyper.Node.FireVMM.Meter.Opts.t()) :: Supervisor.child_spec()
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec start_link(Hyper.Node.FireVMM.Meter.Opts.t()) :: GenServer.on_start()
@spec unflushed(Hyper.Vm.Id.t()) :: Unit.Time.t()
CPU time accrued by vm_id's meter but not yet flushed to the usage table.
Unit.Time.zero() when no meter is reachable (VM stopped, meter mid-restart)
— callers fall back to the flushed record, an under-count of at most one
flush window.