# `ZenWebsocket.PoolRouter`
[🔗](https://github.com/ZenHive/zen_websocket/blob/v0.4.2/lib/zen_websocket/pool_router.ex#L1)

Health-based connection routing for WebSocket client pools.

Provides connection selection based on health scoring that considers:
- Pending request count (queue depth)
- Response latency (p99)
- Recent error count (with 60s decay)
- Rate limiter pressure level

## Health Score Formula (0-100)

    score = 100 - pending_penalty - latency_penalty - error_penalty - pressure_penalty

Where:
- `pending_penalty` = min(pending_requests × 10, 40) — 0-40 pts
- `latency_penalty` = min(p99_ms ÷ 25, 30) — 0-30 pts
- `error_penalty` = min(error_count × 15, 20) — 0-20 pts
- `pressure_penalty` = pressure_level_to_points — 0-10 pts

## ETS Storage

Uses `:zen_websocket_pool` ETS table for:
- Round-robin index for fallback selection
- Per-connection error tracking with 60-second decay

## Performance Notes

Round-robin selection among equally-healthy connections is O(n) where n is
the number of connections with the same health score. For typical pools
(under 100 connections), this is negligible. If you need larger pools,
consider partitioning into multiple smaller pools.

## Configuration

The error decay period can be configured at compile time:

    config :zen_websocket, :error_decay_ms, 120_000  # 2 minutes

Default is 60 seconds (60,000 ms).

## API Functions
| Function | Arity | Description | Param Kinds |
| --- | --- | --- | --- |
| `pool_health` | 1 | Get health information for all connections in the pool. | `pids: value` |
| `clear_errors` | 1 | Clear the error count for a connection. | `pid: value` |
| `record_error` | 1 | Record an error for a connection (decays after 60s). | `pid: value` |
| `calculate_health` | 1 | Calculate health score (0-100) for a connection. | `pid: value` |
| `select_connection` | 1 | Select the healthiest connection from a pool. | `pids: value` |

# `health_score`

```elixir
@type health_score() :: 0..100
```

# `calculate_health`

```elixir
@spec calculate_health(pid()) :: health_score()
```

Calculates health score (0-100) for a connection.

Gathers metrics from the client and applies the scoring formula.
Returns 100 if metrics cannot be retrieved.
TODO: optimistic default (100) when metrics unavailable — acceptable for pool routing where unhealthy connections self-report via errors

# `clear_errors`

```elixir
@spec clear_errors(pid()) :: :ok
```

Clears the error count for a connection.

# `pool_health`

```elixir
@spec pool_health([pid()]) :: [%{pid: pid(), health: health_score()}]
```

Returns health information for all connections in the pool.

# `record_error`

```elixir
@spec record_error(pid()) :: :ok
```

Records an error for a connection, incrementing the error count.

Error counts decay after 60 seconds of no new errors.

# `select_connection`

```elixir
@spec select_connection([pid()]) :: {:ok, pid()} | {:error, :no_connections}
```

Selects the healthiest connection from a list of client PIDs.

Returns the connection with the highest health score, or falls back
to round-robin selection when health scores are equal.

## Examples

    iex> PoolRouter.select_connection([pid1, pid2, pid3])
    {:ok, pid2}

    iex> PoolRouter.select_connection([])
    {:error, :no_connections}

---

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