Event Sourcing w Javie

0

Witajcie,

Chciałbym stworzyć pewien system w Javie oparty na mikrousługach oraz DDD. W związku z tym mam kilka pytań dotyczących Event Sourcingu:

  1. Jakiej technologii najlepiej użyć do Event Sourcingu? Które z dostępnych rozwiązań jest waszym zdaniem najlepsze i jakie w ogóle są?

  2. Które jest najbardziej popularne w systemach napisanych w Javie? Rozwiązanie nie musi być przeznaczone tylko dla aplikacji pisanych w Javie. Bardziej chodzi mi o to z jaką technologią Javowcy się najczęściej spotykają podczas prac z mikrousługami i Event Sourcingiem.

  3. Czy znacie rozwiązania dzięki którym można w Event Storze umieścić zdarzenia w taki sposób aby ich kolejność podczas odtwarzania stanu agregatu ze zdarzeń była inna niż kolejność faktycznego umieszczenia zdarzeń w Event Storze? Chodzi mi o to, że chciałbym mieć aplikacje serwerową i aplikacje klienckie, które z założenia byłyby zdolne do pracy w trybie offline. W sytuacji gdyby jeden użytkownik korzystający z aplikacji offline zmienił dane pewnego dnia powiedzmy o godzinie 18:00 a drugi (korzystający z aplikacji online) zmieniłby te same dane o godzinie 19:00 to podczas synchronizacji o godzinie 20:00 (dane są synchronizowane przez aplikacje działającą wcześniej offline podczas przejścia w stan online) chciałbym mieć zdarzenia poukładane chronologicznie (użytkownik pracujący offline dokonał zmiany wcześniej niż użytkownik pracujący online ale to ten, który pracował online miał dostęp do serwera i z punktu widzenia serwera dokonał zmian wcześniej).

Pytanie trzecie jest bardziej opcjonalne: podczas odpowiadania na ten post nie należy sugerować rozwiązań pod trzecie kryterium.

1

Ad. 1. Chodzi Ci o broker wiadomości? Kafka lub RabbitMQ
Ad. 2. Nie rozumiem pytania
Ad. 3. Synchronizacja stanu bazy po offline to strasznie skomplikiwany problem. W przypadku ogolnym nierozwiązywalny

1

Ta opcja 3 brzmi bardzo źle, bo oznacza że można by bardzo łatwo zaburzyć spójność danych i generalnie narobić cudów w systemie, skoro pozwalasz na "modyfikowanie historii", szczególnie z poziomu jakiejś aplikacji klienckiej. Nie wiem co ten system ma robić, ale mocno bym się nad tym zastanowił.

0
KamilAdam napisał(a):

Ad. 1. Chodzi Ci o broker wiadomości? Kafka lub RabbitMQ

Chodzi mi o to aby mógł odtworzyć stan obiektów na podstawie zdarzeń, które zaszły w systemie. Jeżeli więc użytkownik:

  1. Zarejestruje się w systemie i poda swój adres e-mail. Niech to będzie adres X
  2. Później zmieni swój adres e-mail z X na Y
  3. Później zmieni swój adres e-mail z Y na Z

To ja:

  1. Zapiszę gdzieś informację o tym, że doszło do zarejestrowania użytkownika z adresem e-mail X
  2. Zapiszę gdzieś informację o tym, że użytkownik zmienił adres e-mail z X na Y
  3. Zapiszę gdzieś informację o tym, że użytkownik zmienił adres e-mail z Y na Z
  4. Jak trzeba będzie ustalić jaki jest obecny adres e-mail użytkownika to przeczytam wszystkie powyższe zdarzenia i będę wiedział, że aktualny adres e-mail to Z.

Poczytałem trochę, że Kafka może sprostać tym oczekiwaniom ponieważ w Kafce można zapisać sekwencje zdarzeń i ją później przeczytać od początku do końca. Jednak spotkałem się też z krytyką takiego zastosowania i w sumie nie wiem jak to jest (informacje krytyczne mogą nie być już aktualne).

Co do Rabbita to mam trochę większe wątpliwości czy to się do tego nadaje. Nie używałem tego ale kojarzy mi się to bardziej z przesyłaniem komunikatów bez gwarancji, że zostanie zachowana kolejność ich dostarczania no i nie wiem czy można sobie przeczytać od tak cała historie komunikatów od samego początku czy bardziej działa to na zasadzie "masz dostęp do komunikatu, którego jeszcze nie odebrałeś a nie do tych, które kiedyś zostały już dostarczone".

Ad. 2. Nie rozumiem pytania

