EMZETT.
Login

Outbox-Muster: Datenbank und Nachricht zuverlässig koppeln

Kurz: Beim Outbox-Muster schreibt man die zu verschickende Nachricht in dieselbe Datenbank-Transaktion wie die Änderung. Ein getrennter Prozess liest die Outbox-Tabelle und verschickt sie. So geht nie eine Änderung ohne Nachricht (oder umgekehrt) verloren.

Das Problem: zwei Systeme, eine Wahrheit

Eine Bestellung soll in der Datenbank gespeichert und als Ereignis an eine Message Queue (Nachrichtenwarteschlange) gesendet werden. Zwei getrennte Schritte können halb scheitern:

  • Datenbank geschrieben, Nachricht nicht gesendet (Absturz dazwischen): niemand erfährt von der Bestellung.
  • Nachricht gesendet, Speichern schlägt fehl: das Ereignis beschreibt etwas, das es nicht gibt.

Eine echte gemeinsame Transaktion über beide Systeme ist teuer (Verteilte Transaktionen: Zwei-Phasen-Commit und Saga).

Die Lösung

  1. In einer Transaktion: Zeile in orders und Zeile in outbox (Ereignis als Text/JSON) schreiben.
  2. Ein Relay liest neue Outbox-Zeilen (Abfrage in kurzen Abständen oder über das Änderungsprotokoll der Datenbank) und sendet sie.
  3. Nach erfolgreichem Senden wird die Zeile als erledigt markiert oder gelöscht.

Wichtig: Doppelte Nachrichten

Stürzt das Relay nach dem Senden, aber vor dem Markieren ab, geht die Nachricht ein zweites Mal raus. Das Muster garantiert also mindestens einmal. Empfänger müssen deshalb idempotent sein, zum Beispiel mit einer Ereignis-ID, die sie schon verarbeitete Nachrichten wiedererkennen lässt.

Wann sinnvoll

Immer dann, wenn eine Änderung zuverlässig ein Ereignis auslösen muss, etwa bei Microservices oder Event Sourcing und CQRS. Für eine einzelne Anwendung mit einer Datenbank reicht oft eine einfache Tabelle als Warteschlange.