
Optimistic locking vs pessimistic locking
Rozważmy sytuację, gdy mamy do aktualizacji jakieś dane, które mogą być zaktualizowane równolegle przez wielu użytkowników. Wyobraźmy sobie taką sytuację. Mamy dwóch użytkowników A i B oraz rekord z trzema stanami: X, Y, Z, gdzie stan X jest jego stanem początkowym. Rozważmy taki ciąg zdarzeń:
- Użytkownik B odczytuje rekord, który jest w stanie X i rozpoczyna jego modyfikację.
- Użytkownik A odczytuje rekord, który jest w stanie X i rozpoczyna jego modyfikację.
- Użytkownik A zmienia stan rekordu na Y i zapisuje swoją zmianę.
- Użytkownik B zmienia stan rekordu na X i zapisuje swoją zmianę.
Problem powinien być łatwy do zauważenia. Zmiana stanu rekordu na Y została „zgubiona”. Użytkownik B odczytał rekord w stanie X i zapisał go w stanie Z, po tym jak użytkownik A zmienił jego stan.
Chciałbym tu przedstawić dwa ogólne podejścia do rozwiązania powyższego problemu.
Pierwszym z nich jest optimistic locking. Wraz z pobraniem rekordu pobierany jest znacznik jego wersji (hash, data ostatniej modyfikacji). W trakcie zapisu zmodyfikowanego rekordu znacznik ten jest porównywany do znacznika wersji rekordu w miejscu gdzie rekord jest przechowywany. jeśli są takie same to rekord zostaje zaktualizowany, a znacznik wersji rekordu się zmienia.
Jeśli podczas zapisu znacznik przesłanego zmodyfikowanego przez użytkownika rekordu jest różny od znacznika przechowywanego rekordu to oznacza, że został on w międzyczasie zmodyfikowany i powinniśmy podjąć jakąś akcję, np. poinformować o tym użytkownika.
Wadą tego podejścia jest to, że jeśli rekord zmienia się często to możemy nie być w stanie go zmodyfikować.
Drugim podejściem jest pessimistic locking. Polega ono na „zablokowaniu” rekordu w momencie rozpoczęcia jego edycji do czasu aż tą edycję zakończymy.
To podejście ma jednak znaczną wadę. Co jeśli w tym samym czasie inni użytkownicy będą chcieli zaktualizować rekord albo co się stanie jeśli rozpoczniemy edycję albo jej nie zakończymy? Rekord w tym czasie będzie niemożliwy do zmodyfikowania dla innych.
W bazach danych i innych systemach służących do przechowywania danych ten problem niemal zawsze jest już rozwiązany, a my korzystamy z nich czasami nie zdając sobie z niego sprawy.
Jednakże czasami może się zdarzyć, że sami będziemy musieli rozwiązać przedstawiony wyżej problem i wtedy warto rozważyć, które z przedstawionych podejść lepiej spełnia nasze wymagania. Wiele aplikacji internetowych nie będzie miało problemów z działaniem zezwalając na dirty reads (pierwszy przypadek – odczytany rekord niekonicznie ma ten sam stan jak jego zapisany odpowiednik). Jednakże w systemach finansowych taka sytuacja jest niedopuszczalna i zastosowanie drugiego podejścia jest wymagane.
Jak zwykle w programowaniu – jedyną poprawną odpowiedzią na pytanie, które rozwiązanie wybrać jest „to zależy”. Od czego? Na to pytanie każdy powinien odpowiedzieć sam, rozważając projekt, w którym pracuje, jego specyfikę, wymagania klienta, wagę przechowywanych danych, etc.