Skip to content

Comments

Fix eventsource reconnection: proper backoff and close() behavior#649

Open
jvosloo wants to merge 1 commit intobigskysoftware:masterfrom
jvosloo:fix/eventsource-reconnection-backoff
Open

Fix eventsource reconnection: proper backoff and close() behavior#649
jvosloo wants to merge 1 commit intobigskysoftware:masterfrom
jvosloo:fix/eventsource-reconnection-backoff

Conversation

@jvosloo
Copy link
Contributor

@jvosloo jvosloo commented Feb 14, 2026

Summary

Fixes three bugs in the EventSource plugin's reconnection logic that cause rapid-fire reconnection attempts when the server is unreachable, and prevent .close() from working in error handlers.

Bug 1: XOR instead of exponentiation

// Before (bitwise XOR — 2 ^ 2 = 0, giving 0ms delay\!)
var timeout = Math.random() * (2 ^ stub.retryCount) * 500;
// After (proper exponential backoff)
var timeout = Math.random() * Math.pow(2, stub.retryCount) * 500;

Bug 2: Backoff only triggers on CLOSED state

The error handler only reconnected when readyState == EventSource.CLOSED, but browsers keep readyState = CONNECTING on connection-refused errors and auto-retry every ~3 seconds — completely bypassing the backoff. Fixed by closing the EventSource on error to stop the browser's native auto-reconnect, then managing retry ourselves with proper exponential backoff.

Bug 3: .close() does not stop reconnection (#259)

Calling .close() in a user's on error handler sets readyState to CLOSED, which immediately triggers the plugin's own error handler to call open() — making the problem worse (as reported in #259). Fixed by:

  • Adding a closed flag set by .close() and checked before scheduling reconnection
  • Tracking the reconnect timeout ID and clearing it on .close()
  • Resetting closed on .open() so reopening after explicit close still works

Backoff behavior after fix

Retry Max delay
1 ~1s
2 ~2s
3 ~4s
4 ~8s
5 ~16s
6 ~32s
7 ~64s (cap)

Closes #259

Fixes three bugs in the EventSource plugin reconnection logic:

1. XOR instead of exponentiation: 2 ^ retryCount uses JavaScript
   bitwise XOR, not exponentiation. This produces erratic delays
   (e.g. 2 ^ 2 = 0, giving 0ms delay). Fixed to Math.pow(2, n).

2. Backoff only triggers on CLOSED state: The error handler only
   reconnected when readyState == EventSource.CLOSED, but browsers
   keep readyState = CONNECTING on connection-refused errors and
   auto-retry every ~3 seconds -- bypassing the backoff entirely.
   Fixed by closing the EventSource on error to stop the browser
   native auto-reconnect, then managing retry with proper backoff.

3. close() does not stop reconnection: Calling close() in a user
   on error handler sets readyState to CLOSED, which immediately
   triggers the plugin own error handler to call open() -- making
   the problem worse. Fixed by tracking a closed flag that is set
   in close() and checked before scheduling reconnection.

Fixes bigskysoftware#259
@jvosloo jvosloo force-pushed the fix/eventsource-reconnection-backoff branch from 24b4720 to d9b3226 Compare February 14, 2026 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

eventsource close() does not work on a failing connection.

1 participant