Jak zadbać o poprawność danych przy update'ach w wielu miejscach?

1

Załóżmy taką sytuację:

  1. Użytkownik w panelu web uzupełnia formularz i go zatwierdza wysyłając żądanie do backendu
  2. Backend robi update na naszej bazie
  3. Backend wysyła żądanie do zewnętrznej aplikacji z danymi przesłanymi przez użytkownika
  4. Backend zapisuje do redisa dane formularza

W prawidłowym scenariuszu we wszystkich tych miejscach - u nas na bazie, w bazie zewnętrznej aplikacji, w redisie powinny być spójne dane. Ale przecież może dojść do sytuacji, że ta aplikacja backendowa wybuchnie między krokiem 2 a 3, lub 3 a 4 prowadząc do tego, że część danych nie będzie zaktualizowana.

Dodatkowo, gdy użytkownik szybko wyśle 3 żądania z innymi danymi, nazwijmy te żądania: A, B, C, to przecież krok 2 może wykonać się w kolejności np. A, B, C a krok 3 i 4 niekoniecznie, powodując, że dane będą niespójne.

Co w takiej sytuacji można zrobić? Gdy mamy do czynienia tylko z bazą, to temat jest w miarę prosty, bo używamy transakcji i tyle. Ale w tym przypadku? Załóżmy, że krok 2 i 3 się wykonuje, ale 4 nigdy nie, bo jest jakiś problem z redisem. Trzeba teraz cofnąć zmiany z kroku 2 i 3. W kroku 3 można to ograć transakcją, ale wycowanie zmian z kroku 2 to już nie taka prosta sprawa, bo przecież to jest zewnętrzny serwis. Trzeba strzelić POSTem z poprzednimi danymi..

3

Do rozwiązania problemu tego, że w wielu miejscach ma coś się zadziać, to można skorzystać z takiego wzorca jak Saga. Wtedy jak coś nie pójdzie to jesteś w stanie poinformować o tym poprzednie kroki, żeby wycofały zmiany.
Jednak też jeżeli te miejsca są rozproszone, to sam design powinien uwzględniać to, że przez jakiś czas są niezsynchronizowane (tzw. eventual consistency) i umieć to obsłużyć.

Zapoznaj się też z takimi wzorcami jak "inbox" i "outbox" do zapewniania, że w komunikacji między serwisami nic nie zginie, bo może się zdarzyć, że jakieś zdarzenie zajdzieje się więcej niż raz (np. jak user 2x kliknie przycisk Wyślij, albo jakiś błąd komunikacji spowoduje ponowienie requestu do innego serwisu).

1

Zrób mikroserwis do sprawdzania consistency, tylko co jak on nie zadziała? Dobra, wpadam w paranoję:)

0

Nie ma jednej prawilnej odpowiedzi na to pytanie, przynajmniej w ogólnej formie. Nie wiemy jake są implikacje kroku 3 - może zewnętrzna apka wysyła maila do klienta z potwierdzeniem otrzymania zamówienia? Ale co jeśli krok 4 sfailuje i zamówienie nigdy się nie pojawi w twoim systemie? A może krok 3 to zwykły crud i operację wysłania danych można cofnąć?

Polecam ten materiał:

0

Zrób aplikację przechwytującą (walidującą) informacje z tych poprzednich kroków i niech ona będąc proxy obsługuje bazę danych i odsyła odpowiedź. Na tym komputerze gdzie jest ostatnia baza danych. Przyda Ci się na milion sposobów.

7

@johnny_Be_good: nie wszystkie problemy można rozwiązać twoim dziecinnym sposobem myślenia. Wnosisz do tematu wartość ujemną, nawet nie zadałeś sobie trudu żeby sprawdzić takie pojęcia jak:
Saga Pattern
Two-Phase Commit
DTM
I na ich podstawie coś naskrobać, ale do proponowania swoich durnych i nie mających związku z temat rozwiązań jesteś pierwszy. Na początku byłeś nawet zabawny z tymi barachłami które produkowałeś ale jak zacząłeś dawać rady innym to tylko zaśmiecasz forum.

1
dasek napisał(a):

Co w takiej sytuacji można zrobić?

Najprościej prowadzić jeden centralny dziennik i jak coś wybuchnie to po prostu przejść po jego wpisach od ostatniego przed wybuchem...

