W systemach monolitycznych mamy:
zcentralizowany punkt przechowywania danych
Transakcje ACID (atomicity, consistency, isolation, durability)
niepodzielność - w całości, albo w ogóle - przykład transfer pieniędzy
spójność - nie zostaną naruszone zasady integralności
izolacja - dwie transakcje wykonują się współbieżnie (w zależności od poziomu izolacji) nie widzą wprowadzanych przez siebie zmian
trwałość - potrafi uruchomić się i udostępnić spójne, nienaruszone i aktualne dane zapisane w ramach zatwierdzonych transakcji, na przykład po nagłej awarii zasilania
Pojawiają się pierwsze problemy ze skalowalnością
Wprowadzamy kolejki
Ale nadal pracujemy w jednym projekcie, gdzie wprowadzanie zmian jest mozolne, musi być dobrze skoordynowane
W systemach monolitycznych problemem jest:
Większe zmiany muszą często być skorelowane we wszystkich modułach
Upgrade frameworku lub vendora może przynieść sporo wyzwań w trakcie developmentu i na CR
Easi’r 326 endpointów publicznych 42 agregaty
Zmieniamy podejście idziemy w stronę mikroserwisów
Najpierw pojawia się jeden Uber mikroservice - taki mdakroserwice w zasadzie
Każdy mikroserwis musi mieć swoje API wiadomo, odseparowujemy UI poprzez API Gateway
Pojawiają się urządzenia mobilne
Pojawiają się nowi klienci korzystający z klientów API
Pojawiają się kolejne mikroserwisy, nowe lub wydzierane z Uber mikroserwisu
Nowe mikroserwisy mają oddzielne bazy danych (SQL / NoSQL nie ma znaczenia)
API Gateway ma więcej roboty możemy zacząć skalować kendpointy
Łatwiej zarządzać zmianą w każdym z osobna
Pojawiają się kolejne mikroserwisy, z odrębnymi bazami danych
Na tym poziomie już widać że problemy ze spójnością danych mogą się pojawić
Tradycyjne transakcje bazodanowe nie realne do osiągnięcia
Mamy problemy z wydajnością, zwykłe skalowanie nie wystarcza
Mamy piki ruchu i potrzebujemy rozładować co się da na kolejkach
Zarządzanie zmianą końcu nie jest problemem (może nie do końca ale przynajmniej większość zmian w dziedzinie mikroserwisu wdrażamy mniejszym impaktem na pozostałe)
Ostateczna spójność (Eventual Consistency) fajnie to opisał Radek Maziarka
Dlaczego Bounded Contexty są ważne - ostateczna spójność
gdy system działał jako monolit - OK
połączenia do tej samej bazy danych,
jeden proces,
łatwo było rollbackować transakcje.
Jak zapewnić spójność danych w systemie rozproszonym?
W systemach rozproszonych podzielonych na mikrosierwisy każdy z mikroserwisów będzie korzystał z własnej bazy danych (relacyjnej bądź nie) stąd brak możliwości rozpostarcia transakcji bazodanowej (systemy mogą fizycznie być w różnych centrach danych).
Przy 2PC rozciągamy transakcję na wszystkie mikroserwisy
2PC - Two Phase Commit - Prepare a potem Commit - w PHP?! Rzeźba do tego Single Point of failure (jedno miejsce zarządza transakcją)
SAGA Pattern jest szeroko stosowany od ponad 20 lat.
Często Process Manager jest rozumiany tożsamo ze wzorcem Sagi.
whose execution, even without interference from other transactions, takes substantial amount of time, possibly on the order of hours or day
Saga to coś co może rozciągać się w czasie niczym telenowela.
Saga jest patternem służącym do orkiestracji wielu zdarzeń, które mogą zajść w rozproszonym systemie w nieokreślonej kolejności. Czas pomiędzy zajściem zdarzeń może być relatywnie długi, dlatego należy persystować jej stan w czasie oczekiwania na kolejne zdarzenie.
Saga to coś co może rozciągać się w czasie niczym telenowela.
Saga jest wzorcem służącym do orkiestracji wielu zdarzeń, które mogą zajść w rozproszonym systemie w nieokreślonej kolejności. Czas pomiędzy zajściem zdarzeń może być relatywnie długi, dlatego należy persystować jej stan w czasie oczekiwania na kolejne zdarzenie.
Zamiast tego przy pomocy Sagi sprowadzamy to do SERII lokalnych transakcji które implementują ACID
Lecz cała seria transakcji razem implementuje ACD nie ma izolacji
Co to była izolacja, przypomnienie:
izolacja - dwie transakcje wykonują się współbieżnie (w zależności od poziomu izolacji) nie widzą wprowadzanych przez siebie zmian
BookCar i BookHotel będą widoczne dla innych przed zakończeniem cyklu życia Sagi
W trakcie wykonywania procesu Sagi w ujęciu transakcyjnym
zmiany dokonane przez jeden proces
będą widoczne przez wszystkich w trakcie trwania procesu
Istnieje kilka sposobów implementacji wzorca SAGA,
Główne to Choreografia i Orchestrator.
Istnieje kilka sposobów implementacji wzorca SAGA,
Główne to Choreografia i Orchestrator.
1. OrderCreated zdarzenie Agregatu Order w OrderService
2. PaymentService(OrderCreated) listener dispatchuje CreatePayment które się procesuje i albo jest Completed / Failed
3. PaymentCompleted obsługiwane przez ShipmentService itd.
Istnieje kilka sposobów implementacji wzorca SAGA,
Główne to Choreografia i Orchestrator.
1. OrderCreated zdarzenie Agregatu Order w OrderService
2. OrderProcess(OrderCreated) dispatchuje CreatePayment które się procesuje i albo jest Completed / Failed
3. PaymentCompleted obsługiwane przez OrderProcess itd.
Przewaga
Logika w jednym miejscu
Stan procesu / transakcji w jednym miejscu
Łatwiejsze zarządzanie zmianą (decoupling kolejki)
Orkiestracja pozwala zarządzać procesem z boku co pozwala na integrowanie mikroserwisów i całych usług różnych dostawców 3rd parties
Nic nas nie ogranicza, nie ma “DistributedTransaction” jak w 2PC
Przewaga nadal taka sama
Logika w jednym miejscu
Stan procesu / transakcji w jednym miejscu
Łatwiejsze zarządzanie zmianą (decoupling kolejki)
Łatwość wymiany całej usługi
Seria transakcji z kompensacją ma zapobiec niespójnościom.
Nie zrealizowany payment - zamówienie anulowane
Nie dostarczony towar - zwrot środków, zamówienie anulowane
Analogia do Agregatów
Agregaty(Commandy) -> Eventy
Saga(Eventy) -> Commandy
StartSagi determinuje zdarzenie i wybrana polityka
IfNoneExist
Always
Never
SagaMessageHandler adnotacja determinuje key po którym znajdujemy konkretną instancję
Metody Sagi powinny być idempotentne
Każda metoda sagi powinna mieć kopensację jeśli jest cień szansy na Exception
Metody kompensujące powinny być idempotentne tak samo jak metody Sagi
Nie ma event listenerów!
Saga jest tym persystentnym multi event listeners
Który jedyne co robi to nadzoruje proces i wydaje komendy
Ewentualnie nakłada deadline’y lub kompensuje niepowodzenia
Logika jest w jednym miejscu, w implementacji Sagi
Nie potrzebujemy dodatkowych agregatów np. OrderProcess ani specjalnego repozytorium
Spora część kodu zastąpiona przez zautomatyzowany mechanizm persystencji każdej instancji Sagi
Serializujemy instancje Sagi, do JSON lub PHP nie ma znaczenia (JSON lepiej widać w bazie)
Przy kolejnym zdarzeniu szukamy instancji Sagi po kluczu i odserializowujemy
Wywołujemy metodę handlera
Obsługujemy wyjątki
Zapisujemy nowe powiązania
Kończymy cykl życia instancji
Planujemy nowe deadline’y
Anulujemy deadline’y
Przykłąd OrderProcessing ma ok 120 lini kodu! Tylko