Ich habe in letzter Zeit über Event-Sourcing gelesen und mag die Ideen, die dahinter stecken, aber ich habe folgendes Problem.
Angenommen, Sie haben N gleichzeitige Prozesse, die Befehle empfangen (z. B. Webserver), als Ergebnis Ereignisse generieren und diese in einem zentralen Speicher speichern. Nehmen wir außerdem an, dass alle vorübergehenden Anwendungszustände im Speicher der einzelnen Prozesse beibehalten werden, indem Ereignisse aus dem Speicher nacheinander angewendet werden.
Angenommen, wir haben die folgende Geschäftsregel: Jeder einzelne Benutzer muss einen eindeutigen Benutzernamen haben.
Wenn zwei Prozesse einen Benutzerregistrierungsbefehl für denselben Benutzernamen X erhalten, überprüfen sie beide, dass X nicht in ihrer Liste der Benutzernamen enthalten ist. Die Regel wird für beide Prozesse validiert und sie speichern beide ein Ereignis "Neuer Benutzer mit Benutzername X" im Speicher .
Wir sind jetzt in einen inkonsistenten globalen Status eingetreten, da die Geschäftsregel verletzt wurde (es gibt zwei unterschiedliche Benutzer mit demselben Benutzernamen).
In einem herkömmlichen RDBMS-System mit N Servern <-> 1 wird die Datenbank als zentraler Synchronisationspunkt verwendet, um solche Inkonsistenzen zu vermeiden.
Meine Frage lautet: Wie gehen Event-Sourcing-Systeme normalerweise mit diesem Problem um? Verarbeiten sie einfach alle Befehle der Reihe nach (z. B. begrenzen sie die Anzahl der Prozesse, die in den Speicher geschrieben werden können, auf 1)?
quelle
Antworten:
In Ereignissystemen hat der "Ereignisspeicher" dieselbe Funktion. Bei einem Ereignisquellenobjekt besteht Ihr Schreibvorgang aus dem Anhängen Ihrer neuen Ereignisse an eine bestimmte Version des Ereignisdatenstroms. Genau wie bei der gleichzeitigen Programmierung können Sie bei der Verarbeitung des Befehls eine Sperre für diesen Verlauf festlegen. Bei Ereignissystemen ist es üblicher, optimistischer vorzugehen: Laden Sie den vorherigen Verlauf, berechnen Sie den neuen Verlauf und tauschen Sie ihn aus. Wenn ein anderer Befehl ebenfalls in diesen Stream geschrieben hat, schlägt das Vergleichen und Austauschen fehl. Von dort aus führen Sie entweder Ihren Befehl erneut aus oder geben Ihren Befehl auf oder führen Ihre Ergebnisse sogar in den Verlauf ein.
Konflikte werden zu einem Hauptproblem, wenn alle N Server mit ihren M Befehlen versuchen, in einen einzigen Stream zu schreiben. Die übliche Antwort besteht darin, jedem Ereignis in Ihrem Modell einen Verlauf zuzuweisen. Benutzer (Bob) hat also einen anderen Verlauf als Benutzer (Alice) und schreibt in den einen, blockiert aber nicht die Schreibvorgänge in den anderen.
Greg Young bei der Set-Validierung
Gibt es eine elegante Möglichkeit, eindeutige Einschränkungen für Domänenobjektattribute zu überprüfen, ohne die Geschäftslogik in die Serviceschicht zu verschieben?
Eine kurze Antwort, die in vielen Fällen eingehender untersucht wird, zeigt, dass entweder (a) es sich um einen schlecht verstandenen Proxy für eine andere Anforderung handelt oder (b) Verstöße gegen die "Regel" zulässig sind, wenn sie festgestellt werden können (Ausnahmebericht). , innerhalb eines bestimmten Zeitfensters abgeschwächt oder mit geringer Häufigkeit (z. B .: Clients können prüfen, ob ein Name verfügbar ist, bevor sie einen Befehl zur Verwendung senden).
In einigen Fällen, in denen Ihr Ereignisspeicher eine gute Mengenvalidierung aufweist (z. B. eine relationale Datenbank), implementieren Sie die Anforderung, indem Sie in dieselbe Transaktion, in der die Ereignisse bestehen, in eine Tabelle mit eindeutigen Namen schreiben.
In einigen Fällen können Sie die Anforderung nur erzwingen, indem Sie alle Benutzernamen im selben Stream veröffentlichen (wodurch Sie den Satz von Namen im Speicher als Teil Ihres Domänenmodells auswerten können). - In diesem Fall aktualisieren zwei Prozesse den Aktualisierungsversuch "des" Stream-Verlaufs, aber eine der Compare-and-Swap-Operationen schlägt fehl, und die Wiederholung dieses Befehls kann den Konflikt erkennen.
quelle
Klingt so, als ob Sie einen Geschäftsprozess (
saga
im Kontext vonDomain Driven Design
) für die Benutzerregistrierung implementieren könnten, in dem der Benutzer wie ein Benutzer behandelt wirdCRDT
.Ressourcen
https://doc.akka.io/docs/akka/current/distributed-data.html http://archive.is/t0QIx
"CRDTs mit verteilten Akka-Daten" https://www.slideshare.net/markusjura/crdts-with-akka-distributed-data , um mehr zu erfahren
CmRDT
s - Operationsbasierte CRDTsCvRDT
s - state basierte CRTDsCodebeispiele in Scala https://github.com/akka/akka-samples/tree/master/akka-sample-distributed-data-scala . Vielleicht ist "Einkaufswagen" am besten geeignet.
quelle