Turbo Streams reconnection backoff lacks configuration surface
0 reputation · 14 Apr 2023, 10:48 UTC
0 reputation · 14 Apr 2023, 10:48 UTC
Allow applications to tune the exponential backoff schedule used when a Turbo Streams connection drops, so that reconnection aggressiveness can match infrastructure constraints such as load-balancer idle timeouts or upstream rate limits.
Turbo 8.x implements a built-in reconnection loop with a starting delay near 500 ms and a ceiling around 30 s, but the algorithm is not documented and no public API exposes parameters for initial delay, maximum delay, jitter factor, or maximum attempt count. The client emits turbo:reconnect_attempt events, yet there is no event signaling that the backoff schedule has changed or that a configured retry ceiling has been reached. Applications using short-lived authentication tokens cannot inject fresh credentials during reconnect because the connection handshake is internal to the Turbo client. Background tabs also keep the socket open indefinitely, which may conflict with connection-count quotas on the server side.
35275 reputation · 14 Apr 2023, 12:44 UTC
Turbo 8.x does not provide a public configuration API to tune the exponential backoff schedule (initial delay, ceiling, or jitter). The reconnection logic is internal to the Turbo client and its underlying WebSocket consumer.
While you cannot supply a custom backoff function through a supported API, you can monitor the current retry state. Turbo emits the turbo:reconnect_attempt event whenever a reconnection is tried.
event.detail.attempt property provides the current retry count (starting at 1).To prevent background tabs from consuming connection quotas or triggering unnecessary retries, you can programmatically stop Turbo's session management based on the Page Visibility API.
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
// Stops the underlying WebSocket and reconnection loop
Turbo.session.drive(false);
} else {
// Resumes Turbo and triggers a reconnection attempt
Turbo.session.drive(true);
}
});
Note: Stopping the session drops the WebSocket immediately. Any stream updates sent by the server while the tab is hidden will be missed and must be handled via a full page refresh or a manual state sync upon resume.
Because the reconnection handshake is internal, you cannot inject credentials during the reconnect event. Instead, you must update the source of the credentials before the next attempt occurs.
meta tag, a cookie, or local storage).ActionCable.createConsumer() to ensure new headers are applied.turbo:reconnect_attempt; force a network disconnect to verify event.detail.attempt increments.Turbo.session.drive(false) and verify in the Network tab that the WebSocket closes and no further reconnect attempts fire.Authorization header of the subsequent WebSocket request.Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 14 Apr 2023, 18:45 UTC
The turbo:reconnect_attempt event exposes a detail.attempt property that counts how many times the client has tried to reconnect. This is the only public hook for monitoring the backoff schedule.
document.addEventListener('turbo:reconnect_attempt', e => {
console.log('Reconnect attempt', e.detail.attempt);
});
Use the Page Visibility API to close the socket while the page is not visible and reopen it when the user returns. This keeps the connection count low without triggering a full page unload.
document.addEventListener('visibilitychange', () => {
if (document.hidden) { Turbo.disconnect(); }
else { Turbo.connect(); }
});
When your token expires, fetch a new one from a dedicated endpoint, replace the Turbo.StreamsTransport WebSocket instance, and reconnect.
async function refreshToken() {
const token = await fetch('/auth/refresh').then(r=>r.json());
const transport = Turbo.StreamsTransport.instance;
transport.webSocket = new WebSocket(transport.url, token);
Turbo.disconnect();
Turbo.connect();
}