Dwie sesje powinny wyglądać tak:
user = Student.query.with_for_update(of=Student, nowait=True).filter(Student.id == 122).first()
user.type = 1
db.session.commit()
i
user = Student.query.with_for_update(of=Student, nowait=True).filter(Student.id == 122).first()
user.type -= 1
db.session.commit()
Aby wykonać FOR UPDATE
działać poprawnie, wszystkie zaangażowane transakcje, które zamierzają zaktualizować wiersz, muszą z niego korzystać.
W twoim przykładzie sesja 2 nie używa with_for_update
. Ponieważ nie kazałeś mu używać FOR UPDATE
, można odczytać starą wartość wiersza (ponieważ nowa wartość nie została jeszcze zatwierdzona, a blokady nie blokują czystych czytelników), a następnie zmodyfikować tę wartość w pamięci, a następnie zapisać ją z powrotem.
Jeśli nie chcesz używać FOR UPDATE
wszędzie tam, gdzie czytasz wiersz z zamiarem jego zmiany, możesz zamiast tego użyć isolation level serializable
wszędzie. Jeśli jednak to zrobisz, rzeczy mogą się nie blokować, ale raczej będą wyglądać na szczęśliwe do czasu zatwierdzenia, a następnie wyrzuć błędy serializacji, które będą musiały zostać przechwycone i rozwiązane.
Uwaga: Twój przykład przed edycją powinien zadziałać, ponieważ obie sesje zostały oznaczone tagiem with_for_update
.