EMZETT.
Login

Retry und exponentielles Backoff: Wiederholen, ohne zu überlasten

Kurz: Eine fehlgeschlagene Anfrage erneut versuchen, aber mit wachsenden Pausen und etwas Zufall, damit ein belasteter Dienst nicht noch stärker unter Druck gerät.

Genauer: In verteilten Systemen scheitern Aufrufe oft nur kurz: Netzfehler, überlasteter Server, Neustart. Ein erneuter Versuch hilft dann. Wiederholt aber jeder Client sofort und ständig, verstärkt sich das Problem (Retry-Storm). Deshalb wartet man zwischen den Versuchen immer länger und verteilt die Zeitpunkte zufällig.

Im Detail

Exponentielles Backoff

Die Wartezeit verdoppelt sich: 1 s, 2 s, 4 s, 8 s … bis zu einem Höchstwert. Dadurch bekommt ein überlasteter Dienst Luft zum Erholen. Eine Obergrenze bei der Zahl der Versuche (zum Beispiel 5) verhindert endloses Warten.

Jitter

Ohne Zufall würden alle Clients, die gleichzeitig scheiterten, auch gleichzeitig wieder anfragen. Jitter mischt Zufall in die Wartezeit, etwa wartezeit = zufall(0, min(max, basis * 2^versuch)) („Full Jitter“). So verteilt sich die Last.

Beispiel in JavaScript

async function mitRetry(fn, versuche = 5) {
  for (let i = 0; ; i++) {
    try { return await fn(); }
    catch (e) {
      if (i >= versuche - 1 || !wiederholbar(e)) throw e;
      const warte = Math.random() * Math.min(10000, 500 * 2 ** i);
      await new Promise((r) => setTimeout(r, warte));
    }
  }
}

Wann nicht wiederholen

Nur vorübergehende Fehler (Zeitüberschreitung, 429, 502, 503) sind wiederholbar. Bei 400 oder 404 bringt ein Wiederholen nichts. Nicht idempotente Aktionen (Zahlung auslösen) dürfen nur wiederholt werden, wenn ein Idempotenz-Schlüssel doppelte Ausführung verhindert. Bei 429 oder 503 gibt der Server oft einen Retry-After-Header vor, der Vorrang hat. Ergänzend schützt ein Circuit Breaker, der nach vielen Fehlern vorübergehend gar nicht mehr anfragt.

Siehe auch: Circuit Breaker und Backpressure, Rate Limiting, REST-API-Design