e-bon
e-bon.ro
Realtime

Reconectare și ciclu de viață

Programul de auto-reconectare, controalele close / suspend / resume și hook-ul CursorStore pentru livrarea viitoare at-least-once.

Reconectare și ciclu de viață

RealtimeClient este construit să rămână conectat pe toată durata de viață a procesului tău. Se reconectează automat, expune controale explicite de suspend și resume pentru gazde mobile / atente la baterie și pune la dispoziție un punct de injecție CursorStore, astfel încât livrarea viitoare at-least-once (E-1b / E-10) să poată conecta un strat de persistență fără să schimbi codul tău.

Auto-reconectare cu backoff exponențial

Când socket-ul cade, clientul se reconectează automat. Programul este fix:

ConstantăValoareÎnțeles
DEFAULT_RECONNECT_DELAY1000 msPrima reîncercare are loc la 1 secundă după deconectare.
RECONNECT_BACKOFF_FACTOR2Fiecare reîncercare următoare dublează întârzierea.
MAX_RECONNECT_DELAY30000 msPlafon — întârzierea nu trece niciodată de 30 de secunde.

Așadar, intervalele dintre încercările de reconectare merg 1s → 2s → 4s → 8s → 16s → 30s → 30s → … până când socket-ul revine, moment în care întârzierea se resetează la 1 secundă pentru următoarea deconectare. Evenimentul connected se declanșează de fiecare dată când socket-ul se redeschide; disconnected se declanșează de fiecare dată când cade.

realtime.on('connected', () => console.log('realtime: live'));
realtime.on('disconnected', () => console.warn('realtime: căzut — se reconectează'));
realtime.on('error', (err) => console.error('realtime: eroare socket', err));

Închide clientul

Apelează realtime.close() când ai terminat. Marchează clientul ca închis, anulează orice timer de reconectare în așteptare și închide socket-ul subiacent. După close(), clientul este permanent închis — construiește unul nou dacă vrei să o iei de la capăt.

process.on('SIGTERM', () => realtime.close());

Suspendare și reluare

suspend() și resume() sunt primitivele potrivite când vrei să opreşti temporar primirea evenimentelor fără să renunți permanent la conexiune — de exemplu când o aplicație trece în fundal, când dispozitivul se trezește din sleep sau când un controler predă un dispozitiv altei instanțe.

// Aplicația a trecut în fundal — închide socket-ul, dar păstrează listenerii.
appLifecycle.on('background', () => realtime.suspend());

// Aplicația a revenit în prim-plan — reconectează-te imediat.
appLifecycle.on('foreground', () => realtime.resume());

Ce face fiecare apel:

  • suspend() — închide socket-ul, anulează orice timer de reconectare în așteptare și refuză să se reconecteze până când resume() este apelat. No-op dacă este deja suspendat.
  • resume() — șterge flag-ul de suspendare, resetează întârzierea de backoff la 1 secundă și se conectează imediat. No-op dacă nu este suspendat în prezent.

Listenerii rămân înregistrați între ciclurile suspend / resume — doar socket-ul subiacent ciclează.

suspend()nu marchează clientul ca închis. După resume() aceeași instanță este reutilizată; nu trebuie să-ți re-adaugi listenerii.

Mesajele incorecte sunt aruncate în tăcere

Cadrele de protocol server-către-dispozitiv (schemele de pe pagina Scheme) sunt validate strict cu zod. Un cadru care nu trece validarea este aruncat și logat prin console.warn ([RealtimeClient] dropping invalid wire frame), astfel încât un mesaj incorect al gateway-ului să nu poată ajunge niciodată la un listener ca obiect parțial tipat. Socket-ul rămâne deschis.

Cadrele non-JSON sunt aruncate în tăcere, fără log.

Cursor store

RealtimeClientOptions.cursorStore este hook-ul de persistență pentru cursorul ultimului eveniment văzut. Clientul actual (E-1a) doar păstrează referința — căile de citire / scriere sunt conectate de munca de livrare at-least-once din E-1b / E-10. Implementarea interfeței astăzi este forward-compatibilă:

import { RealtimeClient, type CursorStore } from '@e-bon/realtime';

const cursorStore: CursorStore = {
  async load() {
    return (await fs.readFile('cursor', 'utf8').catch(() => null)) ?? null;
  },
  async save(cursor: string) {
    await fs.writeFile('cursor', cursor, 'utf8');
  },
};

const realtime = new RealtimeClient('https://api.e-bon.ro', {
  apiKey: process.env.EBON_API_KEY!,
  cursorStore,
});

Implementările pot fi sprijinite pe AsyncStorage, localStorage, un fișier sau orice altceva care satisface contractul cu două metode:

interface CursorStore {
  load(): Promise<string | null>;
  save(cursor: string): Promise<void>;
}

Unde mergi mai departe

  • Evenimente — cele opt forme de payload partenere pe care le poți asculta.
  • Scheme — cadrele server→dispozitiv și dispozitiv→server.