Obsluga racecondition podczas walidacji

0

Cześć

Mam prolem z projektem gdzie muszę zaimplementować racecondition podczas walidacji.
Czy ktoś obeznany w temacie mógłby mi pomóc??

1

Musisz zaimplementować race condition? Ominąć czy zabezpieczyć przed to miałoby jeszcze sens, ale w zaimplementowaniu nie widzę żadnego celu ("mam problem [...] muszę połączyć rurę wydechową z wnętrzem samochodu") - czy na pewno tak brzmi treść Twojego polecenia? 😅

0

Doprecyzuje moje pytanie chodzi obsłużenie racecondition podczas walidacji, brakuje mi tego w projekcie i nie wiem jak do tego podejść, jak to rozwiązać

1

Nadal trochę mało - skąd bierze się ten race condition, czego dotyczy (baza danych, jakaś wewnętrzna struktura danych w aplikacji, ...)? Najlepiej byłoby gdybyś wrzucił kod.

0

@Patryk27:
ja z tym kolegą sobie rozmawiałem i sytuacja wygląda tak:
Masz rest api służące do dodawania np. lekarzy. API przyjmuje LekarzDTO, na klasie jest zrobiona walidacja. Walidacja jest zrobiona w postaci customowej adnotacji, walidacja sprawdza czy tam nip taki nie istnieje w bazie. No generalnie walidacja odpytuje bazę czy może taki obiekt wgl. spróbować wstawić - nie pytaj czemu tak jest bo to bez sensu. No i rozpatrz taki case:

  • wysyłasz DTO Janusz z NIPem 0000, odpytujesz bazę w walidacji czy taki nip już nie istnieje, jak nie istnieje to dto na encje i to leci do repo
  • w tym samym czasie, leci drugi req. DTO Grażyna z nipem 0000, Janusz został zwalidowany ale nie zdążyliśmy go zapisać a walidacja Grażyny zwróci true bo w bazie nie jest jeszcze zapisany Janusz. Tu poleci błąd przy zapisie grażyny na etapie zapisu i kolega "ma się przed tym zabezpieczyć".
    Czaisz, abstrakcyjny przypadek rozbijający się o "okno" rzędu 0.001 sekundy.

I teraz cała zabawa polega na tym że trzeba do tego naklepać testy w Springu.

1

W takiej sytuacji oparłbym się o mechanizmy dostępne wprost w bazie danych - i tak na przykład Postgresql obsługuje tzw. predicate locking, który powinien rozwiązać ten problem :-)

0

W sensie zamkną walidację i zapis w jakimś locku bazodanowym aby uczynić tą operację atomową?

1

Kinda; wykonujesz transakcje w trybie serializable - wtedy gdy jedna transakcja odpali select * from lekarze where nip = 'abcd', to druga, próbując odpalić to samo zapytanie, zostanie AFAIR zblokowana do czasu zakończenia tej pierwszej (stąd "predicate locking", ponieważ blokada następuje na predykacie nip = 'abcd', a nie na konkretnym wierszu - który może nie jeszcze istnieć - jak miałoby miejsce w przypadku select for update).

Przy czym wiem tyle tylko, że taki mechanizm istnieje - nie jestem na 100% pewien czy tak właśnie działa, ale do przetestowania wystarczy odpalić w konsoli kilka zapytań :-)

5

Moim zdaniem to w ogóle nie ma żadnego sensu jako walidator na poziomie DTO. Tam sie waliduje jakieś statyczne duperele jak puste pola czy długość stringa. Trudniejsze rzeczy niech się walidują w operacjach biznesowych i tyle. Inaczej to i tak jest klasyczne https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use i nie da się tego jakoś magicznie naprawić. Jakakolwiek walidacja która nie jest atomowa z finalną operacją, będzie miała race condition.

0

Tak bardziej szczegółowo - to co tutaj jest problemem zazwyczaj w kontekście baz jest nazywane phantom read. Wyszukujesz w tabeli wszystkie wiersze spełniające pełen predykat, ale przy poziomach izolacji repeatable read i poniżej nie masz żadnej gwarancji że zestaw wierszy zwróconych z predykatu się nie zmieni w trakcie transakcji (repeatable read gwarantuje jedynie że wiersze zwrócone w poprzednich odczytach z predykatem zostaną zwrócone po raz drugi, nie gwarantuje że nie powstaną nowe wiersze)

Zgodnie z ANSII jedyny mocniejszy poziom izolacji to serializable, z tym że tutaj trzeba popatrzyć na:

  • fakt że nie wszystkie bazy faktycznie implementują to poprawnie (np w Oracle jak ustawisz serializable to dostajesz tylko snapshot isolation co jest za słabe żeby pozbyć się fantomów, musisz sam założyć lock na tabelę)
  • dwa style implementacji - z pessimistic concurrency control (np MSSQL) i z optimistic concurrency control (np postgres) - w przypadku pierwszego założony zostanie lock na tabeli na wszystko co spełnia predykat, czyli zablokuje ci to współbieżne transakcje. W przypadku drugiego masz jedynie gwarancję że konflikt taki zostanie wykryty, i jedna z transakcji zostanie zabortowana.
  • tendencję do zabijania wydajności przy tym poziomie izolacji

Najprościej będzie sobie wrzucić unique constraint na NIP, bo to zostanie wykryte nawet w przypadku kiedy dojdzie do podobnego problemu w transakcji.

Jeśli chodzi o testowanie - jedynie testy integracyjne niefunkcjonalne, bo problem leży po stronie bazy a nie aplikacji (chyba że chcesz się bawić w implementację transakcji na poziomie aplikacji, tylko po co dla takiej pierdoły)

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