Retry-Logik richtig umgesetzt: Exponentielles Backoff für zuverlässige Systeme implementieren

In der Softwareentwicklung ist eine zuverlässige Wiederholungslogik entscheidend für den Umgang mit intermittierenden Fehlern, wie z.B. Netzwerkproblemen oder vorübergehenden Ausfällen. Kürzlich stieß ich auf einen Codebestand, in dem ein Entwickler eine for-Schleife mit einem festen Zeitintervall verwendete, um fehlgeschlagene Operationen erneut zu versuchen. Obwohl dieser Ansatz einfach erscheinen mag, fehlt ihm die Resilienz, die für Anwendungen in der realen Welt erforderlich ist. Hier kommt das Exponential Backoff ins Spiel – eine Strategie, die darauf abzielt, Wiederholungen intelligenter und effizienter zu gestalten.

In diesem Artikel werden wir uns ansehen, wie Exponential Backoff funktioniert, welche Vorteile es gegenüber einer einfachen Wiederholungsschleife hat und wie Sie es implementieren können, um die Zuverlässigkeit Ihres Systems zu verbessern. Ich werde Ihnen auch ein praktisches Beispiel mit einem E-Mail-Sende-Modul zeigen, das veranschaulicht, wie Sie Exponential Backoff nutzen können, um eine robustere Fehlerbehandlung zu gewährleisten.

Exponential Backoff ist eine Wiederholungsstrategie, bei der die Wartezeit zwischen den Wiederholungsversuchen nach jedem Fehler exponentiell zunimmt. Anstatt in festen Intervallen zu wiederholen, wartet jeder nachfolgende Versuch länger als der vorherige – typischerweise verdoppelt sich die Verzögerung jedes Mal. Zum Beispiel, wenn die anfängliche Verzögerung 1 Sekunde beträgt, erfolgen die nächsten Wiederholungen nach 2, 4, 8 Sekunden usw. Dieser Ansatz hilft, die Belastung des Systems zu reduzieren und das Risiko zu minimieren, externe Dienste während Zeiten hoher Nachfrage zu überfordern.

Durch die Gewährung von mehr Zeit zwischen den Wiederholungen gibt Exponential Backoff vorübergehenden Problemen die Möglichkeit, sich zu lösen, was zu einer effizienteren Fehlerbehandlung und verbesserter Anwendungsstabilität führt.

  • Reduzierte Systemlast: Durch das zeitliche Auseinanderziehen von Wiederholungsversuchen minimiert das exponentielle Backoff die Wahrscheinlichkeit, Server zu überlasten, besonders nützlich zur Bewältigung von Ratenbeschränkungen oder vorübergehenden Ausfällen.

  • Effiziente Fehlerbehandlung: Die zunehmende Verzögerung ermöglicht es vorübergehenden Problemen, sich natürlicherweise zu lösen, was die Wahrscheinlichkeit eines erfolgreichen erneuten Versuchs verbessert.

  • Verbesserte Stabilität: Besonders für stark frequentierte Systeme verhindert es einen Strom von Wiederholungsversuchen und sorgt dafür, dass Anwendungen reibungslos laufen, ohne übermäßigen Ressourcenverbrauch.

  • Erhöhte Latenz: Da jeder erneute Versuch progressiv länger dauert, kann das exponentielle Backoff zu Verzögerungen führen, insbesondere wenn viele Wiederholungsversuche vor dem Erfolg erforderlich sind.

Exponentielles Backoff ist besonders nützlich in Szenarien, in denen Systeme mit externen Diensten interagieren oder große Datenmengen verarbeiten. Hier sind einige weitere gängige Anwendungsfälle:

  1. Rate-Limited APIs: Einige APIs haben Rate-Limits, die Anfragen innerhalb einer bestimmten Zeit begrenzen. Das exponentielle Backoff-Verfahren hilft dabei, sofortige Wiederholungsversuche zu vermeiden, die das Limit überschreiten könnten, und gibt dem Limit Zeit zum Zurücksetzen.

  2. Network Instability: Bei vorübergehenden Netzwerkausfällen oder Zeitüberschreitungen hilft das exponentielle Backoff-Verfahren, indem längere Wartezeiten zwischen den Versuchen ermöglicht werden, um dem Netzwerk Zeit zur Stabilisierung zu geben.

  3. Database Connections: Beim Verbinden mit Datenbanken bei hoher Last hilft das exponentielle Backoff-Verfahren, eine weitere Überlastung zu verhindern, indem Wiederholungsversuche verzögert werden und der Datenbank Zeit zur Erholung gegeben wird.

  4. Queue Systems: In Nachrichtenwarteschlangensystemen kann bei einem Fehler, der zum Scheitern einer Nachricht führt, die Verwendung des exponentiellen Backoff-Verfahrens für Wiederholungsversuche ein schnelles erneutes Verarbeiten verhindern und Zeit für die Lösung vorübergehender Probleme einräumen.

