# `Pkcs11ex.Slot.Pool`
[🔗](https://github.com/utaladriz/pkcs11ex/blob/v0.1.0/lib/pkcs11ex/slot/pool.ex#L1)

Round-robin dispatcher for multi-session slot pools.

A slot configured with `session_pool_size: N > 1` runs N independent
`Pkcs11ex.Slot.Server` workers, each holding its own PKCS#11 session.
Sign/verify calls round-robin across workers so unrelated requests run
concurrently rather than serializing through one mailbox.

## When pooling helps

Cloud HSMs (e.g., GCP Cloud HSM via libkmsp11) make every operation a
remote call. A single GenServer mailbox + single session means request
N+1 waits for request N's network round-trip. With `session_pool_size:
4`, four signs land on four sessions in parallel — practical 4× lower
tail latency for bursty workloads.

## When pooling does NOT help and is forbidden

PIN-protected token slots: login state lives on the session, not on
the token. With multiple sessions, every worker would need to login
independently — fine for `pin_callback`-driven flows but error-prone
for explicit `login/2`, and the configured-PIN tests across the
library all assume one logged-in session per slot.

Cross-field config validation enforces `session_pool_size: 1` (the
default) for `type: :token` slots.

## Concurrency model

ETS-backed counter (named table, write_concurrency on). Round-robin via
`:ets.update_counter/4` — atomic, lock-free in the BEAM ETS path. No
GenServer round-trip on the hot path; only `register/2` (called once
at slot startup) holds the GenServer.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `next_worker_index`

```elixir
@spec next_worker_index(atom()) :: pos_integer()
```

Returns the next worker index in the range `1..pool_size(slot_ref)`,
using atomic round-robin.

For `pool_size = 1`, always returns `1` without touching ETS — the
fast path for non-pooled slots.

# `pool_size`

```elixir
@spec pool_size(atom()) :: pos_integer()
```

Pool size for `slot_ref`. Defaults to 1 (single-worker, no pool) when
the slot hasn't been registered — keeps the dispatch path uniform for
ad-hoc test setups that start a `Slot.Server` without registering a
pool size.

# `register`

```elixir
@spec register(atom(), pos_integer()) :: :ok
```

Register a slot's pool size. Idempotent — re-registering with the same
size is a no-op; with a different size, overwrites AND logs a warning
so a config drift between two startup paths can't slip through silently.

Called once per slot from `Pkcs11ex.SlotSupervisor.init/1`.

# `start_link`

---

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