Files
Laan Tungir 38eb721ae8 Fix relay pool zombie connections: reconnect logic, health monitoring, state sync
The relay pool had the same class of bugs as the C lib's relay pool
(fixed there in core_relay_pool.c), plus a few unique ones:

1. No reconnection at all. ReconnectConfig was defined and stored but
   never read — dead configuration. The run() event loop skipped
   non-connected relays forever; once a relay dropped, it never came
   back. Now run() attempts reconnection of any non-connected relay
   after exponential backoff (initial 1s, x2, max 60s, reset after 60s
   stable), honoring max_reconnect_attempts.

2. send_text()/ping() failures left the ws state as Connected (same
   bug as the C lib). A relay that dropped the TCP connection kept
   reporting Connected and every publish silently failed. Both now
   set state to Error on failure.

3. Error-state trap: subscribe() and publish_async() only auto-
   connected from Disconnected, but receive_text() sets Error on
   transport failure — so a relay that died mid-session was stuck
   permanently, even for publishing. Both now reconnect from any
   non-Connected state.

4. Stale RelayEntry.status: set to Disconnected in add_relay() and
   never updated again, so list_relays() reported Disconnected for
   relays connected for days. run() now syncs pool status from the
   live ws state each iteration.

5. No health monitoring: PING_INTERVAL_SECS was defined but unused,
   ping_latency stats never populated, half-open connections never
   probed. run() now sends pings on the configured interval, detects
   pong timeouts and ping-send failures, and force-closes dead
   transports so the reconnect path picks them up.

Tests: 5 new tests (backoff calculation with default and custom
configs, initial status, connect-failure Error state, entry defaults).
Full workspace suite passes: 210 tests, 0 failures.
2026-08-27 07:10:39 -04:00
..
2026-08-13 12:42:40 -04:00