Skip to content

Adaptive WebSocket Reconnection Stratum with Exponential Backoff, Full-Jitter, and Sticky Session Affinity #57

Description

@elizabetheonoja-art

Problem Statement / Feature Objective

The real-time dashboard relies on a persistent WebSocket connection to a load-balanced backend that streams telemetry and event data. Network partitions, server rolls, or DNS hiccups cause disconnections. A naive reconnect floods the server with retry storms and may land on a different backend node, losing in-memory subscription state. An adaptive reconnection layer must implement capped exponential backoff with full-jitter randomization, sticky session cookies, and a graceful subscription-recovery handshake so that the operator experiences at most 3 seconds of data loss during a failure cascade.

Technical Invariants & Bounds

  • Initial backoff: 500 ms; cap: 30 seconds; multiplier: 2.0; full-jitter: random(0, min(cap, base * 2^attempt)).
  • Maximum attempts before declaring terminal failure: 12 (covers ~3.5 minutes of reconnection). After 12, surface a persistent error banner.
  • Subscription recovery: the client must buffer the last 500 telemetry frames (each ~200 bytes to 100 KB) and replay them as a batch in the "recovery" field of the reconnection handshake.
  • WebSocket URL: wss://api.utilityprotocol.com/ws?nodeId={sticky} where "sticky" is a server-assigned node ID stored in sessionStorage.
  • Heartbeat: server sends "ping" every 15 seconds; client must respond with "pong" within 5 seconds or the connection is considered dead. Missed heartbeat count is exposed to the reconnection state machine.

Codebase Navigation Guide

  • src/hooks/useWebSocket.ts - Current WebSocket hook; needs reconnection logic extracted into a state machine.
  • src/services/reconnectMachine.ts - XState or custom finite state machine for reconnect phases: Connected, Disconnecting, Waiting, Connecting, Recovering, Failed.
  • src/utils/backoff.ts - Exponential backoff with full-jitter calculation.
  • src/store/slices/connectionSlice.ts - Redux slice tracking connection status, latency, node ID, missed heartbeats.
  • src/components/panels/ConnectionStatusBar.tsx - Fixed top bar showing connection quality (green/yellow/red).
  • src/utils/frameBuffer.ts - Circular buffer for telemetry frame replay during recovery.

Implementation Blueprint

  1. Implement fullJitterBackoff(attempt, base, cap) in src/utils/backoff.ts.
  2. Build the state machine src/services/reconnectMachine.ts with states: idle, connecting, connected, reconnecting(attempt), recovering, failed. Transitions: DISCONNECTED, HEARTBEAT_TIMEOUT, RECOVERY_SUCCESS, MAX_ATTEMPTS.
  3. Refactor useWebSocket to delegate connection lifecycle to the machine. On entering reconnecting, compute delay via full-jitter, schedule a reconnect with setTimeout.
  4. On successful reconnection (state recovering), send a JSON recovery frame: { type: 'recovery', lastSequenceId, frames: frameBuffer.drain() }. Wait for server to acknowledge with { type: 'recovery_ack', missedCount }.
  5. Backfill missed frames by requesting a REST endpoint GET /ws/replay?from={sequenceId}&to={serverCurrentSeq} if missedCount > 0.
  6. Maintain the frame buffer in frameBuffer.ts as a ring buffer of 500 TelemetryFrame objects. Push on every incoming frame; drain on recovery send.
  7. Dispatch connectionSlice actions for status transitions so UI components can react (e.g., dim charts during reconnect).

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions