Czytałem ostatnio o pozyskiwaniu wydarzeń i bardzo podobają mi się pomysły, ale utknąłem z następującym problemem.
Załóżmy, że masz N równoczesnych procesów, które odbierają polecenia (np. Serwery sieciowe), generują w rezultacie zdarzenia i przechowują je w scentralizowanym sklepie. Załóżmy również, że cały przejściowy stan aplikacji jest utrzymywany w pamięci poszczególnych procesów poprzez sekwencyjne stosowanie zdarzeń ze sklepu.
Załóżmy, że mamy następującą regułę biznesową: każdy odrębny użytkownik musi mieć unikalną nazwę użytkownika.
Jeśli dwa procesy otrzymają polecenie rejestracji użytkownika dla tej samej nazwy użytkownika X, oba sprawdzają, czy X nie znajduje się na ich liście nazw użytkowników, reguła sprawdza się dla obu procesów i oba przechowują w sklepie zdarzenie „nowy użytkownik o nazwie X” .
Wprowadziliśmy teraz niespójny stan globalny, ponieważ naruszono regułę biznesową (dwóch różnych użytkowników ma tę samą nazwę użytkownika).
W tradycyjnym systemie typu N serwera <-> 1 RDBMS baza danych jest używana jako centralny punkt synchronizacji, który pomaga zapobiegać takim niespójnościom.
Moje pytanie brzmi: w jaki sposób systemy oparte na zdarzeniach zazwyczaj podchodzą do tego problemu? Czy po prostu przetwarzają wszystkie polecenia sekwencyjnie (np. Ograniczają ilość procesów, które można zapisać w sklepie do 1)?
źródło
Odpowiedzi:
W systemach pochodzących ze zdarzeń „magazyn zdarzeń” pełni tę samą rolę. W przypadku obiektu pozyskiwanego ze zdarzenia zapis jest dołączeniem nowych zdarzeń do określonej wersji strumienia zdarzeń. Tak więc, podobnie jak w przypadku programowania współbieżnego, można uzyskać blokadę tej historii podczas przetwarzania polecenia. W systemach opartych na zdarzeniach bardziej optymistyczne jest podejście - ładowanie poprzedniej historii, obliczanie nowej historii, a następnie porównywanie i zamiana. Jeśli do tego strumienia zapisano także inne polecenie, porównanie i zamiana nie powiodą się. Stamtąd albo ponownie uruchomisz polecenie, albo je porzucisz, a może nawet połączysz swoje wyniki z historią.
Rywalizacja staje się poważnym problemem, jeśli wszystkie N serwerów z ich komendami M próbuje zapisać w jednym strumieniu. Zwykłą odpowiedzią jest przydzielenie historii każdemu podmiotowi pochodzącemu ze zdarzenia w twoim modelu. Użytkownik (Bob) miałby więc inną historię niż Użytkownik (Alice) i zapisy do jednego nie blokują zapisu do drugiego.
Greg Young o sprawdzaniu poprawności zestawu
Czy istnieje elegancki sposób sprawdzania unikalnych ograniczeń atrybutów obiektów domeny bez przenoszenia logiki biznesowej do warstwy usług?
Krótka odpowiedź, w wielu przypadkach, głębsze zbadanie tego wymogu ujawnia, że (a) jest to słabo rozumiany serwer zastępczy dla innych wymagań lub (b) że naruszenia „reguły” są dopuszczalne, jeśli można je wykryć (raport wyjątków) , zostały złagodzone w określonym przedziale czasu lub mają niską częstotliwość (np .: klienci mogą sprawdzić, czy nazwa jest dostępna przed wysłaniem polecenia, aby z niej skorzystać).
W niektórych przypadkach, gdy Twój magazyn zdarzeń jest dobry w sprawdzaniu poprawności zestawu (np. Relacyjna baza danych), wówczas implementujesz wymaganie, pisząc do tabeli „unikalnych nazw” w tej samej transakcji, która utrzymuje zdarzenia.
W niektórych przypadkach można wymusić wymóg tylko poprzez opublikowanie wszystkich nazw użytkowników w tym samym strumieniu (co pozwala na ocenę zestawu nazw w pamięci, jako część modelu domeny). - W takim przypadku dwa procesy zaktualizują próbę aktualizacji historii strumienia, ale jedna z operacji porównania i zamiany zakończy się niepowodzeniem, a ponowna próba wykonania polecenia będzie w stanie wykryć konflikt.
źródło
Wygląda na to, że można wdrożyć proces biznesowy (
saga
w kontekścieDomain Driven Design
) rejestracji użytkownika, w którym użytkownik jest traktowany jak użytkownikCRDT
.Zasoby
https://doc.akka.io/docs/akka/current/distribut-data.html http://archive.is/t0QIx
„CRDT z danymi rozproszonymi Akka” https://www.slideshare.net/markusjura/crdts-with-akka-distribution-data, aby dowiedzieć się o
CmRDT
s - CRDT oparte na operacjiCvRDT
s - CRTD oparte na staniePrzykłady kodu w Scali https://github.com/akka/akka-samples/tree/master/akka-sample-distribution-data-scala . Być może najbardziej odpowiedni jest „koszyk”.
źródło