Zastanów się nad witryną e-commerce, w której Alice i Bob edytują listę produktów. Alice poprawia opisy, a Bob aktualizuje ceny. Jednocześnie rozpoczynają edycję widgetu Acme Wonder. Bob kończy jako pierwszy i zapisuje produkt w nowej cenie. Alice potrzebuje nieco więcej czasu na aktualizację opisu, a kiedy skończy, zapisuje produkt z nowym opisem. Niestety, ona również zastępuje cenę starą, która nie była zamierzona.
Z mojego doświadczenia wynika, że te problemy są niezwykle powszechne w aplikacjach internetowych. Niektóre oprogramowanie (np. Oprogramowanie wiki) ma ochronę przed tym - zwykle drugie zapisywanie kończy się niepowodzeniem z informacją, że „strona została zaktualizowana podczas edycji”. Ale większość stron internetowych nie ma tej ochrony.
Warto zauważyć, że metody kontrolerów same w sobie są bezpieczne dla wątków. Zwykle używają transakcji bazy danych, co czyni je bezpiecznymi w tym sensie, że jeśli Alice i Bob spróbują zaoszczędzić dokładnie w tym samym momencie, nie spowoduje to uszkodzenia. Wyścig wynika z faktu, że Alice lub Bob mają nieaktualne dane w przeglądarce.
Jak możemy zapobiec takim warunkom wyścigowym? W szczególności chciałbym wiedzieć:
- Jakie techniki można zastosować? np. śledzenie czasu ostatniej zmiany. Jakie są zalety i wady każdego z nich.
- Jaka jest wygodna obsługa?
- Jakie ramy mają wbudowaną tę ochronę?
źródło
Odpowiedzi:
Musisz „czytać swoje zapisy”, co oznacza, że zanim zanotujesz zmianę, musisz ponownie przeczytać zapis i sprawdzić, czy dokonano w nim zmian od czasu ostatniego odczytu. Możesz to zrobić pole po polu (drobnoziarnisty) lub na podstawie znacznika czasu (gruboziarnisty). Podczas wykonywania tej kontroli potrzebujesz wyłącznej blokady rekordu. Jeśli nie wprowadzono żadnych zmian, możesz zapisać zmiany i zwolnić blokadę. Jeśli w międzyczasie rekord się zmienił, przerywasz transakcję, zwalniasz blokadę i powiadamiasz użytkownika.
źródło
Widziałem 2 główne sposoby:
Dodaj znacznik czasu ostatniej aktualizacji strony, którą edytuje użytkownik, w ukrytych danych wejściowych. Przy zatwierdzaniu znacznik czasu jest porównywany z bieżącym, a jeśli się nie zgadzają, został zaktualizowany przez kogoś innego i zwraca błąd.
pro: wielu użytkowników może edytować różne części strony. Strona błędu może prowadzić do strony różnicowej, na której drugi użytkownik może scalić swoje zmiany na nowej stronie.
con: czasami duża część wysiłku zostaje zmarnowana podczas dużych jednoczesnych edycji.
Gdy użytkownik rozpoczyna edycję strony, blokuje ją na odpowiedni czas, a następnie inny użytkownik próbuje edytować stronę, wyświetla stronę błędu i musi poczekać, aż wygaśnie blokada lub pierwszy użytkownik się na nią zgodzi.
pro: wysiłki edycyjne nie są marnowane.
con: pozbawiony skrupułów użytkownik może zablokować stronę na czas nieokreślony. Strona z wygasłą blokadą może nadal być w stanie dokonać zatwierdzenia, chyba że postępuje się inaczej (przy użyciu techniki 1)
źródło
Użyj optymistycznej kontroli współbieżności .
Dodaj kolumnę versionNumber lub versionTimestamp do odpowiedniej tabeli (liczba całkowita jest najbezpieczniejsza).
Użytkownik 1 czyta rekord:
Użytkownik 2 czyta rekord:
Użytkownik 1 zapisuje rekord, co zwiększa wersję:
Użytkownik 2 próbuje zapisać odczytany rekord:
Hibernacja / JPA może to zrobić automatycznie z
@Version
adnotacjąMusisz gdzieś utrzymać stan odczytanego rekordu, na ogół w sesji (jest to bezpieczniejsze niż w zmiennej o ukrytej formie).
źródło
SQLAlchemy
do niczego nie wskazuje na optymistyczne blokady offline i nie mogę tego znaleźć w dokumentacji. Czy masz bardziej pomocny link lub po prostu kierujesz ludzi do SQLAlchemy w ogóle?Niektóre systemy mapowania obiektowego (ORM) wykryją, które pola obiektu uległy zmianie od czasu załadowania z bazy danych, i utworzą instrukcję aktualizacji SQL, aby ustawić tylko te wartości. ActiveRecord dla Ruby on Rails to jedna z takich ORM.
Efektem netto jest to, że pola, których użytkownik nie zmienił, nie są uwzględnione w poleceniu UPDATE wysłanym do bazy danych. Ludzie, którzy aktualizują różne pola w tym samym czasie, nie zastępują się nawzajem zmianami.
W zależności od używanego języka programowania sprawdź, które ORM są dostępne i sprawdź, czy którykolwiek z nich zaktualizuje tylko kolumny w bazie danych oznaczone jako „brudne” w Twojej aplikacji.
źródło