RFC: Adaptive Exponential Backoff and Reconnection Jitter for DDP/WebSocket Connections
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Idoneità per principianti
- 45/100
- Tipo di issue
- Funzionalità
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Tranquilla
- Stack tecnologico
- react-native, typescript
- Ambito
- mobile, networking, performance
Direzione di ricerca
Inizia da app/lib/services/connect.ts e app/lib/services/sdk.ts, quindi determina come la riapertura nativa dell’SDK interagisce con il ReconnectionManager proposto. Implementa l’integrazione di backoff e jitter solo dopo aver scelto tra l’intercettazione degli eventi di connessione e l’override dello scheduler dell’SDK. Il lavoro è completato quando i tentativi di ripristino seguono la curva di ritardo proposta e si riconnettono correttamente dopo un’interruzione simulata del server, senza tentativi sincronizzati.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
RFC: Adaptive Exponential Backoff and Reconnection Jitter for DDP/WebSocket Connections
- Type: Resiliency & Infrastructure RFC
- Target Subsystem: Connection Service (
app/lib/services/connect.ts) & SDK Initializer (app/lib/services/sdk.ts) - Status: Proposed
1. Problem Statement & Root Cause Analysis
The client initializes its connection to the Rocket.Chat server via the Rocket.Chat JS SDK:
private initializeSdk(server: string): typeof Rocketchat {
return new Rocketchat({ host: server, protocol: 'ddp', useSsl: isSsl(server), reopen: __DEV__ ? 20000 : 5000 });
}
The Architectural Defect: Fixed Interval Reconnection
- Reconnection Polling: In production, the
reopenconfiguration parameter is set to a fixed5000ms(5 seconds). When the connection is dropped, the app blindly retries establishing a connection every 5 seconds. - Thundering Herd Problem: If a server experiences a temporary outage, or a network flap occurs, thousands of active mobile clients disconnect simultaneously. When they all attempt to reconnect at identical 5-second intervals, they hammer the server. This synchronized reconnection storm creates a distributed denial-of-service (DDoS) effect, preventing the server from recovering gracefully.
- Battery & Data Exhaustion: Mobile clients attempting aggressive WebSocket handshakes every 5 seconds on a flaky cellular network will rapidly deplete device battery and consume unnecessary user cellular bandwidth.
2. Proposed Architectural Solution
Replace the static reconnect interval with an Adaptive Exponential Backoff algorithm containing Randomized Jitter.
The Mathematical Model
The delay between reconnection attempts should follow this formula:
$$T_{\text{retry}} = \min(T_{\text{max}}, T_{\text{base}} \times M^{\text{attempt}}) + \text{random_jitter}$$
- $T_{\text{base}} = 1000,\text{ms}$ (Initial retry delay: 1 second)
- $M = 2.0$ (Backoff multiplier)
- $T_{\text{max}} = 60000,\text{ms}$ (Cap the delay at 60 seconds to guarantee connection within 1 minute of recovery)
- $\text{random_jitter} \in [0, 0.5 \times T_{\text{retry}}]$ (Add up to 50% randomized variation to desynchronize clients)
Expected Reconnection Delay Curve
| Attempt | Base Delay ($2^n$) | Jitter Range (0–50%) | Total Delay Range |
|---|---|---|---|
| 1 | 1.0s | 0.0s – 0.5s | 1.0s – 1.5s |
| 2 | 2.0s | 0.0s – 1.0s | 2.0s – 3.0s |
| 3 | 4.0s | 0.0s – 2.0s | 4.0s – 6.0s |
| 4 | 8.0s | 0.0s – 4.0s | 8.0s – 12.0s |
| 5 | 16.0s | 0.0s – 8.0s | 16.0s – 24.0s |
| 6 | 32.0s | 0.0s – 16.0s | 32.0s – 48.0s |
| 7 (Max) | 60.0s | 0.0s – 30.0s | 60.0s – 90.0s |
3. Proposed Blueprint & Implementation Strategy
Since the Rocket.Chat JS SDK under the hood uses a simple internal interval, we can either intercept the connection events or override the reconnection scheduler on the SDK driver directly.
Connection Resilience Controller Blueprint
// Proposed Reconnection Controller for app/lib/services/ReconnectionManager.ts
interface IBackoffConfig {
baseDelay: number; // 1000ms
maxDelay: number; // 60000ms
multiplier: number; // 2.0
jitter: number; // 0.5 (50%)
}
export class ReconnectionManager {
private attempt = 0;
private config: IBackoffConfig;
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
private onReconnectCallback: () => Promise<void>;
constructor(onReconnect: () => Promise<void>, config?: Partial<IBackoffConfig>) {
this.onReconnectCallback = onReconnect;
this.config = {
baseDelay: 1000,
maxDelay: 60000,
multiplier: 2.0,
jitter: 0.5,
...config
};
}
public reset() {
this.attempt = 0;
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
}
/**
* Computes the next retry delay using exponential backoff with full jitter.
*/
public getNextDelay(): number {
const rawDelay = this.config.baseDelay * Math.pow(this.config.multiplier, this.attempt);
const cappedDelay = Math.min(this.config.maxDelay, rawDelay);
// Add randomized jitter
const jitterRange = cappedDelay * this.config.jitter;
const randomJitter = Math.random() * jitterRange;
this.attempt++;
return cappedDelay + randomJitter;
}
/**
* Schedules the next reconnection attempt.
*/
public scheduleReconnection() {
if (this.reconnectTimer) return;
const delay = this.getNextDelay();
console.log(`[Reconnection] Connection lost. Attempt ${this.attempt}. Retrying in ${(delay / 1000).toFixed(2)} seconds...`);
this.reconnectTimer = setTimeout(async () => {
this.reconnectTimer = null;
try {
await this.onReconnectCallback();
} catch (error) {
// If attempt fails, schedule the next one
this.scheduleReconnection();
}
}, delay);
}
}
Integration in app/lib/services/connect.ts
Modify the socket closure logic to delegate reconnection scheduling to the ReconnectionManager instead of relying on the SDK's built-in hardcoded loop:
// Integration Blueprint inside app/lib/services/connect.ts
import { ReconnectionManager } from './ReconnectionManager';
import { store } from '../store/auxStore';
import { connectRequest } from '../../actions/connect';
let reconnectionManager: ReconnectionManager | null = null;
const performReconnection = async () => {
const activeServer = store.getState().server.server;
if (!activeServer) return;
store.dispatch(connectRequest());
await sdk.current.connect();
};
// Initialize manager
reconnectionManager = new ReconnectionManager(performReconnection);
// Inside the connect() sequence:
connectedListener = sdk.current.onStreamData('connected', () => {
reconnectionManager?.reset(); // Successful connect resets the backoff state
// ... normal login logic ...
});
closeListener = sdk.current.onStreamData('close', () => {
unsubscribeRooms();
store.dispatch(disconnectAction());
// Trigger the resilient backoff scheduling
reconnectionManager?.scheduleReconnection();
});
4. Implementation Steps
- Step 1: Add the
ReconnectionManagerfile to the services directory. - Step 2: Modify
initializeSdkinapp/lib/services/sdk.tsto disable native auto-reopening or override it (settingreopen: falseor extremely large values). - Step 3: Integrate
ReconnectionManagerinto theclosestream handler inapp/lib/services/connect.ts. - Step 4: Test connection drop recovery:
- Mock server downtime and observe that client reconnection attempts disperse along the backoff delay curve.
- Verify that when the server comes back online, all clients reconnect without overloading the server.
- Lingua principale
- TypeScript
- Stelle
- 2.4k
- Fork
- 1.5k
- Merge medio
- 1g 15h
- PR unite (30g)
- 29
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di RocketChat/Rocket.Chat.ReactNative
-
[IMPROVEMENT] Remove redundant `await` on synchronous `userPreferences.getBool` in `getUsersPresence`Forse già presa Una pull request collegata a questa issue è aperta o già unita. Apertatype: feature
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 92/100
RocketChat/Rocket.Chat.ReactNative#7737 ·
I maintainer di solito rispondono entro 1 giorno
-
UIKit modal: a viewSubmit response with no `type` field fails to close the modal on mobile (regression from #7057)Forse già presa @erikurt9 l’ha presa 106 giorni fa. Apertatype: bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
RocketChat/Rocket.Chat.ReactNative#7503 · 3 commenti · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
type: bug
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 86/100
RocketChat/Rocket.Chat.ReactNative#7437 · 1 commento · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
✅ tests 👷 ci type: feature
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
RocketChat/Rocket.Chat.ReactNative#7247 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
bug: goRoom navigates to broken RoomView after failed DM creation from searchForse già presa @deepak0x l’ha presa 216 giorni fa. Apertatype: bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
RocketChat/Rocket.Chat.ReactNative#7098 ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di RocketChat/Rocket.Chat.ReactNative
Issue simili
-
Bug: Broken RTL / BiDi text rendering when mixing Persian (Farsi) with English or numbersForse già presa Una pull request collegata a questa issue è aperta o già unita. Apertabug
Difficoltà 2/5 Mezza giornata Idoneità per principianti 68/100
amosblomqvist/learnharness#758 ·
I maintainer di solito rispondono entro 2 giorni
-
Change WorkspacesAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
bitfocus/companion-module-renewedvision-propresenter-api#134 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
rcpch/digital-growth-charts-react-component-library#304 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
dsh-market/dsh-market#1210 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
good first issue
Difficoltà 2/5 1-3 ore Idoneità per principianti 86/100
trustjonathan/STUDY-HUB#13 ·