Um Exponential Backoff zu demonstrieren, werden wir einen grundlegenden E-Mail-Versanddienst erstellen, der den Versand von E-Mails bei Auftreten eines Fehlers wiederholt. Dieses Beispiel zeigt, wie Exponential Backoff den Wiederholungsprozess im Vergleich zu einer einfachen For-Schleife verbessert.

import nodemailer from "nodemailer";
import { config } from "../common/config";
import SMTPTransport from "nodemailer/lib/smtp-transport";

const emailSender = async (
  subject: string,
  recipient: string,
  body: string
): Promise<boolean> => {
  const transport = nodemailer.createTransport({
    host: config.EMAIL_HOST,
    port: config.EMAIL_PORT,
    secure: true,
    auth: { user: config.EMAIL_SENDER, pass: config.EMAIL_PASSWORD },
  } as SMTPTransport.Options);

  const mailOptions: any = {
    from: config.EMAIL_SENDER,
    to: recipient,
    subject: subject,
  };

  const maxRetries = 5; // maximum number of retries before giving up
  let retryCount = 0;
  let delay = 1000; // initial delay of 1 second

  while (retryCount < maxRetries) {
    try {
      // send email
      await transport.sendMail(mailOptions);
      return true;
    } catch (error) {
      // Exponential backoff strategy
      retryCount++;
      if (retryCount < maxRetries) {
        const jitter = Math.random() * 1000; // random jitter(in seconds) to prevent thundering herd problem
        const delayMultiplier = 2
        const backOffDelay = delay * delayMultiplier ** retryCount + jitter;
        await new Promise((resolve) => setTimeout(resolve, backOffDelay));
      } else {
        // Log error
        console.log(error)
        return false; // maximum number of retries reached
      }
    }
  }
  return false;
};

Die Implementierung von Exponential Backoff umfasst die Anpassung bestimmter Parameter, um sicherzustellen, dass die Wiederholungsstrategie gut zu den Bedürfnissen Ihrer Anwendung passt. Die folgenden Schlüsselparameter beeinflussen das Verhalten und die Leistung von Exponential Backoff in einem Wiederholungsmechanismus:

  1. Initiale Verzögerung
  • Zweck: Legt die Wartezeit vor der ersten Wiederholung fest. Sie sollte lang genug sein, um sofortige Wiederholungen zu verhindern, aber kurz genug, um merkliche Verzögerungen zu vermeiden.

  • Empfohlene Einstellung: Beginnen Sie mit einer Verzögerung von 500 ms bis 1000 ms. Für kritische Systeme verwenden Sie eine kürzere Verzögerung, während weniger dringende Vorgänge eine längere Verzögerung haben können.

  1. Verzögerungsvervielfacher
  • Zweck: Steuert, wie schnell die Verzögerung nach jedem erneuten Versuch zunimmt. Ein Multiplikator von 2 verdoppelt die Verzögerung (z.B. 1s, 2s, 4s).

  • Empfohlene Einstellung: In der Regel balanciert ein Multiplikator zwischen 1,5 und 2 Reaktionsfähigkeit und Stabilität aus. Höhere Multiplikatoren (z.B. 3) können geeignet sein, wenn das System längere Verzögerungen zwischen den Wiederholungen verarbeiten kann.

  1. Maximale Wiederholungen
  • Zweck: Begrenzt die Wiederholungsversuche, um übermäßige Wiederholungen zu verhindern, die Ressourcen verbrauchen oder die Systemlast erhöhen könnten.

  • Empfohlene Einstellung: Ein Bereich von 3 bis 5 Wiederholungen ist in der Regel ausreichend für die meisten Anwendungen. Darüber hinaus sollte die Operation möglicherweise als fehlgeschlagen protokolliert oder anders verwaltet werden, beispielsweise durch Benachrichtigung des Benutzers oder Auslösen eines Alarms.

  1. Jitter (Randomisierung)
  • Zweck: Fügt jeder Verzögerung Zufälligkeit hinzu, um zu verhindern, dass Wiederholungen clustering und einen Thundering Herd-Effekt verursachen.

  • Empfohlene Einstellung: Fügen Sie jedem Wiederholungsintervall eine zufällige Verzögerung zwischen 0 und 500 ms hinzu. Dieser Jitter hilft, die Wiederholungsversuche gleichmäßiger über die Zeit zu verteilen.

Durch die Verwendung von Exponential Backoff fügen Sie Ihrer Anwendung Resilienz hinzu und bereiten sie darauf vor, unerwartete Probleme zu bewältigen. Es ist eine kleine Änderung mit großer Wirkung, insbesondere wenn Ihre Anwendung wächst.

Und das war’s vorerst, Leute. Hinterlasst gerne einen Kommentar und stellt Fragen, wenn ihr welche habt. Prost auf den Bau zuverlässigerer und widerstandsfähigerer Apps.

Viel Spaß beim Coden! 👨‍💻❤️

Source:
https://timothy.hashnode.dev/implementing-exponential-backoff-for-reliable-systems