Skill — WebSocket Patterns
When this skill activates
Any task involving WebSocket connection management, real-time communication,
heartbeat/keepalive mechanisms, reconnection strategies, room/channel design,
or WebSocket scaling across multiple server instances.
Mandatory actions when this skill is active
Before writing any code
- Define the websocket connection lifecycle (upgrade, auth, message flow, close).
- Choose heartbeat interval and reconnection strategy.
- Determine scaling approach (Redis pub/sub, sticky sessions, etc.).
During implementation
- Implement heartbeat mechanism (server ping every 30s).
- Add exponential backoff with jitter for reconnection.
- Handle backpressure (buffer limits, overflow policy).
After implementation
- Load test concurrent connection capacity.
- Verify reconnection behavior under network partitions.
- Document WebSocket protocol in ARCHITECTURE.md.
Connection Lifecycle
HTTP Upgrade → Open → Authenticate → Subscribe → Message Loop → Ping/Pong → Close
Upgrade
- Client sends HTTP Upgrade request with
Sec-WebSocket-Key.
- Server responds with 101 Switching Protocols.
- Connection is now bidirectional full-duplex.
Open
- Connection established, ready for messages.
- Server assigns connection ID for tracking.
- Start heartbeat timer.
Authenticate
- First message from client must be auth token.
- Server validates token, associates connection with user.
- Reject and close if auth fails (close code 4001).
Message Loop
- Bidirectional message exchange.
- Messages typed by application protocol (JSON envelope with
type field).
- Server processes commands, pushes events.
Close
- Graceful: client/server sends close frame with reason code.
- Ungraceful: TCP connection drops (detected by heartbeat timeout).
- Clean up subscriptions, remove from rooms, release resources.
Heartbeat Mechanism
Server-Initiated Ping
- Server sends WebSocket ping frame every 30 seconds.
- Client automatically responds with pong (browser handles this).
- If no pong received within 60 seconds: connection dead, close it.
Application-Level Heartbeat
- Send JSON
{"type": "ping", "ts": 1234567890} every 30s.
- Client responds with
{"type": "pong", "ts": 1234567890}.
- Allows RTT measurement and connection quality monitoring.
- Required when infrastructure (load balancers) strips WebSocket pings.
Idle Timeout
- Close connections with no activity for 5 minutes.
- Heartbeat keeps connection alive during quiet periods.
- Differentiate: no data vs dead connection.
Reconnection Strategy
Exponential Backoff with Jitter
attempt 1: wait 1s + random(0-500ms)
attempt 2: wait 2s + random(0-500ms)
attempt 3: wait 4s + random(0-500ms)
attempt 4: wait 8s + random(0-500ms)
...
max wait: 30s + random(0-500ms)
Reconnection Behavior
- On disconnect: immediately attempt reconnect (might be transient).
- On failure: apply exponential backoff.
- On reconnect success: re-authenticate, re-subscribe to channels.
- Resume from last received message sequence number (gap detection).
Client State During Reconnection
- Show "reconnecting..." indicator to user.
- Buffer outgoing messages (send after reconnect).
- Merge missed messages on reconnection (request gap fill from server).
Rooms and Channels
Topic-Based Subscriptions
{"type": "subscribe", "channel": "chat:room-123"}
{"type": "unsubscribe", "channel": "chat:room-123"}
Room Semantics
- Join: add connection to room's subscriber list.
- Leave: remove connection from room's subscriber list.
- Broadcast: send message to all connections in room.
- Presence: track who is currently in room (online/offline).
Channel Naming Convention
{domain}:{resource_type}:{resource_id}
chat:room:abc123
orders:user:user456
notifications:global
Scaling Across Instances
Problem
- WebSocket connections are stateful (pinned to one server).
- Broadcasting must reach connections on ALL servers.
- Server A has user X, Server B has user Y — both in same room.
Solution: Redis Pub/Sub
Server A publishes → Redis channel → Server B receives → delivers to local connections
- Each server subscribes to Redis channels matching its connections' rooms.
- On broadcast: publish to Redis, all servers deliver to their local connections.
- Redis Pub/Sub is fire-and-forget (acceptable for real-time messages).
Alternative: Sticky Sessions
- Route same user to same server (via cookie or IP hash).
- Simpler but limits horizontal scaling.
- Fails on server restart (all connections drop).
Authentication
Token in Query Parameter (During Upgrade)
ws://example.com/ws?token=jwt_token_here
- Simple, works with browser WebSocket API.
- Risk: token in URL may appear in logs.
Token in First Message (After Connect)
{"type": "auth", "token": "jwt_token_here"}
- More secure (not in URL/logs).
- Requires handling unauthenticated state.
- Close connection if no auth within 5 seconds.
Token Refresh
- Server sends
{"type": "token_expiring", "expires_in": 60}.
- Client sends new token before expiration.
- If token expires: close connection, client reconnects with fresh token.
Backpressure Handling
Problem
- Server produces messages faster than client can consume.
- Client's buffer grows unbounded, eventually crashes.
Solutions
Buffer with Limit
- Set maximum buffer size per connection (e.g., 1000 messages).
- When buffer full: drop oldest messages.
- Notify client:
{"type": "lag_warning", "dropped": 47}.
Flow Control
- Client sends acknowledgment every N messages.
- Server pauses sending if no ack received.
- Similar to TCP flow control at application level.
Priority Queues
- Critical messages (errors, auth) never dropped.
- Real-time data (cursor positions) can be dropped.
- Batch/compress low-priority messages during lag.
Self-check before task completion
Before marking a task done when this skill was active:
1---2name: websocket-patterns3description: Skill — WebSocket Patterns4---56# Skill — WebSocket Patterns78## When this skill activates9Any task involving WebSocket connection management, real-time communication,10heartbeat/keepalive mechanisms, reconnection strategies, room/channel design,11or WebSocket scaling across multiple server instances.1213## Mandatory actions when this skill is active1415### Before writing any code161. Define the websocket connection lifecycle (upgrade, auth, message flow, close).172. Choose heartbeat interval and reconnection strategy.183. Determine scaling approach (Redis pub/sub, sticky sessions, etc.).1920### During implementation21- Implement heartbeat mechanism (server ping every 30s).22- Add exponential backoff with jitter for reconnection.23- Handle backpressure (buffer limits, overflow policy).2425### After implementation26- Load test concurrent connection capacity.27- Verify reconnection behavior under network partitions.28- Document WebSocket protocol in ARCHITECTURE.md.2930## Connection Lifecycle3132```33HTTP Upgrade → Open → Authenticate → Subscribe → Message Loop → Ping/Pong → Close34```3536### Upgrade37- Client sends HTTP Upgrade request with `Sec-WebSocket-Key`.38- Server responds with 101 Switching Protocols.39- Connection is now bidirectional full-duplex.4041### Open42- Connection established, ready for messages.43- Server assigns connection ID for tracking.44- Start heartbeat timer.4546### Authenticate47- First message from client must be auth token.48- Server validates token, associates connection with user.49- Reject and close if auth fails (close code 4001).5051### Message Loop52- Bidirectional message exchange.53- Messages typed by application protocol (JSON envelope with `type` field).54- Server processes commands, pushes events.5556### Close57- Graceful: client/server sends close frame with reason code.58- Ungraceful: TCP connection drops (detected by heartbeat timeout).59- Clean up subscriptions, remove from rooms, release resources.6061## Heartbeat Mechanism6263### Server-Initiated Ping64- Server sends WebSocket ping frame every 30 seconds.65- Client automatically responds with pong (browser handles this).66- If no pong received within 60 seconds: connection dead, close it.6768### Application-Level Heartbeat69- Send JSON `{"type": "ping", "ts": 1234567890}` every 30s.70- Client responds with `{"type": "pong", "ts": 1234567890}`.71- Allows RTT measurement and connection quality monitoring.72- Required when infrastructure (load balancers) strips WebSocket pings.7374### Idle Timeout75- Close connections with no activity for 5 minutes.76- Heartbeat keeps connection alive during quiet periods.77- Differentiate: no data vs dead connection.7879## Reconnection Strategy8081### Exponential Backoff with Jitter82```83attempt 1: wait 1s + random(0-500ms)84attempt 2: wait 2s + random(0-500ms)85attempt 3: wait 4s + random(0-500ms)86attempt 4: wait 8s + random(0-500ms)87...88max wait: 30s + random(0-500ms)89```9091### Reconnection Behavior92- On disconnect: immediately attempt reconnect (might be transient).93- On failure: apply exponential backoff.94- On reconnect success: re-authenticate, re-subscribe to channels.95- Resume from last received message sequence number (gap detection).9697### Client State During Reconnection98- Show "reconnecting..." indicator to user.99- Buffer outgoing messages (send after reconnect).100- Merge missed messages on reconnection (request gap fill from server).101102## Rooms and Channels103104### Topic-Based Subscriptions105```json106{"type": "subscribe", "channel": "chat:room-123"}107{"type": "unsubscribe", "channel": "chat:room-123"}108```109110### Room Semantics111- Join: add connection to room's subscriber list.112- Leave: remove connection from room's subscriber list.113- Broadcast: send message to all connections in room.114- Presence: track who is currently in room (online/offline).115116### Channel Naming Convention117```118{domain}:{resource_type}:{resource_id}119chat:room:abc123120orders:user:user456121notifications:global122```123124## Scaling Across Instances125126### Problem127- WebSocket connections are stateful (pinned to one server).128- Broadcasting must reach connections on ALL servers.129- Server A has user X, Server B has user Y — both in same room.130131### Solution: Redis Pub/Sub132```133Server A publishes → Redis channel → Server B receives → delivers to local connections134```135136- Each server subscribes to Redis channels matching its connections' rooms.137- On broadcast: publish to Redis, all servers deliver to their local connections.138- Redis Pub/Sub is fire-and-forget (acceptable for real-time messages).139140### Alternative: Sticky Sessions141- Route same user to same server (via cookie or IP hash).142- Simpler but limits horizontal scaling.143- Fails on server restart (all connections drop).144145## Authentication146147### Token in Query Parameter (During Upgrade)148```149ws://example.com/ws?token=jwt_token_here150```151- Simple, works with browser WebSocket API.152- Risk: token in URL may appear in logs.153154### Token in First Message (After Connect)155```json156{"type": "auth", "token": "jwt_token_here"}157```158- More secure (not in URL/logs).159- Requires handling unauthenticated state.160- Close connection if no auth within 5 seconds.161162### Token Refresh163- Server sends `{"type": "token_expiring", "expires_in": 60}`.164- Client sends new token before expiration.165- If token expires: close connection, client reconnects with fresh token.166167## Backpressure Handling168169### Problem170- Server produces messages faster than client can consume.171- Client's buffer grows unbounded, eventually crashes.172173### Solutions174175#### Buffer with Limit176- Set maximum buffer size per connection (e.g., 1000 messages).177- When buffer full: drop oldest messages.178- Notify client: `{"type": "lag_warning", "dropped": 47}`.179180#### Flow Control181- Client sends acknowledgment every N messages.182- Server pauses sending if no ack received.183- Similar to TCP flow control at application level.184185#### Priority Queues186- Critical messages (errors, auth) never dropped.187- Real-time data (cursor positions) can be dropped.188- Batch/compress low-priority messages during lag.189190## Self-check before task completion191192Before marking a task done when this skill was active:193194- [ ] Did I read the full SKILL.md before starting? (Not just the triggers)195- [ ] Is heartbeat mechanism implemented (30s ping, 60s timeout)?196- [ ] Is reconnection using exponential backoff with jitter?197- [ ] Is authentication handled (first message or query param)?198- [ ] Is backpressure handled (buffer limit, drop policy)?199- [ ] Is cross-instance scaling addressed (Redis pub/sub or equivalent)?200- [ ] Are rooms/channels properly implemented with join/leave semantics?