Lifecycle¶
This document describes the complete lifecycle of a WebSocket connection: from opening the socket, authenticating, joining rooms, receiving updates, keeping alive with heartbeats, disconnecting, and reconnecting with state recovery.
Reference: connection.md · authentication.md · rooms.md · events/auction.md
The lifecycle at a glance¶
Stage 1 — Connection¶
- The client opens a WebSocket to
wss://api.example.com/ws(TLS, RFC 6455). - The client supplies an access token (header or
bearer.<token>subprotocol) and thewebsocket.v1subprotocol. - The server upgrades the request and verifies the token.
Failure at this stage closes the socket with code 1008 / Authentication failed (see connection.md).
Stage 2 — Authentication¶
- The server decodes the access token and builds a
SocketUser(userId,role,companyId, permissions). - There is no unauthenticated window; the socket is only "open" for processing once the token is valid.
See authentication.md.
Stage 3 — Join rooms¶
After the handshake the client typically:
- fetches
auctionIdvia REST (e.g.GET /v2/.../auctions/by-cycle/:cycleId— the auction row is guaranteed beforeREADY/LIVE), then subscribes withauction.subscribe(joinsauction:<auctionId>), - optionally joins
company:<id>,user:<id>,notifications, oradminswithroom.join.
The server authorizes each join; disallowed joins return FORBIDDEN.
Stage 4 — Receive updates¶
auction.subscribetriggersauction.connected,auction.state.snapshot, andauction.audience.snapshot.- Live bids, presence transitions, and state changes arrive as server-pushed events (see events/auction.md).
- The client applies the snapshot as a base and then the deltas.
Stage 5 — Heartbeat¶
- The server sends protocol
pingevery 20 s; clients auto-answerpong. - Missed pong → the server terminates the connection (see connection.md).
Stage 6 — Disconnect¶
On close/error, the server:
- stops the heartbeat,
- runs every subscription cleanup (unsubscribe from realtime + presence leave),
- leaves all rooms,
- removes the connection from the connection manager,
- logs the closure.
All server-side state tied to the connection is released automatically.
Stage 7 — Reconnect¶
The client reconnects with a fresh token and restores state:
- Open a new socket.
- Re-
auction.subscribewithlastSequenceto replay missed events. - Re-join rooms.
- Apply the new
auction.state.snapshot.
Async command lifecycle¶
Write commands (auction.bid.place, auction.pause, auction.winner.declare, …) follow an asynchronous CQRS flow: the gateway acknowledges immediately, then the business result is delivered as a domain event. See protocol.md for the envelope definitions and the frontend migration guide.
sequenceDiagram
participant C as Client
participant S as WebSocket Gateway
participant UC as Command Use Case
participant E as Event Bus
C->>S: command { id: cmd_123, type: auction.bid.place, data }
S->>S: validate + authenticate + authorize
S->>UC: dispatch (async, fire-and-forget)
S-->>C: command.acknowledged { correlationId: cmd_123 }
UC->>UC: validate business rules + transaction
alt accepted
UC->>E: publish auction.bid.created
E-->>S: auction.bid.created
S-->>C: auction.bid.created { correlationId, sequence }
else rejected by business rule
UC-->>S: error
S-->>C: command.failed { correlationId: cmd_123, errorCode, message }
end Sequence diagram¶
sequenceDiagram
participant C as Client
participant S as WebSocket Server
participant R as Redis Pub/Sub
Note over C,S: CONNECT
C->>S: WS upgrade + token (websocket.v1, bearer.<token>)
alt valid token
S-->>C: 101 Switching Protocols
else invalid token
S--xC: close 1008
end
Note over C,S: AUTHENTICATED
C->>S: auction.subscribe { auctionId, lastSequence? }
S->>S: authorize + join auction:<auctionId>
S->>R: ensure ws:room:auction:<auctionId>
S-->>C: auction.connected
S-->>C: auction.state.snapshot
S-->>C: auction.audience.snapshot
S-->>C: ack
Note over C,S: UPDATES
loop live events
R--)S: ws:room:auction:<auctionId>
S-->>C: auction.bid.created / auction.state.changed / auction.audience.changed ...
end
Note over C,S: HEARTBEAT
loop every 20s
S-->>C: ping
C-->>S: pong
end
Note over C,S: DISCONNECT
alt network failure / close
C--xS: close
S->>R: unsubscribe channel when room empty
S->>S: cleanup rooms + presence + heartbeat
end
Note over C,S: RECONNECT
C->>S: reconnect with fresh token
C->>S: auction.subscribe { auctionId, lastSequence }
S-->>C: auction.state.snapshot (+ replayed events) State transitions¶
| State | Entered when | Actions | Exit |
|---|---|---|---|
connecting | client opens socket | — | authenticating or closed (handshake fail) |
authenticating | upgrade request received | verify token | open or closed (code 1008) |
open | token valid | register connection, start heartbeat | closing / closed |
subscribed | first room/auction join | join rooms, subscribe redis, presence join | open (on unsubscribe) |
closing | client/server close | run subscription cleanup | closed |
closed | socket closed | remove connection, leave rooms, stop heartbeat | — (may reconnect) |
Resource cleanup checklist (server)¶
| Resource | Released on |
|---|---|
| Heartbeat timer | connection close |
| Room memberships | connection close |
Redis ws:room:<room> subscription | when the room has no members |
| Auction realtime subscription | connection close / auction.unsubscribe |
| Presence (subscriber) | last connection for the enrollment leaves |
| Connection registry entry | connection close |
Reconnect checklist (client)¶
- Refresh the access token (REST refresh endpoint).
- Open a fresh socket with the new token.
- For each active auction:
auction.subscribewithlastSequence= last seenserverSequence. - Rejoin
company:<id>,user:<id>,notifications,adminsas needed. - Discard any in-flight unacknowledged commands that share an
idwith a previous submission; reuse ids so replays are safe.
Related documents¶
- connection.md — transport, heartbeat, reconnection.
- events/auction.md — subscribe/unsubscribe and snapshot/replay.
- diagrams.md — connection lifecycle and reconnection diagrams.