refactor(worker): collapse session goroutines into single runSession loop

Replace the five-goroutine session model (RunDialer, RunKeepalive,
RunReader, RunHeartbeatForwarder, RunStopMonitor, Session) with a single
DefaultWorker.runSession method containing two select loops: one
pre-connection and one connected. Ephemeral dial goroutines replace
RunDialer; the keepalive timer and heartbeat reset are inlined. No
exported building-block symbols remain.

Consolidate worker_dialer_test.go, worker_session_test.go, and
worker_start_test.go into worker_test.go. Add seven new behavioral
tests covering dial failure, keepalive-driven dial replacement,
pre-connection stop, message delivery with timestamp, sustained
activity and pong resetting the keepalive timer, keepalive-triggered
reconnect, and nil connection pointer after disconnect.
Update EXTEND.md and README.md to remove references to the deleted

building blocks and document the single worker replacement pattern
This commit is contained in:
Jay
2026-05-20 14:01:01 -04:00
parent b44a46ed2f
commit cda6d286ab
10 changed files with 811 additions and 1659 deletions
+3 -19
View File
@@ -65,27 +65,11 @@ The pool calls the factory under its write lock when `Connect` is called. The fa
The factory is set via `honeybee.WithWorkerFactory` on the pool config.
### Building Blocks
### Replacing the Worker
**`RunDialer(id, ctx, pool, dial, newConn, logger)`** Listens on `dial` for connection requests. On each signal, calls `connect` to dial a new `*transport.Connection`. While a dial is in progress, drains additional `dial` signals so that at most one dial runs at a time. On failure, logs the error and waits for the next `dial` signal. On success, sends the connection on `newConn`. Exits when `ctx` is cancelled.
Satisfy the `Worker` interface and register your implementation via `honeybee.WithWorkerFactory`. Your worker is responsible for the full connection lifecycle: dialing and redialing, managing connection state, forwarding received messages to `pool.Inbox`, emitting `EventConnected` and `EventDisconnected` to `pool.Events`, and incrementing `pool.InboxCounter` for each message forwarded.
**`RunKeepalive(ctx, heartbeat, keepalive, timeout, logger)`** Monitors `heartbeat`. Resets a timer on each signal. When the timer fires, sends a signal on `keepalive` to notify the session that the connection should be replaced. When `timeout` is zero, keepalive is disabled: it drains `heartbeat` without acting and exits when `ctx` is cancelled.
**`RunReader(id, ctx, onStop, conn, inbox, heartbeat, logger)`** Reads from `conn.Incoming()` until the channel closes or `ctx` is cancelled. Builds an `InboxMessage` inline with the peer ID and writes it directly to `inbox`. Sends a signal on `heartbeat` for each message. On exit, calls `conn.Close()` and then `onStop`.
**`RunHeartbeatForwarder(ctx, conn, heartbeat, logger)`** Reads from `conn.Heartbeat()` and forwards each signal to `heartbeat`. Propagates pong replies into the worker's heartbeat channel so pongs reset the keepalive timer alongside data messages and sends.
**`RunStopMonitor(ctx, onStop, conn, keepalive, logger)`** Waits for either `ctx.Done` or a signal on `keepalive`. On either, calls `conn.Close()` and then `onStop`. This is how a keepalive expiry propagates into a session tear-down.
**`Session`** The coordination struct that ties the above blocks together for one connection lifecycle. `Session.Start` runs a loop: request a dial, wait for a connection, run `RunReader`, `RunHeartbeatForwarder`, and `RunStopMonitor` concurrently, wait for them to finish, emit `EventDisconnected`, sleep for `ReconnectDelay`, then repeat. `Session` is exported so it can be embedded or used directly in a custom worker.
### Replacement Patterns
**Swap one block.** The most common case is replacing `RunReader` to intercept or annotate inbound messages, or replacing `RunKeepalive` with a different activity metric. Reuse `Session` for the connection lifecycle and substitute the one goroutine you need to change.
**Replace the session loop.** Construct your own loop using `RunDialer`, `RunKeepalive`, and the session-level blocks. This gives you control over reconnection logic, back-off behavior, or multi-connection strategies while keeping the lower-level I/O blocks intact.
**Implement from scratch.** Satisfy the `Worker` interface directly. You are responsible for dialing, managing connection state, forwarding messages to `pool.Inbox`, emitting `honeybee.EventConnected` and `honeybee.EventDisconnected` to `pool.Events`, and incrementing `pool.InboxCounter`.
`DefaultWorker`'s source is the authoritative reference for how those responsibilities are met.
## Factory Constraints