Inbox Pattern
Eingehende Nachrichten erst speichern, dann verarbeiten
Die Idee und ihr Nutzen
Der Regulierungsdienst einer Versicherung, das Programm für die Schadenbearbeitung, erhält die Nachricht „Schaden 4711 gemeldet“ über eine Nachrichtenplattform, ein Programm, das Nachrichten von einem Programm annimmt und an andere Programme zustellt. Daraufhin legt der Regulierungsdienst einen Fall zur Bearbeitung an. Kurz bevor der Regulierungsdienst der Nachrichtenplattform den Empfang bestätigen kann, stürzt er ab. Nach dem Neustart stellt die Nachrichtenplattform dem Regulierungsdienst dieselbe Nachricht noch einmal zu. Ohne Vorkehrung legt der Regulierungsdienst denselben Fall ein zweites Mal an. Deshalb speichert er die eindeutige Kennung jeder Nachricht, etwa n-9921, in einer eigenen Tabelle, der Inbox-Tabelle, und zwar nur zusammen mit dem neuen Fall. Kommt die Nachricht erneut, findet er die Kennung und legt keinen zweiten Fall an. Dieses Vorgehen heißt Inbox Pattern.
Wie funktioniert es?
Die Nachrichtenplattform stellt dem Regulierungsdienst die Nachricht „Schaden 4711 gemeldet“ mit der Kennung n-9921 zu. In einer einzigen Datenbanktransaktion trägt der Regulierungsdienst n-9921 in die Inbox-Tabelle ein und legt den Regulierungsfall 4711 an. In einer Datenbanktransaktion werden mehrere Änderungen entweder alle gemeinsam gespeichert oder bei einem Fehler alle verworfen. Die Datenbank des Regulierungsdienstes meldet, dass Kennung und Fall gemeinsam gespeichert sind. Genau jetzt, noch vor der Bestätigung an die Nachrichtenplattform, stürzt der Regulierungsdienst wegen eines Fehlers im Arbeitsspeicher ab. Weil die Bestätigung fehlt, stellt die Nachrichtenplattform nach dem Neustart dieselbe Nachricht mit derselben Kennung n-9921 noch einmal zu. Der Regulierungsdienst versucht wieder, n-9921 in die Inbox-Tabelle einzutragen. Die Datenbank lehnt den Eintrag ab, weil die Kennung schon vorhanden ist. Der Regulierungsdienst erkennt daran, dass der Fall bereits angelegt ist, legt keinen zweiten an und bestätigt die Nachricht nur. Für Schaden 4711 gibt es genau einen Regulierungsfall.
Die Nachrichtenplattform stellt jede Nachricht so lange erneut zu, bis der Empfänger sie bestätigt. Diese Zusage heißt „mindestens einmal“. Sie verhindert, dass Nachrichten verloren gehen, macht doppelte Zustellungen aber zum normalen Betrieb. Jede Nachricht trägt eine eindeutige Kennung, die der Absender vergibt und die bei einer erneuten Zustellung gleich bleibt. Die Inbox-Tabelle liegt in der Datenbank des Regulierungsdienstes, neben den Regulierungsfällen. Sie enthält die Kennungen aller Nachrichten, die der Regulierungsdienst bereits verarbeitet hat. Ein eindeutiger Index, eine Regel in der Datenbank, erlaubt jede Kennung nur einmal. Die Prüfung auf Wiederholung übernimmt damit die Datenbank und nicht ein eigener Prüfcode im Regulierungsdienst. Weil Kennung und Fall immer gemeinsam gespeichert werden, gibt es nach einem Absturz nur zwei Möglichkeiten. Entweder ist beides gespeichert, dann erkennt der Regulierungsdienst die Wiederholung. Oder keines von beiden ist gespeichert, dann verarbeitet er die Nachricht ganz normal neu. Bestätigt wird die Nachricht erst, wenn die Transaktion abgeschlossen ist.
Eine zweite Bauweise trennt Empfang und Verarbeitung. Der Regulierungsdienst speichert zunächst die ganze Nachricht mit dem Zustand „offen“ in der Inbox-Tabelle und bestätigt sie sofort bei der Nachrichtenplattform. Ein eigener Programmteil holt später offene Einträge ab, verarbeitet sie und setzt sie auf „erledigt“. Dabei muss der Zustand verlässlich zwischen „offen“, „in Bearbeitung“ und „erledigt“ wechseln. Eine doppelt zugestellte Nachricht darf nicht als erledigt gelten, solange ihre erste Verarbeitung noch aussteht.
Inbox: eine doppelte Nachricht legt keinen zweiten Fall an
Wann ist es sinnvoll und wann nicht?
Das Inbox Pattern lohnt sich
- Ein Empfänger ändert bei jeder Nachricht etwas in seiner eigenen Datenbank und verträgt keine doppelte Verarbeitung. Ohne Inbox-Tabelle gäbe es für Schaden 4711 zwei Regulierungsfälle. Womöglich arbeiten dann zwei Sachbearbeiter parallel an den beiden Fällen, und der Schaden wird doppelt bezahlt.
- Der Empfänger kann mitten in der Arbeit abstürzen. Ohne Inbox-Tabelle weiß der Regulierungsdienst nach dem Neustart nicht, wie weit er beim letzten Mal gekommen war. Mit der Inbox-Tabelle zeigt der Eintrag
n-9921, dass die Nachricht schon verarbeitet ist. - Die Nachrichtenplattform soll weiter „mindestens einmal“ zustellen, damit keine Nachricht verloren geht. Ohne Inbox-Tabelle legt der Regulierungsdienst bei jeder erneuten Zustellung einen weiteren Regulierungsfall an.
- Beim Absender ist bereits das Outbox Pattern im Einsatz. Es stellt sicher, dass eine Nachricht nach dem Speichern der Daten auch tatsächlich verschickt wird. Nach einem Absturz des Absenders kann das Outbox Pattern dieselbe Nachricht aber ein zweites Mal verschicken, und ohne Inbox-Tabelle legt der Regulierungsdienst dann einen zweiten Fall an. Die Inbox-Tabelle bildet das Gegenstück beim Empfänger. Mit beiden zusammen geht auf dem Weg von der Datenbank des Absenders in die Datenbank des Empfängers keine Nachricht verloren, und der Empfänger verarbeitet keine Nachricht doppelt, auch wenn Absender oder Empfänger abstürzen.
Das Inbox Pattern ist nicht das Richtige
- Die Verarbeitung bewirkt bei einer Wiederholung von sich aus nichts doppelt. Das ist zum Beispiel so, wenn der Regulierungsdienst auf die Nachricht „Schaden 4711 abgeschlossen“ hin nur den Zustand des Regulierungsfalls 4711 auf „abgeschlossen“ setzt. Dann verarbeitet der Regulierungsdienst auch eine doppelt zugestellte Nachricht einfach noch einmal, und der Zustand bleibt „abgeschlossen“. Die Inbox-Tabelle, ihr Aufräumen und der zusätzliche Schreibvorgang je Nachricht entfallen.
- Der Empfänger hat keine eigene Datenbank mit Transaktionen. Dann kann der Empfänger Kennung und Änderung nicht gemeinsam speichern. Das Team gestaltet die Verarbeitung stattdessen so, dass eine Wiederholung nichts doppelt bewirkt, etwa indem die Verarbeitung nur einen Wert auf einen festen Stand setzt.
- Die Verarbeitung besteht hauptsächlich aus Aufrufen anderer Systeme. Der Regulierungsdienst lässt auf eine Nachricht hin zum Beispiel nur eine E-Mail an den Kunden verschicken. Die Inbox-Tabelle schützt nur, was in derselben Datenbanktransaktion gespeichert wird, und die E-Mail gehört nicht dazu. Stattdessen gibt der Regulierungsdienst die Kennung der Nachricht an den E-Mail-Dienst weiter, der die E-Mails verschickt. Der E-Mail-Dienst verschickt zu jeder Kennung höchstens eine E-Mail. Das geht nur, wenn der E-Mail-Dienst solche Kennungen annimmt.
- Es gibt fachlich ohnehin höchstens einen Regulierungsfall je Schaden. Dann legt das Team einen eindeutigen Index auf die Schadennummer in der Tabelle der Regulierungsfälle an. Ein zweiter Fall für Schaden 4711 scheitert dann direkt beim Anlegen, und die Inbox-Tabelle mit ihrem Aufräumen entfällt. Das gelingt nur, solange jede Nachricht genau eine solche eindeutige fachliche Nummer hat.
- Die gesamte Verarbeitung bleibt innerhalb der Nachrichtenplattform: Ein Programm liest Nachrichten aus der Nachrichtenplattform und schreibt seine Ergebnisse nur wieder in die Nachrichtenplattform. Es gibt dann keine eigene Datenbank, in der eine Inbox-Tabelle in derselben Transaktion wie das Ergebnis gespeichert werden könnte. Stattdessen nutzt das Team die Zusage „genau einmal“, die manche Nachrichtenplattformen für diesen Fall anbieten, und braucht keine Inbox-Tabelle. Delivery Semantics erklärt diese Zusage und warum Nachrichtenplattformen Nachrichten doppelt zustellen können.
Nachteile und typische Fehler
Die Inbox-Tabelle wächst mit jeder Nachricht und muss regelmäßig aufgeräumt werden. Einträge dürfen aber erst gelöscht werden, wenn keine Wiederholung dieser Nachricht mehr zu erwarten ist. Wie lange das dauert, hängt davon ab, wie lange die Nachrichtenplattform unbestätigte Nachrichten aufbewahrt und erneut zustellt. Jede Nachricht verursacht einen zusätzlichen Schreibvorgang in der Datenbank. Der Regulierungsdienst ist darauf angewiesen, dass der Absender jede Nachricht mit einer eindeutigen Kennung versieht und die Kennung bei einer Wiederholung nicht ändert. Die Inbox-Tabelle schützt nur, was in derselben Datenbanktransaktion gespeichert wird. Eine E-Mail an den Kunden braucht zum Beispiel eine eigene Vorkehrung, damit der Regulierungsdienst sie nicht doppelt verschickt. Die zweite Bauweise braucht zusätzlich einen eigenen Programmteil, der offene Einträge abholt und ihren Zustand von „offen“ über „in Bearbeitung“ auf „erledigt“ setzt.
Typische Fehler und woran man sie erkennt:
- Getrennte Transaktionen: Der Regulierungsdienst trägt die Kennung in einer eigenen Transaktion ein und legt den Fall in einer zweiten an. Der Regulierungsdienst stürzt zwischen beiden Transaktionen ab. In der Inbox-Tabelle steht dann die Kennung, aber der Fall fehlt. Bei der nächsten Zustellung lehnt die Datenbank den Eintrag der Kennung ab, und der Regulierungsdienst verwirft die Nachricht als Wiederholung. Erkennbar ist das an Kennungen in der Inbox-Tabelle, zu denen kein Regulierungsfall existiert.
- Zu frühes Aufräumen: Das Team lässt Einträge der Inbox-Tabelle löschen, während die Nachrichtenplattform die zugehörige Nachricht noch erneut zustellen kann. Stellt die Nachrichtenplattform eine solche Nachricht danach noch einmal zu, findet die Datenbank die Kennung nicht mehr, und der Regulierungsdienst legt einen zweiten Fall an. Erkennbar ist das an doppelten Regulierungsfällen, die kurz nach dem Aufräumen der Inbox-Tabelle entstehen.
- Neue Kennung bei Wiederholung: Der Absender erhält von der Nachrichtenplattform keine Bestätigung für seine Nachricht und schickt die Nachricht erneut, aber mit einer neuen Kennung. Die Nachrichtenplattform stellt dem Regulierungsdienst beide Nachrichten zu. Die Datenbank nimmt beide Kennungen an, und der Regulierungsdienst legt zwei Fälle an. Erkennbar ist das an zwei Nachrichten mit gleichem Inhalt und verschiedenen Kennungen.
- Doppelte Wirkung außerhalb der Datenbank: Der Regulierungsdienst schickt beim Anlegen des Falls eine E-Mail an den Kunden, bevor die Transaktion abgeschlossen ist. Dann stürzt der Regulierungsdienst ab, und die Datenbank verwirft Kennung und Fall. Bei der zweiten Zustellung legt der Regulierungsdienst den Fall neu an und schickt die E-Mail ein zweites Mal. Erkennbar ist das an Kundenbeschwerden über doppelte Benachrichtigungen, obwohl nur ein Fall existiert.
- Doppelt als erledigt: Bei der zweiten Bauweise setzt der eigene Programmteil einen offenen Eintrag auf „in Bearbeitung“ und beginnt die Verarbeitung. Die Bestätigung des Regulierungsdienstes an die Nachrichtenplattform ist verloren gegangen, und die Nachrichtenplattform stellt dieselbe Nachricht noch einmal zu. Der Regulierungsdienst findet die Kennung in der Inbox-Tabelle und setzt den Eintrag auf „erledigt“, obwohl die erste Verarbeitung noch läuft. Scheitert die erste Verarbeitung danach, holt niemand den Eintrag noch einmal ab. Erkennbar ist das an Einträgen im Zustand „erledigt“ ohne zugehörigen Regulierungsfall.
Weiterführend
Verwandte Ansätze
- Idempotency: beschreibt das Ziel, dass eine Wiederholung nichts zusätzlich bewirkt. Die Inbox-Tabelle ist eine Möglichkeit, dieses Ziel zu erreichen.
- Dead Letter Queue: eine Ablage für Nachrichten, deren Verarbeitung dauerhaft scheitert, etwa weil ihr Inhalt fehlerhaft ist.
Werkzeuge und Umsetzungen
- Wolverine: Bibliothek für die Plattform .NET, die eine Inbox- und eine Outbox-Tabelle eingebaut hat.
- Eventuate Tram: Java-Rahmenwerk, das doppelte Nachrichten über eine Tabelle bereits verarbeiteter Kennungen erkennt.
- Apache Camel: Sein Baustein Idempotent Consumer arbeitet mit einem Speicher in der Datenbank nach demselben Prinzip.
Quellen
- Chris Richardson: Pattern: Idempotent Consumer: beschreibt, wie ein Empfänger die Kennungen verarbeiteter Nachrichten in derselben Datenbanktransaktion wie die fachliche Änderung speichert und doppelte Nachrichten am gescheiterten Eintrag erkennt.
- Gregor Hohpe, Bobby Woolf: Enterprise Integration Patterns, Idempotent Receiver: beschreibt, warum ein Empfänger dieselbe Nachricht mehrfach erhalten kann, und nennt das Aussortieren von Wiederholungen als eine von zwei Möglichkeiten, einen Empfänger zu bauen, der Wiederholungen verträgt.