2

Na pewno nie ma jednej dobrej odpowiedzi na to pytanie. Wszystko się sprowadza do tego jak zaprojektujesz rozwiązanie.

Mój pierwszy pomysł jak to rozwiązać - czemu w ogóle potrzebujesz dwa miejsca z informacjami? Ja trzymałbym je tylko w jednym miejscu, wtedy nie ma problemu. Rozważ najpierw tę opcję.

0
dasek napisał(a):

Dodatkowo, gdy użytkownik szybko wyśle 3 żądania z innymi danymi, nazwijmy te żądania: A, B, C, to przecież krok 2 może wykonać się w kolejności np. A, B, C a krok 3 i 4 niekoniecznie, powodując, że dane będą niespójne.

To można łatwo rozwiązać dodając wersjonowanie na kroku 2, wtedy zawsze krok 3 i 4 będzie miał najświeższe dane, bo starsze może odrzucić. Właściwie wystarczy dodać datę modyfikacji lub uuid w wersji 7.
Wersjonowanie pomoże też w synchronizacji danych które się rozeszły, można na przykład okresowo puszczać synchronizację, na sam początek wystarczy że sprawdzisz czy liczba rekordów po danej dacie się zgadza. To normalne w systemach rozproszonych że różne serwery mają jeszcze przez pewien czas nieaktualne dane. Może w ogóle nie musisz kroków 3 i 4 robić na bieżąco tylko wystarczy synchronizacja co pewien czas? Wtedy masz mniejszy chaos do ogarnięcia

1

Sumy kontrolne, saga, optimistic locking, wszystkie te cudowne rozwiązania; są zarówno wymyślne, jak i bardzo skomplikowane; i konieczne jeśli musisz mieć dwa źródła danych.

Ale po co Ci dwa źródła danych, skoro możesz mieć jedno i wtedy nie masz w ogóle tego problemu?

1

Najprościej prowadzić jeden centralny dziennik i jak coś wybuchnie to po prostu przejść po jego wpisach od ostatniego przed wybuchem...

OK, ale samo zamiatanie i czyszczenie w razie rozjazdu/problemu to nie jest wszystko. Bo jeszcze trzeba zrobić jakiś mechanizm blokowania operacji na danym rekordzie do czasu jego pełnego "klepnięcia".

Powiedzmy, że w serwisie A dodajemy klienta, potem w B jego wykupione usługi, a w C jeszcze coś innego. I teraz, póki C nie zwróci potwierdzenia, że wszystko poszło OK, nie możemy traktować tego klienta jako pełnoprawnego, nie możemy mu świadczyć usługi, pobierać opłat czy dokonywać innych działań na nim. Bo OK, można odkręcić oczywiście jakieś działania, ale w sytuacji rollback'u ciężko będzie cofnąć np. przyznane dostępy, albo powiedzieć, że ma zapomnieć o materiale, który otrzymał (np. jakaś prezentacja czy film). Tak samo w innych biznesach - coś nie poszło, klienta wycofujemy z systemu - tyle, że ma już złożone zamówienie/towar uszykowany/podpisaną umowę. I mamy problem.

0
cerrato napisał(a):

Tak samo w innych biznesach - coś nie poszło, klienta wycofujemy z systemu - tyle, że ma już złożone zamówienie/towar uszykowany/podpisaną umowę.

Tak jak w każdym biznesie spekulacja rodzi za sobą ryzyko biznesowe. A kto nie spekuluje ten zostaje w tyle bo rzeczywistość jest zbyt skomplikowana by ogarnąć wszystko na spokojnie. Fail fast przyjacielu!

I mamy problem.

Nie, ten który zrealizował zamówienie ma problem. Bo on faktycznie zainwestował realny czas i pracę. Ty tylko zmieniałeś stan w abstrakcyjnej maszynie. Oczywiście kontrahent może złożyć zażalenie, zacząć się sądzić, w najlepszym przypadku po prostu może upaść.

Ale tak czysto realnie - skoro zatory płatnicze to norma a kredyt kupiecki opiera się o fikcję to może czas przestać analizować wyidealizowane modele a po prostu wyliczyć jaki % niespójności jest akceptowalny i yolo.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.