Make live state reliable
Reconnect with backoff
Use jittered exponential backoff and clear connection states so an outage does not become a retry storm.
When the status server goes dark, every open tab tries to come back at once. Immediate synchronized retries turn a brief outage into a second incident.
Backoff spreads reconnect attempts. Clear connection states tell the user their data may be stale instead of showing a permanent green dot.
States the board exposes
connecting -> live -> reconnecting -> stale -> offline
let state = 'connecting'
let attempt = 0
let lastConfirmedAt = null
function setState(next) {
state = next
renderStatusBadge(state, lastConfirmedAt)
}
When an event applies, set lastConfirmedAt and move to live. When the socket closes unexpectedly, move to reconnecting and schedule the next attempt.
Jittered exponential delay
function scheduleReconnect(connect) {
attempt += 1
const base = Math.min(30000, 1000 * 2 ** attempt)
const jitter = Math.random() * 500
const delay = base + jitter
setTimeout(() => connect(), delay)
}
After five minutes of stability, reset attempt to zero. Without jitter, two thousand tabs still collide on the same second.
Show staleness honestly
Render the age of the last confirmed update:
renderStatusBadge('reconnecting', lastConfirmedAt)
// UI: "Reconnecting… last confirmed 4m ago"
Simulate a five minute outage with many clients. Graph reconnect attempts per second. You want a spread hill, not a spike.
SSE reconnects automatically; you still own UI state and snapshot recovery when auto-reconnect is not enough. WebSockets need this loop entirely in your code.
EventSource vs custom sockets
EventSource reconnects with its own timing. You still control UI honestly:
source.onerror = () => setState('reconnecting')
source.addEventListener('incident.updated', () => {
lastConfirmedAt = new Date()
setState('live')
})
For WebSockets you own the entire loop. Copy the same state names so operators see consistent behavior across viewer SSE and operator sockets.
Log each attempt:
console.log('reconnect attempt', attempt, 'delay', delay)
During a lab outage you want a CSV of attempts per second, not a hunch.
Pause retries while the document is hidden if the product allows it. Mobile users with thirty background tabs should not hammer your origin when nobody is looking at the board.
Try this on your own project: cap delay at thirty seconds, add jitter, reset after stability, and label stale data instead of hiding it.
Lesson completed