# `Hyper.Node.FireVMM.Meter`
[🔗](https://github.com/harmont-dev/hyper/blob/main/lib/hyper/node/fire_vmm/meter.ex#L1)

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.

# `child_spec`

```elixir
@spec child_spec(Hyper.Node.FireVMM.Meter.Opts.t()) :: Supervisor.child_spec()
```

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `start_link`

```elixir
@spec start_link(Hyper.Node.FireVMM.Meter.Opts.t()) :: GenServer.on_start()
```

# `unflushed`

```elixir
@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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
