MongoDB Academy · Lektion

Fehlerbehandlung und Wiederholungslogik

Sie fangen TransientTransactionError und UnknownTransactionCommitResult ab und implementieren die empfohlene Wiederholungsschleife für zuverlässige Produktionssysteme.

Lektion 3 von 413 Schritte

Fehlerbehandlung und Wiederholungslogik ist eine kostenlose MongoDB Academy-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des MongoDB Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der MongoDB Academy-Kurs umfasst insgesamt 4 Lektionen.

Warum Transaktionen eine Logik für Wiederholungen benötigen

MongoDB-Transaktionen können mit vorübergehenden Fehlern fehlschlagen – etwa kurzen Netzwerkstörungen, einem Wechsel des Primärservers oder Schreibkonflikten. Diese Bedingungen weisen nicht auf ein logisches Problem in Ihrem Code hin. Solche Fehler können sicher wiederholt werden: Die Transaktion wurde sauber zurückgerollt, und eine Wiederholung führt zu korrekten Ergebnissen. Ohne eine Logik für Wiederholungen würde Ihre Anwendung den Benutzern unnötige Fehler anzeigen, obwohl eine einfache Wiederholung das Problem beheben würde.

Zwei wichtige Fehlerkennzeichnungen

MongoDB klassifiziert Transaktionsfehler mit zwei wichtigen Fehlerkennzeichnungen: TransientTransactionError und UnknownTransactionCommitResult. TransientTransactionError bedeutet, dass die gesamte Transaktion fehlgeschlagen ist und sicher von Anfang an wiederholt werden kann. UnknownTransactionCommitResult bedeutet, dass die Bestätigung gesendet wurde, der Client aber nicht weiß, ob sie erfolgreich war – die Bestätigung muss wiederholt werden, nicht die gesamte Transaktion. Für beide Fälle ist eine unterschiedliche Strategie für Wiederholungen erforderlich.

// Checking error labels
if (error.hasErrorLabel('TransientTransactionError')) {
  // Retry the whole transaction from scratch
  console.log('Transient error — retrying transaction');
} else if (error.hasErrorLabel('UnknownTransactionCommitResult')) {
  // Retry only the commit, not the whole transaction
  console.log('Unknown commit result — retrying commit only');
} else {
  // Application error (e.g., InsufficientFunds) — do not retry
  throw error;
}

Ursachen für TransientTransactionError

TransientTransactionError tritt auf, wenn eine Transaktion aufgrund vorübergehender Bedingungen nicht fortgesetzt werden kann: Schreibkonflikte (eine andere Transaktion hat dasselbe Dokument zuerst geändert), Zeitüberschreitungen beim Erlangen von Sperren, Ausfälle durch Wahlen im Replica Set oder Probleme mit der Netzwerkverbindung. MongoDB rollt die Transaktion vollständig zurück, bevor dieser Fehler zurückgegeben wird. Die sichere Reaktion besteht darin, mit einer neuen Session von vorn zu beginnen und alle Operationen erneut auszuführen.

Ursachen für UnknownTransactionCommitResult

UnknownTransactionCommitResult tritt auf, wenn nach dem Bestätigen der Transaktion durch MongoDB, aber vor dem Eintreffen der Bestätigung beim Client ein Netzwerk-Timeout oder ein Failover auftritt. Die Transaktion kann bestätigt worden sein oder auch nicht. Die richtige Reaktion besteht darin, nur commitTransaction() zu wiederholen, nicht die gesamte Transaktion, da MongoDB doppelte Bestätigungsaufrufe für dieselbe Session erkennt und erfolgreich zurückgibt, wenn die Transaktion bereits bestätigt wurde.

Das empfohlene Muster für Wiederholungen

Die MongoDB-Dokumentation beschreibt ein Wiederholungsmuster mit zwei Schleifen: eine äußere Schleife, die bei TransientTransactionError die gesamte Transaktion wiederholt, und eine innere Schleife, die bei UnknownTransactionCommitResult nur die Bestätigung wiederholt. Dieses Muster behandelt alle vorübergehenden Fehlerzustände korrekt und wird intern von der Hilfsfunktion withTransaction() implementiert.

async function runTransactionWithRetry(txnFunc, client, session) {
  while (true) {
    try {
      await txnFunc(client, session); // execute transaction
      break; // success — exit loop
    } catch (error) {
      if (error.hasErrorLabel('TransientTransactionError')) {
        console.log('Retrying transaction due to TransientTransactionError');
        continue; // retry the whole transaction
      } else {
        throw error; // non-transient error — surface to caller
      }
    }
  }
}

