Zruck zum Blog, du Zniachtl
Mai 2026·3 Min. zum LesnVo da KI gmocht

Retry-Strategien: Exponential Backoff und Jitter

#backend#reliability#networking

A Netzwerkaufruf scheitert. Die offensichtliche Lösung is "einfach nochamoi probiern" - aber naiv zu wiederholn is, wia aus am kurzen Wackler a kompletter Ausfall wird, weil jeder Client genau im söbn Moment auf den sich erholenden Service eindrischt. Exponential Backoff mit Jitter is die Standardlösung, und sie is einfach gnua, um sie in etwa fuffzehn Zeiln zu implementiern, sobois ma versteht, warum jedes Stückl do is.

Warum naive Wiederholungen alles schlimmer mochen

Sogn ma, a nachgelagerter Service fällt unter Last für zwoa Sekunden aus. Tausend Clients kriagn olle an fehlgschlogenen Request, und olle probiern sofort nochamoi. Da Service, grod so wieda hochkummen, kriagt dieselben tausend Requests nochamoi auf oamoi - plus den neichn Traffic, der inzwischen kemman is. Er fallt wieda um. Des is a Retry Storm, und es is a häufiger Weg, wia aus am kleinen, vorübergehenden Wackler a lang andauernder Ausfall wird.

Zwoa Probleme müssn behoben wern: Wiederholungen, de zu früh passiern, und Wiederholungen, de bei olle Clients im Gleichschritt passiern.

Exponential Backoff: Wiederholungen auseinanderziehn

Statt sofort zum wiederholn, wart nach jedem Fehlschlag länger - typischerweise verdoppelt sich der Delay jedes Moi.

async function withBackoff(fn, { maxRetries = 5, baseDelayMs = 200, maxDelayMs = 10000 } = {}) {
  let attempt = 0;

  while (true) {
    try {
      return await fn();
    } catch (err) {
      attempt++;
      if (attempt > maxRetries) throw err;

      const delay = Math.min(baseDelayMs * 2 ** (attempt - 1), maxDelayMs);
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
}

Mit baseDelayMs: 200 sand die Delays ungefähr 200ms, 400ms, 800ms, 1600ms, 3200ms - sie wachsn schnö, begrenzt durch maxDelayMs, damit a wackelige Abhängigkeit an Request ned Minuten warten lasst. Allein des behebt "zu früh wiederholn". Für "im Gleichschritt wiederholn" bringt's nix, weil jeder Client genau dieselbe Delay-Folge berechnet.

Jitter: Den Gleichschritt brechn

Wenn jeder Client genau 200ms zruckhoit, dann genau 400ms, wiederholn sie im Verhältnis zueinander immer noch im söbn Augenblick - nur seltener. Jitter fügt dem Delay Zufall hinzu, damit sich Wiederholungen über die Zeit verteiln, statt in synchronisierten Wellen anzukemman.

function backoffWithFullJitter(attempt, baseDelayMs, maxDelayMs) {
  const cap = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
  return Math.random() * cap;
}

Des is "Full Jitter" - da Delay is a Zufallswert zwischn 0 und der exponentiellen Obergrenze, statt die Obergrenze söba. Der Architektur-Blog von AWS hot den Ansatz popularisiert, nachdem er gfunden hot, dass er sowohl reines Exponential Backoff ois a "Equal Jitter" (Cap/2 plus a Zufallsanteil von Cap/2) bei hoher Konkurrenz schlägt, genau weil er die Wiederholungen über das breiteste Fenster verteilt.

Einbindung in die Retry-Schleifn:

async function withBackoffAndJitter(fn, { maxRetries = 5, baseDelayMs = 200, maxDelayMs = 10000 } = {}) {
  let attempt = 0;

  while (true) {
    try {
      return await fn();
    } catch (err) {
      attempt++;
      if (attempt > maxRetries) throw err;

      const delay = backoffWithFullJitter(attempt - 1, baseDelayMs, maxDelayMs);
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }
}

Ned jeder Fehler verdient a Wiederholung

Blind bei jedem Fehler zum wiederholn is a eigener Bug. A 400 Bad Request oda a 401 Unauthorized scheitert identisch, egal wie oft du's wiederholst - du fügst nur sinnlos Last hinzu. Wiederhol nur Fehler, de plausibel vorübergehend sand:

  • Fehler auf Netzwerkebene (Connection Reset, Timeout)
  • 429 Too Many Requests
  • 502, 503, 504 - Gateway-/Upstream-Fehler
function isRetryable(err) {
  if (err.code === 'ECONNRESET' || err.code === 'ETIMEDOUT') return true;
  if (err.status && [429, 502, 503, 504].includes(err.status)) return true;
  return false;
}

Prüf des, bevor du wartest, und wirf bei allem Andern sofort weiter:

} catch (err) {
  if (!isRetryable(err)) throw err;
  // ...backoff logic
}

Retry-After beachtn

A 429- oda 503-Antwort kummt oft mit am Retry-After-Header, der dir genau sagt, wie long du warten soisst. Wenn er da is, nimm ihn statt deines eigenen berechneten Delays - da Server woaß seine eigene Erholungszeit besser ois deine Schätzung.

function delayFromResponse(err, fallbackDelayMs) {
  const retryAfter = err.headers?.['retry-after'];
  if (!retryAfter) return fallbackDelayMs;

  const seconds = Number(retryAfter);
  return Number.isFinite(seconds) ? seconds * 1000 : fallbackDelayMs;
}

Das gesamte Retry-Budget begrenzen, ned nur den Delay

maxRetries alloa kann a einzelne logische Operation immer noch lang dauern lossn, wenn die Obergrenzen großzügig sand - fünf Wiederholungen mit 10 Sekunden Max-Delay sand potenziell über 30 Sekunden, de man mit Wiederholen von oam Aufruf verbringt. Wenn da Aufrufer sein eigenes Timeout hot (a Nutzer, der auf an Request wartet, a Queue-Consumer mit Visibility-Timeout), track die verstrichene Zeit und steig früh aus:

async function withDeadline(fn, deadlineMs) {
  const start = Date.now();
  return withBackoffAndJitter(async () => {
    if (Date.now() - start > deadlineMs) {
      throw new Error('retry deadline exceeded');
    }
    return fn();
  });
}

Fazit

Exponential Backoff behebt Wiederholungen, de zu früh passiern. Jitter behebt Wiederholungen, de bei Clients im Gleichschritt passiern. Koans von beiden ersetzt die Prüfung, ob sich a Fehler überhaupt zum Wiederholn lohnt, und koans soit an Retry-After-Header ignoriern, wenn si da Server die Mühe mocht, oan zu schickn. De meisten HTTP-Client-Bibliotheken (axios-Interceptors, gots Retry-Option, Resilienz-Bibliotheken wia cockatiel oda polly in .NET) implementiern des olles scho - greif zu oana, bevor du's im Produktivcode von Hand baust, aber wiss, wos sie unter da Haubn mocht, damit du erkennst, wann die Standardwerte ned zu deiner Situation passen.

NeicherDatenbank-Transaktionen und Isolationslevel erklärtElterA sanfte Einführung in WebSockets