Idempotency
Mehrfache Verarbeitung ohne mehrfache Wirkung
Die Idee und ihr Nutzen
Das Schadensystem einer Versicherung beauftragt ihren Zahlungsdienst, für Schaden 4711 an den Kunden 1.200 Euro auszuzahlen. Der Zahlungsdienst bucht die Zahlung, doch seine Antwort geht im Netz verloren. Das Schadensystem weiß nicht, ob die Zahlung erfolgt ist, und schickt den Auftrag erneut. Damit der Kunde nicht 2.400 Euro erhält, trägt jeder Auftrag eine eindeutige Auftragskennung, etwa zahl-4711-01. An der Kennung erkennt der Zahlungsdienst den zweiten Auftrag und bucht nicht erneut. Ein Vorgang, dessen Wiederholung nichts zusätzlich bewirkt, heißt idempotent, das Prinzip Idempotency, auf Deutsch Idempotenz.
Wie funktioniert es?
Das Schadensystem schickt dem Zahlungsdienst den Auftrag „Zahle 1.200 €“ für Schaden 4711 mit der Kennung zahl-4711-01. Der Zahlungsdienst speichert seine Buchungen in einer eigenen Zahlungsdatenbank. Der Zahlungsdienst fragt die Zahlungsdatenbank, ob die Kennung bekannt ist. Dabei trägt der Zahlungsdienst die Kennung so ein, dass zwei gleichzeitig eintreffende Aufträge mit derselben Kennung nicht beide als neu gelten. Dafür sorgt eine Datenbankregel, die jede Kennung nur einmal zulässt, oder eine Sperre, die den zweiten Auftrag warten lässt, bis der erste eingetragen ist. Die Zahlungsdatenbank meldet, dass die Kennung neu ist. Der Zahlungsdienst bucht die Zahlung und speichert Buchung, Kennung und Ergebnis in derselben Datenbanktransaktion. In einer Datenbanktransaktion wird entweder alles zusammen gespeichert oder gar nichts. Die Antwort „gebucht“ des Zahlungsdienstes geht durch eine Netzstörung verloren. Nach zehn Sekunden ohne Antwort wiederholt das Schadensystem denselben Auftrag mit derselben Kennung. Der Zahlungsdienst antwortet sonst in weniger als einer Sekunde; nach zehn Sekunden ist mit seiner Antwort nicht mehr zu rechnen. Der Zahlungsdienst fragt die Zahlungsdatenbank erneut, und die Zahlungsdatenbank meldet „bekannt: gebucht“. Der Zahlungsdienst bucht nicht noch einmal, sondern schickt das gespeicherte Ergebnis „gebucht“ zurück. Das Schadensystem vermerkt die Zahlung als erledigt, und der Kunde erhält genau 1.200 Euro.
Manche Vorgänge sind von Natur aus idempotent: „Setze die Adresse auf Hauptstraße 5“ kann man beliebig oft ausführen, die Adresse bleibt Hauptstraße 5. „Zahle 1.200 Euro aus“ dagegen führt bei jeder Ausführung zu einer neuen Zahlung. Solche Vorgänge werden mit der Auftragskennung abgesichert, die auch Idempotenzschlüssel heißt. Das Schadensystem ist der Auftraggeber. Es erzeugt für jeden fachlichen Vorgang eine eigene Kennung und schickt sie bei jeder Wiederholung unverändert mit. Der Zahlungsdienst ist der Empfänger. Er prüft jede eingehende Kennung und merkt sich jede Kennung zusammen mit dem Ergebnis in seiner Zahlungsdatenbank. Der Zahlungsdienst bewahrt die Kennungen mindestens so lange auf, wie das Schadensystem Wiederholungen schicken darf, hier 30 Tage. Die 30 Tage decken auch Aufträge ab, die das Schadensystem nach einem Ausfall von mehreren Tagen erneut schickt. Diese Frist vereinbaren die Teams von Schadensystem und Zahlungsdienst miteinander.
Kommt ein Auftrag mit einer bekannten Kennung, aber mit anderem Inhalt, liegt ein Fehler beim Schadensystem vor. Ein solcher Auftrag wäre zahl-4711-01 mit 1.500 statt 1.200 Euro. Der Zahlungsdienst weist den Auftrag ab, und der Fehler fällt sofort auf. Die Prüfung der Kennung verhindert nur, dass der Zahlungsdienst selbst doppelt bucht. Führt ein externer Zahlungsanbieter wie eine Bank die Überweisung aus, muss auch der Zahlungsanbieter Wiederholungen erkennen. Dafür kann der Zahlungsanbieter zum Beispiel ebenfalls eine Auftragskennung annehmen. Sonst muss der Zahlungsdienst vor einer Wiederholung beim Zahlungsanbieter nachfragen können, ob bereits gezahlt wurde.
Idempotenz: eine wiederholte Zahlung wird nicht doppelt gebucht
Wann ist es sinnvoll und wann nicht?
Eine Auftragskennung lohnt sich
- Ein Vorgang hat Folgen außerhalb des eigenen Programms und kann wiederholt werden. Beispiele sind eine Zahlung, das Anlegen eines Vertrags oder der Versand eines Briefs. Ohne Absicherung zahlt die Versicherung einen Schaden zweimal aus, legt einen Vertrag doppelt an oder verschickt zwei Mahnungen. Solche Fehler fallen oft erst Wochen später auf. Ist das Geld einmal beim Kunden, muss die Versicherung die doppelte Auszahlung vom Kunden zurückfordern, obwohl der Kunde den Fehler nicht verursacht hat.
- Der Auftraggeber kann nicht sicher feststellen, ob der erste Versuch angekommen ist. Jede Nachricht im Netz kann verloren gehen, verspätet ankommen oder doppelt ankommen. Wiederholt das Schadensystem einen verlorenen Auftrag nicht, erhält der Kunde sein Geld nicht. Mit der Auftragskennung darf das Schadensystem nach zehn Sekunden ohne Antwort selbsttätig wiederholen, statt die Zahlung von Hand klären zu lassen.
- Ein Programm empfängt Nachrichten von einer Nachrichtenplattform, also einem Programm, das Nachrichten von einem Programm annimmt und an andere Programme zustellt. Stellt die Nachrichtenplattform nach dem Grundsatz „mindestens einmal“ zu, schickt sie eine Nachricht noch einmal, wenn nach einer Störung die Bestätigung des Empfängers ausbleibt. Doppelte Zustellungen gehören dann zum normalen Betrieb. Ohne Prüfung der Kennung führt der Empfänger jede doppelt zugestellte Nachricht ein zweites Mal aus.
- Kunden oder Partner rufen Programme der Versicherung über das Internet auf und sollen bei Netzproblemen gefahrlos wiederholen können. Ohne Auftragskennung führt schon ein doppelter Klick auf „Absenden“ zu zwei Buchungen. Mit Auftragskennung schickt das aufrufende Programm beide Male dieselbe Kennung, und das Programm der Versicherung bucht nur einmal.
Eine Auftragskennung ist nicht das Richtige
- Ein Aufruf fragt nur Daten ab und verändert nichts. Das Schadensystem fragt zum Beispiel beim Zahlungsdienst ab, welche Zahlungen für Schaden 4711 gebucht sind. Dann wiederholt das Schadensystem die Abfrage nach einer Netzstörung ohne Kennung, denn eine zweite Abfrage bewirkt nichts zusätzlich. Der Zahlungsdienst muss für Abfragen keine Kennungen speichern und prüfen.
- Ein Vorgang ist von Natur aus idempotent. Ein Beispiel ist „Setze die Adresse auf Hauptstraße 5“. Dann wiederholt der Auftraggeber einen solchen Auftrag ohne Kennung, denn nach der zweiten Ausführung steht dieselbe Adresse da wie nach der ersten. Der Empfänger muss keine Kennungen speichern und prüfen.
- Ein Vorgang lässt sich als Zustand statt als Handlung formulieren. Dann schickt das Schadensystem statt „Zahle 1.200 Euro aus“ den Auftrag „Schaden 4711 ist mit 1.200 Euro ausgezahlt“. Der Zahlungsdienst vergleicht den Auftrag mit den Zahlungen, die für Schaden 4711 schon gebucht sind, und zahlt nur, was noch fehlt. Eine Wiederholung bewirkt so nichts zusätzlich, und der Zahlungsdienst muss keine Auftragskennungen speichern und nach 30 Tagen aufräumen.
- Der Empfänger kann keine Kennungen annehmen. Das ist zum Beispiel bei einem externen Zahlungsanbieter so, der für den Zahlungsdienst die Überweisungen ausführt. Der Zahlungsanbieter kann eine Wiederholung dann nicht an der Kennung erkennen. Kann der Zahlungsdienst beim Zahlungsanbieter zuverlässig abfragen, welche Überweisungen schon ausgeführt sind, fragt der Zahlungsdienst vor jeder Wiederholung dort nach. Der Zahlungsdienst schickt den Auftrag nur dann erneut, wenn der Zahlungsanbieter die Überweisung noch nicht ausgeführt hat.
Nachteile und typische Fehler
Die Kennungen müssen gespeichert und nach Ablauf der Frist aufgeräumt werden. Jeder Auftrag braucht dadurch einen zusätzlichen Zugriff auf die Zahlungsdatenbank, und die Tabelle der Kennungen wächst mit der Zahl der Aufträge. Ändert das Schadensystem, wie lange es Wiederholungen schickt, muss der Zahlungsdienst seine Aufbewahrung anpassen. Jeder weitere Empfänger, der Geld bewegt oder Briefe verschickt, braucht eine eigene Prüfung der Auftragskennung mit einer eigenen Tabelle der Kennungen.
Typische Fehler und woran man sie erkennt:
- Gleichzeitige Wiederholung: Der erste Auftrag des Schadensystems wird im Netz aufgehalten und trifft erst nach zehn Sekunden beim Zahlungsdienst ein, gleichzeitig mit der Wiederholung. Der Zahlungsdienst fragt für beide Aufträge zugleich die Zahlungsdatenbank. Weder eine Datenbankregel noch eine Sperre verhindert den zweiten Eintrag. Die Zahlungsdatenbank meldet deshalb beide Male „neu“, und der Zahlungsdienst bucht zweimal. Erkennbar ist das an zwei Buchungen mit derselben Kennung, meist nur unter hoher Last.
- Wechselnde Kennung: Der Auftraggeber erzeugt bei jeder Wiederholung eine neue Kennung. Das Verfahren nützt dann nichts, weil jeder Versuch als neuer Vorgang gilt. Erkennbar ist das an mehreren Zahlungen für denselben Schaden im Abstand von etwa zehn Sekunden und mit verschiedenen Kennungen.
- Zu kurze Aufbewahrung: Die Aufbewahrungsfrist des Zahlungsdienstes ist kürzer als die Zeit, in der das Schadensystem Wiederholungen schicken darf. Der Zahlungsdienst hat die Kennung eines Auftrags deshalb schon gelöscht. Nach einem mehrtägigen Ausfall schickt das Schadensystem genau diesen Auftrag erneut. Der Zahlungsdienst behandelt den Auftrag als neu und bucht ein zweites Mal. Erkennbar ist das an doppelten Zahlungen, deren zweiter Auftrag kurz nach Ablauf der Frist eintraf.
- Getrennt gespeichert: Der Zahlungsdienst speichert Buchung und Kennung nicht in derselben Datenbanktransaktion. Stürzt der Zahlungsdienst zwischen beiden Schritten ab, sind zwei Fehler möglich. Gibt es eine Buchung ohne Kennung, bucht die Wiederholung erneut. Gibt es eine Kennung ohne Buchung, meldet die Wiederholung „gebucht“, obwohl nichts gebucht wurde. Erkennbar ist das beim Abgleich von Buchungen und gespeicherten Kennungen.
- Ungeschützter Zahlungsanbieter: Nur der eigene Zahlungsdienst prüft die Kennung, der externe Zahlungsanbieter aber nicht. Wiederholt der Zahlungsdienst seinen Auftrag an den Zahlungsanbieter, überweist der Zahlungsanbieter doppelt. Erkennbar ist das an Kontoauszügen mit zwei Überweisungen, denen nur eine Buchung im Zahlungsdienst gegenübersteht.
Weiterführend
Verwandte Ansätze
- Delivery Semantics: unterscheidet, was Nachrichtenplattformen über die Zustellung zusagen. Bei der Zusage „mindestens einmal“ sind doppelte Nachrichten normal und müssen idempotent verarbeitet werden.
- Inbox Pattern: speichert speziell eingehende Nachrichten mit ihrer Kennung in einer eigenen Tabelle und erkennt so doppelte Nachrichten.
- Retry with Backoff: wiederholt gescheiterte Aufrufe mit wachsenden Pausen. Solche Wiederholungen sind nur sicher, wenn der Empfänger idempotent arbeitet.
- Compensating Transaction: macht bereits erledigte Schritte durch Gegenaktionen rückgängig. Auch diese Gegenaktionen müssen gegen doppelte Ausführung abgesichert sein.
Werkzeuge und Umsetzungen
Idempotenz baut man meist selbst, oft mit einer Datenbankregel, die jede Kennung nur einmal zulässt. Diese Bausteine helfen:
- Apache Camel: enthält den Baustein Idempotent Consumer, der bereits verarbeitete Nachrichten anhand einer Kennung überspringt.
- Spring Integration: bietet einen Filter, der doppelte Nachrichten anhand eines Speichers bereits gesehener Kennungen erkennt.
- Powertools for AWS Lambda: Open-Source-Bibliothek mit einem Baustein für Idempotenz, gedacht für Funktionen beim Cloud-Anbieter AWS.
- PostgreSQL: Datenbank, in der ein eindeutiger Index auf der Kennungsspalte zuverlässig verhindert, dass derselbe Vorgang zweimal gespeichert wird.
Quellen
- Malcolm Featonby, Amazon Web Services: Making retries safe with idempotent APIs: beschreibt die vom Aufrufer vergebene Auftragskennung, das Abweisen bei gleicher Kennung mit anderem Inhalt, die begrenzte Aufbewahrung und das gemeinsame Speichern von Kennung und Änderung.
- Chris Richardson: Pattern: Idempotent Consumer: erklärt, wie ein Empfänger die Kennungen verarbeiteter Nachrichten in derselben Datenbanktransaktion speichert und doppelte Zustellungen verwirft.