ETS table owner and serializer for slot assignment.
This GenServer owns the pool's ETS table. The table is :public so the
hot path (Pool.get_channel/1) can read it lock-free, but every write
to the channel-slot layout (claiming/releasing a slot, inserting/removing a
channel, and the :channel_count increment/decrement) is funneled through
this process via register_channel/3 and unregister_channel/2. Because a
single GenServer processes those calls one at a time, concurrent worker
connects/disconnects can never interleave a read-modify-write on the slot
map — slots stay a contiguous 0..channel_count-1 range and the count
always matches the number of populated {:channel, index} entries.
Note: the table is not crash-resilient. It has no
:heir, so if this process dies the:named_tableis destroyed and re-created empty on restart; workers re-populate it as they reconnect. (A real heir would require a separate long-lived owner process.)
The ETS table holds only data the hot path (or other processes) read:
{:channel, index}—{channel, last_used_at}for O(1) indexed access:channel_count— number of connected channels:pool_size— expected pool size:config— pool configuration (also stored in :persistent_term):scaling_lock— lock for scaling operations
The pid => slot_index map is not in ETS — get_channel/1 never reads
it, so it lives in this GenServer's state. Keeping it out of ETS avoids
copying the whole map in and out on every connect/disconnect, and since slots
are kept contiguous, claiming a slot is O(1) (map_size).
Summary
Functions
Returns a specification to start this module under a supervisor.
Registers a worker's channel in a pool slot (serialized).
Releases a worker's slot and decrements :channel_count (serialized).
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec register_channel(atom(), pid(), term()) :: {:ok, non_neg_integer()}
Registers a worker's channel in a pool slot (serialized).
Assigns the worker the lowest free slot (or reuses its existing slot),
inserts the channel into ETS, and bumps :channel_count. Returns
{:ok, slot_index}. All mutations happen inside the GenServer so
concurrent registrations cannot race.
Releases a worker's slot and decrements :channel_count (serialized).
Removes the worker's channel, compacts the slot array so the remaining channels stay contiguous, and decrements the count atomically with the slot mutation. No-op if the worker holds no slot.