Wiederholungen sind eines der einfachsten Resilienzmerkmale, die leicht falsch implementiert werden können, da sie auf kleinerer Skala harmlos erscheinen.
Ein einzelner Dienst, der eine fehlgeschlagene Anfrage wiederholt, ist kein Problem. Zehntausend Instanzen, die nach demselben Zeitplan wiederholen, oft schon.
Hier verwandelt sich die Wiederholungslogik von einem Wiederherstellungsmechanismus in einen Lastverstärker.
Das naive Muster
Das ist häufig und gefährlich:
for (let attempt = 0; attempt < 3; attempt += 1) {
try {
return await callPaymentApi();
} catch (error) {
await sleep(1000);
}
}
Alle Aufrufer scheitern gleichzeitig. Alle Aufrufer schlafen dieselbe Zeit. Alle Aufrufer wachen gleichzeitig auf und attackieren die Abhängigkeit erneut.
Was bessere Wiederholungslogik umfasst
Gutes Wiederholungsverhalten kombiniert normalerweise:
- exponentiellen Backoff
- Jitter
- ein maximales Wiederholungsbudget
Zum Beispiel:
function backoffMs(attempt: number) {
const base = 250 * 2 ** attempt;
const jitter = Math.random() * 0.3 * base;
return Math.min(base + jitter, 5000);
}
Der Punkt ist nicht mathematische Eleganz. Der Punkt ist, Tausende von Clients daran zu hindern, im Gleichschritt zu wiederholen.
Der Kompromiss
Wiederholungen sind nur sinnvoll für Fehler, die wahrscheinlich transient sind. Sie sind normalerweise falsch für:
- Validierungsfehler
- permanente Autorisierungsfehler
- Anfragen, die ohne Idempotenz nicht sicher wiederholt werden dürfen
Deshalb gehören die Wiederholungsrichtlinie und die Idempotenzrichtlinie zusammen.
Weitere Lektüre