Patterns
Retry avec backoff exponentiel et jitter
Algorithme initial=1s × 2^attempt ± 10% jitter, max 60s
Le retry naïf (intervalles fixes) crée des thundering herds : à la reprise d'un service, tous les clients retentent en même temps et le re-saturent. Le backoff exponentiel avec jitter étale les retries dans le temps. Algorithme Scell.io pour les webhooks et appels SuperPDP : `delay = min(initial × 2^attempt, max) × (1 + random(-jitter, +jitter))` avec `initial=1s`, `multiplier=2`, `max=60s`, `jitter=0.1`. Sur 5 tentatives, les délais sont approximativement : 1s, 2s, 4s, 8s, 16s (± 10%). Total ~31s pour atteindre l'épuisement, contre 5 retries instantanés qui surchargent. Le jitter est crucial : sans lui, les clients reformeraient des vagues synchrones. À implémenter côté client (SDK) ET côté serveur (worker queue).
À retenir
- Formule : delay = min(initial × 2^n, max) × (1 + jitter)
- Valeurs Scell.io : initial=1s, multiplier=2, max=60s, jitter=±10%
- Jitter obligatoire pour éviter les thundering herds synchrones
- 5 tentatives ≈ 31s d'attente totale (1+2+4+8+16)
- Implémenter côté client SDK ET côté worker queue serveur
Exemple de code
async function retryWithBackoff<T>(
fn: () => Promise<T>,
opts = { maxAttempts: 5, initialMs: 1000, maxMs: 60_000, jitter: 0.1 }
): Promise<T> {
let lastError: Error | undefined;
for (let attempt = 0; attempt < opts.maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
lastError = err as Error;
if (attempt === opts.maxAttempts - 1) break;
const base = Math.min(opts.initialMs * 2 ** attempt, opts.maxMs);
const jitter = 1 + (Math.random() * 2 - 1) * opts.jitter;
await new Promise(r => setTimeout(r, base * jitter));
}
}
throw lastError;
}