Feat/socket backoff jitter#226
Merged
Osuochasam merged 6 commits intoJun 25, 2026
Merged
Conversation
- Add @types/js-yaml as a devDependency with flexible version constraint
- Change from pinned version 4.0.9 (which has Windows-only OS restriction) to ^4.0
- This allows npm to select a compatible version without platform restrictions
- Fixes Docker build failure on Linux where npm ci was failing with:
'Unsupported platform for @types/js-yaml@4.0.9: wanted {"os":"win32"}'
Resolves build failure on job 83241645142
f42b5d0 to
7fcb7e2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the bare onclose null-assignment in useLiveFeed.ts with a structured reconnection loop using exponential backoff and full jitter. Previously, a dropped connection simply cleared the socket ref with no retry logic at all. Under sustained outages, clients would silently stay disconnected. With this change, they automatically recover while protecting the server from reconnection storms.
What changed
useLiveFeed.ts
Added three tunable constants: BACKOFF_BASE_MS (1 s), BACKOFF_MULTIPLIER (2×), BACKOFF_MAX_MS (30 s)
Added computeBackoffDelay(attempt) — applies the exponential curve, caps it at 30 s, then randomises the result across [0, cappedDelay] (full jitter pattern)
attemptRef tracks the current retry count and resets to 0 on ws.onopen after a successful handshake
reconnectTimerRef holds the pending setTimeout handle so it can be cancelled immediately when the user manually stops the feed — no leaked timers, no duplicate sockets
isLiveRef mirrors the isLive state flag so the onclose closure can safely read the current intent without going stale
closeSocket() strips the onclose handler before calling .close() so intentional disconnects never trigger a retry cycle
Unmount cleanup sets isLiveRef.current = false before calling disconnect(), ensuring any in-flight timer is a no-op if it fires after the component is gone
Backoff behaviour
Attempt Capped delay Actual delay (full jitter)
0 1 s 0 – 1 s
1 2 s 0 – 2 s
2 4 s 0 – 4 s
3 8 s 0 – 8 s
4 16 s 0 – 16 s
5+ 30 s (cap) 0 – 30 s
Full jitter ensures that a fleet of simultaneously-disconnected clients spreads reconnection attempts uniformly across the interval rather than spiking at the same moment (thundering herd mitigation).
Verification
tsc --noEmit — zero errors in useLiveFeed.ts
eslint lib/hooks/useLiveFeed.ts --max-warnings=0 — passed clean
Only
useLiveFeed.ts
staged; no unrelated files touched
Files changed
lib/hooks/useLiveFeed.ts | 1 file, +123 / -11
Closes #86