Załóżmy hipotetycznie, że technologie RabbitMQ, Kafka i Axon spełniają wymagania i można to wykorzystać do Event Sourcingu. Jeżeli np. w projektach tworzonych za pomocą języka Java dominuje technologia Axon (np. w 8 na 10 Javowych projektach korzysta się z Axona, w 1 na 10 z Rabbita i w 1 na 10 z Kafki) to odpowiedź na moje pytanie brzmi "Axon jest najpopularniejsza technologią stosowana do Event Sourcingu w Javowych projektach". Gdyby to RabbitMQ był najbardziej popularną technologią w Javowych projektach to wtedy odpowiedź na moje pytanie brzmi "RabbitMQ jest najpopularniejsza technologią stosowana do Event Sourcingu w Javowych projektach". Sam RabbitMQ nie jest wtedy technologią ściśle powiązaną z Javą tylko technologią, z której mogą korzystać również programiści innych języków no ale jeżeli akurat Javowcy tego zazwyczaj używają to taka byłaby odpowiedź.

Ad. 3. Synchronizacja stanu bazy po offline to strasznie skomplikiwany problem. W przypadku ogolnym nierozwiązywalny

No właśnie odnoszę wrażenie, że przy zastosowaniu Event Sourcingu problem mógłby się znacznie uprościć (tak mi się przynajmniej wydaje na chwilę obecną :))

karsa napisał(a):

a czy na pewno jest Ci to potrzebne?

Jeszcze nie wiem :) Generalnie to chciałem poszerzyć nieco horyzonty więc chętnie wypróbuje :)

Shalom napisał(a):

Ta opcja 3 brzmi bardzo źle, bo oznacza że można by bardzo łatwo zaburzyć spójność danych i generalnie narobić cudów w systemie, skoro pozwalasz na "modyfikowanie historii", szczególnie z poziomu jakiejś aplikacji klienckiej. Nie wiem co ten system ma robić, ale mocno bym się nad tym zastanowił.

Rozumiem Twoje obawy. Jednak jeżeli chodzi o aplikacje kliencką to raczej chciałbym to zorganizować w ten sposób żeby taka aplikacja mogła synchronizować zdarzenia z serwerem tylko wtedy kiedy użytkownik ma odpowiednie uprawnienia do tego. Zakładam tutaj, że albo użytkownikiem nie będzie przypadkowa osoba albo sytuacje, w której użytkownik nie będzie miał możliwości synchronizowania zdarzeń, które mogą mieć wpływ na innych użytkowników.

1

Wydaje mi się, że algorytmy biznesowe offline a event sourcing to jest inna bajka - choć w sposób oczywisty widać podobieństwa
Zarazem, o ile algorytmy offline to istotna część realnej pracy w realnym życiu biznesowym, to mocno niedowartościowane teoretycznie.

KamilAdam napisał(a):

Ad. 2. Nie rozumiem pytania

ja też

Ad. 3. Synchronizacja stanu bazy po offline to strasznie skomplikiwany problem. W przypadku ogolnym nierozwiązywalny

Intuicyjnie mi się wydaje, że lekarstwo jest w projekcie dziedzinowym, aby rzeczy ulegały "samonaprawieniu", czy "samosynchronizacji" (idealnie, by nie psuły się wcale)
W każdym razie bardzo daleko od myślenia "weźmy technologię X" i nam załatwi

2
Shalom napisał(a):

Ta opcja 3 brzmi bardzo źle, bo oznacza że można by bardzo łatwo zaburzyć spójność danych i generalnie narobić cudów w systemie, skoro pozwalasz na "modyfikowanie historii", szczególnie z poziomu jakiejś aplikacji klienckiej. Nie wiem co ten system ma robić, ale mocno bym się nad tym zastanowił.

To akurat nie jest żaden problem. Jeśli masz cały system oparty o event sourcing to manipulowanie historią jest oczywiste i (prawie) proste.

Miliony developerów korzystających z gita widzi codziennie, że to działa.
Każdy głupi sieciowy RTS,FPS musi sobie też z tym radzić (o ile nie chcemy aby gracze mieli 2 sekundowy lag).

Problem jest tylko wtedy kiedy częśc systemu nie bazuje na eventach....

A rozwiązaniem jest jak w gicie - najlepiej w evencie podawać do której wersji obiektu się odnosi. Wtedy, w event handlerze można zauważyć, że ktoś nam namieszal i nie zgadza się oczekiwany numer wersji obiektu - można napisać jakiś "conflict solver",

Czasem konflkt będzie trywialny - jeśli inkrementujesz,sumujesz licznik to nie ma problemu.
Czasem będzie trudny - jeden użytkownik dodaje opis do produktu, a drugi go usuwa - rozwiązanie zależy od wymagań biznesowych.

Najbliżej gotowca do CQRS jest dla mnie jest https://www.lagomframework.com/documentation/1.6.x/java/Home.html.
Ale nie wiem na ile trudno by tam zrobić offline handling. Takie rzeczy robiłem dawno temu "ręcznie" (zanim dowiedziałem się, że to jest event sourcing).

0

Git przy commicie zapisuje pełne snapshoty, a nie kompaktowe eventy (diffy). Zachodzi oczywiście deduplikacja danych, by repo gitowe nie zajmowało kosmicznych ilości miejsca na dysku. Diffy w gicie są liczone na żądanie - każdy commit to pełny snapshot, więc jak chcemy porównać dwa commity to wyciągamy te dwa snapshoty i porównujemy. Wadą podejścia gitowego jest to, że czasami silnik do obliczania różnic się myli, np gdy w jednym commicie zarówno zmienimy trochę zawartość pliku jak i jego nazwę to git może nie załapać, że to edycja jednego pliku i zamiast modyfikacji pokaże usunięcie starego pliku i dodanie nowego. SVNy, CVSy i inne starożytne VCSy chyba nie mają takiego problemu. Coś za coś.

