DDD & Co., Teil 8: Eventual Consistency

Die vergangene Folge hat das Entwurfsmuster CQRS vorgestellt, das die Trennung von Schreib- und Leseseite einer Anwendung beschreibt. Das verbessert nicht nur die Struktur, sondern führt auch zu einer unkomplizierten Skalierbarkeit. Der Preis dafür ist die erforderliche Synchronisation – einschließlich ihrer Nebenwirkungen.

In Pocket speichern vorlesen Druckansicht 10 Kommentare lesen
Lesezeit: 5 Min.
Von
  • Golo Roden
Inhaltsverzeichnis

Die vergangene Folge hat das Entwurfsmuster CQRS vorgestellt, das die Trennung von Schreib- und Leseseite einer Anwendung beschreibt. Das verbessert nicht nur die Struktur, sondern führt auch zu einer unkomplizierten Skalierbarkeit. Der Preis dafür ist die erforderliche Synchronisation – einschließlich ihrer Nebenwirkungen.

Die Schreib- und Leseseite einer Anwendung bereits auf architektonischer Ebene zu trennen ist in vielen Szenarien durchaus sinnvoll. Während sich die Schreibseite um die Wahrung der Konsistenz kümmert, ermöglicht die Leseseite ausgesprochen effiziente Abfragen.

Mehr Infos

Allerdings wird eine Synchronisation benötigt, um die Leseseite nach einer Änderung der Daten auf der Schreibseite nachzuziehen. Prinzipiell entspricht das Vorgehen einem Mapping fachlicher Ereignisse auf herkömmliche CRUD-Logik.

Auf dem Weg lässt sich die Schreibseite mit DDD modellieren, während die Leseseite vorberechnete Tabellen für einfache SELECT-Abfragen zur Verfügung stellt. Diese lassen sich effizient auslesen, außerdem kann die dahinter liegende Datenbank leicht skaliert werden.

Einen Aspekt darf man bei alledem allerdings nicht außer Acht lassen: Die Synchronisation benötigt eine gewisse Zeit, weshalb das Aktualisieren der Leseseite etwas später erfolgt als das der Schreibseite. Wie viel Zeit vergeht, bis beide Seiten wieder konsistent sind, hängt von vielen Faktoren ab und lässt sich nicht vorhersagen.

Werden mehrere Datenbanken für die Leseseite genutzt, gilt das Problem gleichermaßen für die einzelnen Instanzen: Das Aktualisieren der Instanzen erfolgt unter Umständen unterschiedlich schnell. Das bedeutet, dass die gleiche Abfrage zwei verschiedene Ergebnisse zurückgeben kann, je nachdem welche Instanz befragt wird.

Das Verhalten wird als gelegentlich konsistent (englisch: eventual consistent) bezeichnet und bedeutet, dass die Konsistenz nicht auf einen Schlag garantiert wird, sondern erst nach und nach eintritt. Anders als aufgrund schlechter Übersetzungen vermutet werden könnte, geht es nicht um eventuelle Konsistenz. Die Konsistenz wird tatsächlich garantiert – nur der Zeitpunkt lässt sich nicht vorhersagen.

Das wirkt auf den ersten Blick, insbesondere im Kontext von Datenbanken, wie ein völlig unbrauchbares Konzept. In Wahrheit ist das Modell jedoch viel näher an der fachlichen Wirklichkeit als klassische verteilte Transaktionen, die die Konsistenz zu einem gegebenen Zeitpunkt erzwingen.

Beispielsweise ist es üblich, in einem Restaurant zunächst zu speisen, bevor die Rechnung beglichen wird. Zwar werden hier Lebensmittel gegen Geld getauscht, eine Transaktion findet jedoch nicht statt. Verlässt man das Restaurant, ohne zu zahlen, macht das den Verzehr der Lebensmittel nicht ungeschehen.

Das Gleiche gilt, wenn man in einem Café ein Getränk wie einen Kaffee zum Mitnehmen bestellt: Üblicherweise zahlt man zunächst und wartet anschließend auf das Getränk. Verlässt man das Café vorzeitig, wurde zwar Geld gezahlt, aber kein Gegenwert geschaffen. Auch hier findet keine Transaktion statt.

Solche Situationen fallen unter die Geschäftsrisiken. Dass ein Kunde die Zeche prellt, lässt sich praktisch kaum vermeiden. Deshalb gilt es, den Schaden zu begrenzen und Risiken im Vorfeld abzuschätzen. Technisch verhindern lassen sich solche Situationen jedoch nicht. So gesehen ist die Realität nicht konsistent.

In technischen Systemen wird häufig versucht, derartige Probleme technisch zu lösen – statt sie als das zu behandeln, was sie sind: Geschäftsrisiken. Wer versucht, jegliches Geschäftsrisiko technisch zu lösen, verschwendet in den meisten Fällen sehr viel Zeit und Geld. Weitaus sinnvoller wäre es, solche Risiken fachlich zu lösen.

Ein Buchungssystem, das den Verkauf einer begrenzten Zahl an Eintrittskarten verwaltet, könnte im Zweifelsfall beispielsweise eine Überbuchung zulassen. Das ließe sich von vornherein einkalkulieren. Außerdem ist auch mit der einen oder anderen Stornierung zu rechnen, was die Überbuchungen nachrücken lässt.

Zu guter Letzt ließe sich außerdem überlegen, wie ein Kunde im Falle des Falles kulant entschädigt wird, wenn die Teilnahme trotz gekaufter Eintrittskarte tatsächlich nicht möglich ist. Da es sich hierbei um einen unwahrscheinlichen Ausnahmefall handelt, lässt sich das fachliche Risiko abschätzen und entsprechend behandeln.

Selbstverständlich gibt es Anwendungsszenarien, für die das Verhalten nicht akzeptabel ist. Dazu zählen beispielsweise alle Anwendungen, die direkten Einfluss auf Gesundheit oder Leben haben. Sie benötigen in der Regel eine echte transaktionsbasierte Konsistenz. Solche Anwendungen sind aber die Ausnahme.

Wie so oft gilt es, das richtige Werkzeug für die gegebene Aufgabe zu wählen. In weitaus mehr Fällen, als man zunächst vermutet, ist der Einsatz der gelegentlichen Konsistenz eine durchaus gangbare Option. Sie erfordert aber ein gewisses Umdenken – auf fachlicher, vor allem aber auch auf technischer Seite.

tl;dr: Gelegentliche Konsistenz garantiert die Integrität, nicht aber einen bestimmten Zeitpunkt. Trotzdem lassen sich mit dem Ansatz viele Anwendungen entwickeln. Das erfordert allerdings die Bereitschaft, fachliche Probleme fachlich und nicht technisch zu lösen. ()