VINGRADGmbH
Deutsch English

2026-10-27 · Architektur

Transactional Outbox in einem Messenger: sieben Phasen, kein veränderter Frame

Erst in die Datenbank schreiben, dann das Ereignis verschicken: Das geht gut, bis der Prozess genau dazwischen abstürzt. In einem Messenger habe ich die Nebenwirkungen deshalb in eine Outbox verlegt.

Ein Messenger mit Go, PostgreSQL, Redis und WebSocket. Nach dem Speichern einer Nachricht verschickte der HTTP-Handler selbst das Ereignis an die Sockets, stiess Push und Webhooks an und schrieb Systemnachrichten wie «X ist beigetreten». Stürzte der Prozess zwischen Commit und Versand ab, ging das Ereignis verloren: Das Gegenüber sah die Nachricht erst nach dem Neuladen, der Webhook ging nie raus. Wiederholungen gab es keine, und dieselbe Logik stand in mehreren Handlern.

Ereignis und Daten in einer Transaktion

Das Ereignis landet jetzt in einer Tabelle outbox_events, in derselben Transaktion wie die Datenänderung. Nach dem Commit laufen schnelle Abnehmer wie der Socket-Versand sofort, langsame wie Push und Webhooks in einem Worker-Pool. Ein Hintergrundlauf holt jede Sekunde Ereignisse ab, die ein abgestürzter Prozess liegen gelassen hat. Fehlgeschlagene Abnehmer werden mit exponentieller Pause wiederholt, höchstens achtmal.

Eine erneute Zustellung liest den aktuellen Zustand nach, nicht einen alten Schnappschuss. Die Zustell-ID eines Webhooks ist deterministisch, also kann der Empfänger Duplikate verwerfen. Wird eine Nachricht bearbeitet oder gelöscht, verschwindet ihr Text auch aus den gespeicherten Ereignissen.

Gefährlich sind die Ränder

Umgesetzt habe ich das in sieben Phasen, jede in einem eigenen Branch, jeder vor dem Merge von einem unabhängigen Review geprüft. Die heikelsten Stellen fand das Review, nicht die Tests: Bei einer langen Transaktion zählte die Zustellfrist ab dem Einfügen statt ab dem Commit; eine Verbindung der Transaktion wurde nach dem Commit weiterverwendet; ein Löschen konnte mit einer bereits eingeplanten Zustellung kollidieren.

Damit die Clients nichts merken, habe ich vor Beginn alle WebSocket-Frames eines Szenarios mit zwei Benutzern, Gruppen und Anrufen aufgezeichnet: 60 Schritte, 241 Frames. Jede Phase lief gegen diese Referenz, ohne eine einzige Formatänderung.

Auf dem Teststand gehen Daten und Folgen nicht mehr auseinander: Entweder ist das Ereignis mit den Daten gespeichert und wird zugestellt, oder keins von beiden ist passiert. Beitritte und Anrufstarts, die bei einer Störung früher nur geloggt und verloren wurden, werden wiederholt. Eine Outbox braucht man, sobald eine Nebenwirkung den Commit überlebt.

Alle Einträge

Passt das zu Ihrem Vorhaben?

Wenn Sie jemanden suchen, der so arbeitet: Buchen Sie ein Erstgespräch oder schreiben Sie mir.

Erstgespräch buchen Schreiben