Jeśli chodzi o automatyczne rozwiązywanie konfliktów przy rozbieżnościach historii edycji to są do tego specjalne struktury danych (dla stosunkowo wąskiego grona przypadków): https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type

Edycja wycinka historii to moim zdaniem kiepski pomysł, bo przy edycji eventu o timestampie T trzeba:

  • wyrzucić wszystkie snapshoty z timestampem >= T
  • cofnąć się do ostatniego pozostałego snapshotu
  • odegrać od nowa (tzn nanieść zmiany w bazie odpowiadające zawartości eventu) wszystkie eventy o timestampie >= T

Można to porównać do edycji commitu gitowego. Jeśli edytujemy commit zrobiony 5000 commitów temu to trzeba przepisać te 5000 commitów, a to zajmuje czas.

W trakcie przepisywania historii (w sensie: odgrywania zedytowanej historii eventów na bazę danych do odczytu) cały system powinien dalej działać. Co więc robimy jeśli do końca przepisywania jeszcze daleko, a klienci chcą widzieć swoje dane (włączając najnowsze zmiany)? Może stawiamy dwie bazy i się między nimi przełączamy po przepisaniu historii?

Każdy głupi sieciowy RTS,FPS musi sobie też z tym radzić (o ile nie chcemy aby gracze mieli 2 sekundowy lag).

W przypadku gier nie ma potrzeby robienia merge'a. Każdy atrybut można traktować jako osobny plik, który się w całości nadpisuje, a nie merge'uje. Jeśli serwer np przeniesie gracza o wektor (3, 5) (bo tak sobie obliczył na podstawie jego poprzednich ruchów), a potem dostanie sygnał od niego, że naprawdę było (4, 4) to ten drugi sygnał wygrywa i tyle. Last write wins i po temacie.

Druga sprawa to to czy te silniki gier sieciowych robią jakąś skuteczną magię? Filmiki znalezione na szybko na YT zdają się temu przeczyć:

Symulowanie ruchów gracza przez serwer może się sprawdza dla małych lagów, ale przy dużych (np sekundy) mamy znaczny teleporting.

1

W aplikacjach biznesowych masz zwykle niezależne strumienie eventów na każdy agregat (właśnie z powodów wydajnościowych/skalowania).
Dlatego raczej tysięcy eventów odgrywać nie musisz.

Co do gier - moja wiedza pochodzi sprzed 15 lat kiedy tematem się zajmowałem (robiłem gre, nawet za pieniądze - chociaż nic z tego nie wyszło na koniec). Dużo wtedy doczytałem, choć w kod żadnej sensownej AAA nie zaglądałem. Ta książka (o ile pamiętam, bo dużo tego kiedyś czytałem) https://helion.pl/ksiazki/perelki-programowania-gier-vademecum-profesjonalisty-tom-3-dante-treglia,ppgvp3.htm
była moją inspiracją w temacie - choć niekoniecznie nazywało się to tam event sourcing (nie pamietam):

Generalnie bez jakiegoś event sourcingu trudno było w czasach internetu z pingiem 1s (20-15 lat temu) zrobić gre sieciową która nie będzie lagowała. RTS to najlepszy przykład - masz 200 jednostek na ekranie, 4 graczy - jedyne co możesz rozsyłać to eventy.
dzieją się wtedy zabawne sytuacje, bo może własnie strzeliłeś z jakiegos BFG... ale wg danych, które przeszły od innego gracza to twoja jednostka zginęła 50ms wcześniej.
Faktycznie w grach jest raczej globalny strumień eventów, a baza danych to kawałek pamięci... i wtedy masz dwie kopie (tak jak piszesz). Jedną z twoimi eventami - to leci na ekran. Drugą (safe), opóźnioną z eventami zatwierdznymi przez master (jeden z graczy zwykle pełni rolę master serwera). Jeśli dojdzie do konfliktu, to robi się klon safe i odtwarza eventy wg. kolejności u mastera.
Ważną rzeczą jest wykrywanie konfliktu - bo większość pomieszania zdarzeń nie wpływa na stan gry - wiec trzeba mieć jakiś checksum stanu gry (wpływają na niego twarde dane - ilość zywych jednostek itp), to że jakaś jednostka jest w nieco innym miejscu można zwykle olać. Jeśli konfliktów będzie dużo to fakycznie jest problem z permanentnym klonowaniem i odtwarzaniem z safe.
Na mniejszą skalę problem jest w grach FPS. To, że gracze lagują i przeskakują po ekranie to mały pikuś - problem jest jak dwóch graczy do siebie strzeli np, z rakiety - może być konflikt, kto zginął. A komuś fraga trzeba dać.

1 użytkowników online, w tym zalogowanych: 0, gości: 1