Spis treściKliknij link, aby przejść do wybranego miejsca
Ta treść została automatycznie przetłumaczona z ukraińskiego.
Jeśli pracujesz z Rails i musisz uniknąć jednoczesnej zmiany jednego rekordu przez kilka procesów, warto zwrócić uwagę na mechanizm blokowania rekordów w bazie danych. Jednym z najpewniejszych sposobów na to jest pessimistic locking.
Co to jest pessimistic lock?
Pessimistic lock (pesymistyczne blokowanie) oznacza, że rekord w bazie danych jest fizycznie blokowany dla zmian z innych wątków lub procesów, dopóki bieżąca operacja się nie zakończy. To znaczy, że jeśli jeden proces uzyskał blokadę na rekord, inne muszą czekać, aż ją zwolni.
W ActiveRecord pesymistyczne blokowanie realizowane jest przez metodę lock, która dodaje SELECT ... FOR UPDATE, co utrzymuje blokadę do końca transakcji.
Użycie w Rails
Oto prosty przykład użycia lock w Rails:
ActiveRecord::Base.transaction do order = Order.lock.find(order_id) order.status = "processed" order.save! end
W tym przypadku, jeśli inny proces spróbuje uzyskać Order z tym samym order_id, będzie musiał czekać, aż bieżący proces zakończy transakcję.
Po co to potrzebne?
Wyobraźmy sobie, że przetwarzamy zamówienia. Kiedy użytkownik naciska przycisk "Zapłać", musimy zmniejszyć ilość towaru na magazynie. Jeśli kilka osób jednocześnie próbuje kupić ostatnią sztukę towaru, bez blokady możliwy jest wyścig warunków:
- Proces 1 odczytuje ilość towaru (1 sztuka).
- Proces 2 odczytuje tę samą ilość (1 sztuka).
- Oba procesy próbują zapisać nową wartość (0 sztuk).
- Jeden z procesów traci aktualizację drugiego.
Jeśli jednak użyjemy lock, drugi proces będzie musiał poczekać na zakończenie pierwszego, a dopiero potem podejmować decyzję, co robić.
Alternatywy dla pessimistic lock
Pessimistic locking nie zawsze jest najlepszym wyborem, ponieważ może blokować inne procesy, spowalniając działanie aplikacji. Czasami lepiej użyć:
- Optimistic Locking — sprawdza, czy rekord zmienił się między odczytem a zapisem.
- Operacje atomowe — używa UPDATE ... WHERE lub increment! bez odczytywania wartości w Ruby.
- Podejście oparte na kolejce — rozdziela aktualizacje przez kolejkę (Sidekiq, RabbitMQ).
Optimistic Locking w Rails
Optimistic locking działa przez kolumnę lock_version. Jeśli dwa procesy odczytują rekord, to przy próbie zapisania zmian system sprawdza, czy rekord nie zmienił się w międzyczasie:
class Order < ApplicationRecord attr_accessor :lock_version end
Wtedy, jeśli jeden proces wprowadzi zmiany, a inny spróbuje zapisać przestarzałą wersję, otrzyma ActiveRecord::StaleObjectError.
Prościej mówiąc:
- Pessimistic locking blokuje rekord, aby uniknąć konfliktów.
- Optimistic locking pozwala pracować bez blokady, ale wymaga obsługi konfliktów.
- Operacje atomowe zmniejszają ryzyko wyścigu warunków bez blokad.
Wybieraj odpowiednie podejście w zależności od zadania. Jeśli krytycznie uniknąć konfliktów za wszelką cenę — użyj lock. Jeśli ważna jest wydajność i można pozwolić na konflikty — lepiej lock_version lub aktualizacje atomowe.
Ten post nie ma jeszcze żadnych dodatków od autora.