# `RpcLoadBalancer.LoadBalancer.SelectionAlgorithm.LeastCpu.Poller`
[🔗](https://github.com/MikaAK/rpc_load_balancer/blob/main/lib/rpc_load_balancer/load_balancer/selection_algorithm/least_cpu/poller.ex#L1)

GenServer that periodically samples local CPU via `:cpu_sup` and fetches
remote node CPU via `:erpc.multicall/5`, storing all results in
`NodeCpuCache`.

Remote fetches run in parallel — `:erpc.multicall/5` applies the timeout
per-call, so poll wall-time stays bounded by `@remote_timeout` regardless
of cluster size.

`:cpu_sup` (from `:os_mon`) is used instead of scheduler wall time because
enabling `:erlang.system_flag(:scheduler_wall_time, true)` is a VM-wide
side effect with measurable overhead. `:os_mon` samples the OS directly
and introduces no global BEAM flag.

## Startup jitter

The first poll is scheduled with a random delay in
`[0, poll_startup_jitter]` ms (default `60_000`). When many nodes boot
simultaneously — e.g. a cluster-wide deploy — this prevents every poller
from issuing its initial multicall in the same instant and overwhelming
remote `NodeCpuCache.get_local/1` workers. Set `:poll_startup_jitter` to
`0` to disable (useful in tests).

## Telemetry

Events are emitted under the `[:rpc_load_balancer, :least_cpu, :poll]`
prefix and can be attached via `:telemetry` or wired into
`Telemetry.Metrics` by downstream applications.

  * `[:rpc_load_balancer, :least_cpu, :poll, :start | :stop | :exception]`
    — span around a full poll cycle (local sample + remote fetches).
    Metadata: `%{load_balancer_name: name}`.
  * `[:rpc_load_balancer, :least_cpu, :poll, :remote_error]`
    — emitted for each remote that failed within a multicall cycle.
    Measurements: `%{count: 1}`.
    Metadata: `%{load_balancer_name: name, node: remote_node, kind: :exit | :error}`.
    `:exit` covers connectivity-class failures (timeout, noconnection);
    `:error` covers remote raises and unexpected shapes.

# `sampler`

```elixir
@type sampler() :: (-&gt; number())
```

# `state`

```elixir
@type state() :: %{
  load_balancer_name: atom() | module(),
  poll_interval: pos_integer(),
  cpu_sampler: sampler()
}
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `poller_name`

```elixir
@spec poller_name(atom() | module()) :: atom()
```

# `start_link`

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

---

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