Skip to content

Commit b4ff5f5

Browse files
committed
fix(app): reduce spurious SSE reconnects after sleep/resume
Two changes to improve connection stability when a device resumes from sleep or a browser tab returns from background: 1. Increase HEARTBEAT_TIMEOUT_MS from 15s to 30s. The server sends heartbeats every 10s, so 15s left only 5s of margin — under load or network jitter a heartbeat could arrive late and trigger a premature disconnect. 30s provides a comfortable 20s buffer while still detecting genuinely dead connections. 2. On visibilitychange, reset the heartbeat timer instead of immediately aborting. Previously, returning to a backgrounded tab after >15s would instantly abort the SSE connection and trigger a full reconnect cycle (re-bootstrap all directories). Now the stream gets one full heartbeat interval to prove it's still alive. If no event arrives within the timeout, the timer fires and aborts — same end result but without false positives for connections that are still healthy.
1 parent f54abe5 commit b4ff5f5

1 file changed

Lines changed: 11 additions & 2 deletions

File tree

packages/app/src/context/global-sdk.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,11 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
105105
const aborted = (error: unknown) => abortError.safeParse(error).success
106106

107107
let attempt: AbortController | undefined
108-
const HEARTBEAT_TIMEOUT_MS = 15_000
108+
// Must exceed the server's heartbeat interval (10s) with margin for
109+
// network jitter and event-loop delays. Too tight causes spurious
110+
// reconnects during idle periods; too loose delays detecting a dead
111+
// connection after sleep/resume.
112+
const HEARTBEAT_TIMEOUT_MS = 30_000
109113
let lastEventAt = Date.now()
110114
let heartbeat: ReturnType<typeof setTimeout> | undefined
111115
const resetHeartbeat = () => {
@@ -190,11 +194,16 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
190194
}
191195
})().finally(flush)
192196

197+
// When the tab becomes visible after being backgrounded, check if the
198+
// connection is likely stale. Instead of aborting immediately (which
199+
// tears down the stream even if it's still alive), restart the
200+
// heartbeat timer so the stream gets one full interval to deliver an
201+
// event. If nothing arrives within the timeout the timer will abort.
193202
const onVisibility = () => {
194203
if (typeof document === "undefined") return
195204
if (document.visibilityState !== "visible") return
196205
if (Date.now() - lastEventAt < HEARTBEAT_TIMEOUT_MS) return
197-
attempt?.abort()
206+
resetHeartbeat()
198207
}
199208
if (typeof document !== "undefined") {
200209
document.addEventListener("visibilitychange", onVisibility)

0 commit comments

Comments
 (0)