# `Hyper.Node.Budget.Hard`
[🔗](https://github.com/harmont-dev/hyper/blob/main/lib/hyper/node/budget/hard.ex#L1)

Hard per-node resource accounting. One `Hard` runs per BEAM node (named
`__MODULE__`, started under `Hyper.Node.Budget.Supervisor`) and tracks how much
memory and disk the VMs scheduled onto this machine have reserved.

"Hard" means the limits are inviolable: a reservation that would push the
running total past the node's configured `mem_max`/`disk_max`
(`Hyper.Cfg.Budget`) is refused outright. Callers reserve through
`with_budget/2`, which holds the budget for the duration of the callback and
releases it afterwards.

# `can_run`

```elixir
@spec can_run(Hyper.Vm.Instance.Spec.t()) :: :ok | {:error, term()}
```

Can this node run the given vm spec? `:ok` if yes, `{:error, reason}` otherwise.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `headroom`

```elixir
@spec headroom() :: %{mem: Unit.Information.t(), disk: Unit.Information.t()}
```

Configured caps minus what is currently reserved.

# `reserve`

```elixir
@spec reserve(Hyper.Vm.Instance.Spec.t(), pid()) :: :ok | {:error, term()}
```

Reserve `spec`'s budget for the lifetime of `owner`.

Atomic: refuses (`{:error, reason}`) if `spec` does not fit remaining headroom.
On success the reservation releases automatically when `owner` dies.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

# `with_budget`

```elixir
@spec with_budget(Hyper.Vm.Instance.Spec.t(), (-&gt; result)) ::
  result | {:error, term()}
when result: var
```

Reserve `vm_spec`'s budget, run `callable`, and release the budget afterwards.

Returns `callable`'s value if the reservation succeeds, or `{:error, reason}`
if the node cannot fit the spec. The budget is released even if `callable`
raises.

---

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