Die Bestätigung separat wiederholen

Die Schleife für die Wiederholung der Bestätigung behandelt UnknownTransactionCommitResult. Sie ruft commitTransaction() fortlaufend auf, bis die Bestätigung entweder bestätigt wurde oder ein nicht wiederholbarer Fehler auftritt. MongoDB behandelt doppelte Bestätigungsaufrufe für dieselbe Transaktion idempotent. Wenn die Transaktion bereits bestätigt wurde, gibt die Wiederholung Erfolg zurück, ohne die Schreibvorgänge erneut anzuwenden.

async function commitWithRetry(session) {
  while (true) {
    try {
      await session.commitTransaction();
      console.log('Transaction committed');
      break;
    } catch (error) {
      if (error.hasErrorLabel('UnknownTransactionCommitResult')) {
        console.log('Retrying commit...');
        continue; // retry commit
      } else {
        throw error;
      }
    }
  }
}

Vollständige Implementierung mit zwei Schleifen

Durch die Kombination beider Wiederholungsschleifen erhalten Sie die vollständige, produktionsreife Transaktionsfunktion. Die Geschäftslogik wird in eine separate Funktion ausgelagert und als Parameter übergeben, sodass das Gerüst für Wiederholungen wiederverwendbar bleibt. Beachten Sie, dass die Session bei Wiederholungen wiederverwendet wird: Sie erstellen nicht für jede Wiederholung eine neue Session, müssen aber zu Beginn jedes Versuchs erneut startTransaction() aufrufen.

