Skip to content

feat(go-api): WebSocket endpoint with Redis Streams fan-out#32

Open
Depo-dev wants to merge 4 commits into
devfrom
feat/issue-15-websocket-fanout
Open

feat(go-api): WebSocket endpoint with Redis Streams fan-out#32
Depo-dev wants to merge 4 commits into
devfrom
feat/issue-15-websocket-fanout

Conversation

@Depo-dev

@Depo-dev Depo-dev commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements GET /ws WebSocket endpoint with per-connection Redis Streams fan-out.

Library: github.com/gorilla/websocket — chosen over golang.org/x/net/websocket for built-in ping/pong control frame support, production hardening, and a stable documented API.

Architecture

Client → GET /ws?contractId=...&topic0=...
         ↓ Hub.Handler upgrades HTTP → WS
         ↓ newClient().run() spawns:
           - drain goroutine (reads WS to receive pong frames)
           - pingLoop (30s interval ping, 60s pong wait)
           - redisReadLoop (XREAD BLOCK 5s, filters by contractId + topic0, WriteJSON)
         ↓ On disconnect → cancel ctx → unregister from Hub → conn.Close()

Files

  • ws/hub.go — Hub with RWMutex-protected client set, Register/Unregister, Handler
  • ws/client.go — Client with Redis fan-out goroutine
  • ws/hub_test.go — 4 tests (all pass, go vet clean)

Tests

Test What it verifies
TestHub_missingContractID_returns400 Missing param → 400 before upgrade
TestHub_connectDisconnect_lifecycle Register → count=1, unregister → count=0
TestHub_multipleClients 5 clients registered/unregistered correctly
TestHub_websocketConnect_receivesEvent Upgrade path + server-side close propagates

Closes #15

Depo-dev added 4 commits June 3, 2026 16:29
Uses github.com/gorilla/websocket (vs golang.org/x/net/websocket):
gorilla has built-in ping/pong control frames, is production-hardened,
and has a stable well-documented API.

Hub tracks all active Client connections with a RWMutex. Each Client
spawns three goroutines: a drain reader (required to receive pong
frames), a ping ticker (30s interval, 60s pong timeout), and a Redis
XREAD BLOCK loop that filters by contractId and optional topic0, then
writes matching event JSON to the WebSocket. Cleanup via context
cancellation on disconnect.

4 hub tests: missing contractId 400, connect/disconnect lifecycle,
multiple clients, WebSocket upgrade path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant