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_penaltyWhere:
pending_penalty= min(pending_requests × 10, 40) — 0-40 ptslatency_penalty= min(p99_ms ÷ 25, 30) — 0-30 ptserror_penalty= min(error_count × 15, 20) — 0-20 ptspressure_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 minutesDefault 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 |
Summary
Functions
Calculates health score (0-100) for a connection.
Clears the error count for a connection.
Returns health information for all connections in the pool.
Records an error for a connection, incrementing the error count.
Selects the healthiest connection from a list of client PIDs.
Types
Functions
@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
@spec clear_errors(pid()) :: :ok
Clears the error count for a connection.
@spec pool_health([pid()]) :: [%{pid: pid(), health: health_score()}]
Returns health information for all connections in the pool.
@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.
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}