async function runTransaction(client, txnBody) {
  const session = client.startSession();
  try {
    let committed = false;
    while (!committed) {
      session.startTransaction({ readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority' } });
      try {
        await txnBody(session);
        // Commit with retry
        while (true) {
          try { await session.commitTransaction(); committed = true; break; }
          catch (e) {
            if (e.hasErrorLabel('UnknownTransactionCommitResult')) continue;
            else throw e;
          }
        }
      } catch (e) {
        await session.abortTransaction();
        if (!e.hasErrorLabel('TransientTransactionError')) throw e;
        // else retry
      }
    }
  } finally {
    await session.endSession();
  }
}

Warum withTransaction() einfacher ist

Die Hilfsfunktion session.withTransaction(callback, options) implementiert das Muster mit zwei Wiederholungsschleifen automatisch. Sie stellen lediglich die Callback-Funktion mit der Geschäftslogik und die Transaktionsoptionen bereit. Die Verwendung von withTransaction reduziert den erforderlichen Boilerplate-Code erheblich, verringert das Risiko einer fehlerhaften Implementierung der Wiederholungslogik und ist der empfohlene Ansatz für sämtlichen produktiven MongoDB-Transaktionscode im Node.js-Treiber.

// withTransaction handles all retry logic internally
const session = client.startSession();
try {
  await session.withTransaction(
    async (session) => {
      // Pure business logic — no retry code needed here
      await debitAccount(session, fromId, amount);
      await creditAccount(session, toId, amount);
      await insertAuditLog(session, fromId, toId, amount);
    },
    { readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority' } }
  );
} finally {
  await session.endSession();
}

Anwendungsfehler und vorübergehende Fehler

Nicht jeder Fehler innerhalb einer Transaktion ist vorübergehend und kann wiederholt werden. Fehler auf Anwendungsebene – etwa ein unzureichendes Guthaben, ein doppelter Benutzername oder eine ungültige Eingabe – sollten die Transaktion abbrechen und dem Benutzer einen Fehler zurückgeben, aber nicht wiederholt werden. Nur Fehler mit den Kennzeichnungen TransientTransactionError oder UnknownTransactionCommitResult sollten Wiederholungen auslösen. Kombinieren Sie beide Arten der Fehlerbehandlung in Ihrer Transaktions-Callback-Funktion: Lösen Sie Domänenfehler aus, die die äußere Ebene vor einem erneuten Versuch prüfen kann.

async function transfer(session, fromId, toId, amount) {
  const account = await db.collection('accounts').findOne({ _id: fromId }, { session });
  
  // Domain error — should NOT be retried
  if (account.balance < amount) {
    const err = new Error('Insufficient funds');
    err.isDomainError = true;
    throw err;
  }
  
  await db.collection('accounts').updateOne({ _id: fromId }, { $inc: { balance: -amount } }, { session });
  await db.collection('accounts').updateOne({ _id: toId }, { $inc: { balance: amount } }, { session });
}

Schreibkonflikte: die häufigste Ursache

Schreibkonflikte treten auf, wenn zwei Transaktionen gleichzeitig versuchen, dasselbe Dokument zu ändern. MongoDB verwendet optimistisches Sperren: Beide Transaktionen dürfen fortfahren, bis der Bestätigungszeitpunkt erreicht ist. Anschließend bricht MongoDB die Transaktion ab, bei der ein Konflikt auftritt. Die abgebrochene Transaktion erhält einen TransientTransactionError mit dem Code WriteConflict. Dieses Verhalten ist erwartungsgemäß und kein Fehler. Die Wiederholungsschleife behandelt es korrekt. Häufige Schreibkonflikte weisen auf ein stark umkämpftes Dokument hin, bei dem möglicherweise eine Neugestaltung des Schemas erforderlich ist.

// Detection: WriteConflict is a TransientTransactionError subtype
try {
  await session.commitTransaction();
} catch (error) {
  if (error.hasErrorLabel('TransientTransactionError')) {
    // This includes WriteConflict (code 112)
    console.log('Code:', error.code); // 112 = WriteConflict
    // Retry the whole transaction
  }
}

Maximale Anzahl von Wiederholungen: Endlosschleifen verhindern

Legen Sie in einer produktiven Wiederholungsschleife immer eine maximale Anzahl von Wiederholungen fest, um in problematischen Szenarien Endlosschleifen zu verhindern (z. B. bei andauernden Schreibkonflikten auf einem stark umkämpften Dokument). Werfen Sie nach dem Erreichen des Maximums den letzten Fehler, damit die Anwendung ihn kontrolliert behandeln kann. Verwenden Sie zwischen den Wiederholungen einen exponentiellen Backoff mit Jitter, um die durch mehrere Clients verursachte Konkurrenz zu verringern, wenn diese dieselbe Transaktion gleichzeitig wiederholen.

async function runWithMaxRetries(session, txnFn, maxRetries = 5) {
  let attempts = 0;
  while (attempts < maxRetries) {
    try {
      await session.withTransaction(txnFn);
      return;
    } catch (error) {
      attempts++;
      if (error.hasErrorLabel('TransientTransactionError') && attempts < maxRetries) {
        const delay = Math.min(100 * Math.pow(2, attempts) + Math.random() * 100, 5000);
        await new Promise(res => setTimeout(res, delay));
        continue;
      }
      throw error; // exceeded retries or non-transient
    }
  }
}

Kurze Überprüfung

Testen Sie Ihr Verständnis der Konzepte zu MongoDB und NoSQL-Datenbanken aus dieser Lektion.

Zusammenfassung der Lektion

In dieser Lektion haben Sie gelernt: TransientTransactionError bedeutet, dass die gesamte Transaktion von Anfang an wiederholt werden muss, UnknownTransactionCommitResult bedeutet, dass nur der Aufruf zur Bestätigung wiederholt werden muss, und withTransaction() implementiert dieses Muster mit zwei Wiederholungsschleifen automatisch und ist der empfohlene Ansatz für den produktiven Einsatz. Als Nächstes sehen wir uns Überlegungen zur Transaktionsleistung und Möglichkeiten zur Verringerung des damit verbundenen Aufwands an.

Kostenlos starten

Lerne JavaScript mit einem KI-Tutor — kostenlos

Schreibe und führe echten Code in deinem Browser aus, bekomme sofortige Hilfe von einem 24/7 KI-Tutor und setze dein Lernen im Web oder in der App fort.

Kurse
30
Lektionen
120

Häufig gestellte Fragen

Ist die Lektion „Fehlerbehandlung und Wiederholungslogik“ kostenlos?

Ja — der vollständige Text von „Fehlerbehandlung und Wiederholungslogik“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des MongoDB Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der MongoDB Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Fehlerbehandlung und Wiederholungslogik“?

Sie fangen TransientTransactionError und UnknownTransactionCommitResult ab und implementieren die empfohlene Wiederholungsschleife für zuverlässige Produktionssysteme. Du übst MongoDB Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um MongoDB Academy zu starten?

Keine Vorkenntnisse erforderlich. MongoDB Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Fehlerbehandlung und Wiederholungslogik“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser MongoDB Academy-Lektion Code schreiben und ausführen?

Ja. Jede MongoDB Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. ACID-Garantien in einem verteilten Dokumentenspeicher
  2. Eine Sitzung und eine Transaktion für mehrere Dokumente starten
  3. Fehlerbehandlung und Wiederholungslogik
  4. Performance-Aspekte von Transaktionen
← Zurück zu MongoDB Academy