Есть ли опыт решения задач блокировок и транзакций в Django
Что проверяют
Сталкивались ли вы с конкурентным доступом к данным на практике и понимаете ли, что ORM делает на уровне SQL. Интервьюер ждёт историю про гонку: двойное списание, повторное бронирование, отрицательный остаток. Следом спрашивают про взаимные блокировки и побочные эффекты внутри транзакции.
Как отвечать
Расскажите свой случай по схеме «проблема, механизм, решение, как проверили» и по ходу покажите инструменты:
- режим по умолчанию: Django работает в автокоммите.
ATOMIC_REQUESTS = Trueоборачивает в транзакцию каждую вьюху целиком, но держит её дольше нужного; transaction.atomic(): декоратор или контекстный менеджер. Вложенныйatomicсоздаёт точку сохранения, и откатывается только внутренний блок;select_for_update(): пессимистичная блокировка строк до конца транзакции. Работает только внутриatomic, иначеTransactionManagementError. Естьnowait,skip_lockedдля очередей задач иofдляselect_related;- атомарное обновление:
filter(pk=pk, balance__gte=amount).update(balance=F("balance") - amount), число изменённых строк покажет, прошло ли списание; - оптимистичная блокировка: поле версии и
updateс условием на старую версию. Ноль строк значит, что запись успел поменять другой; transaction.on_commit(): письма и задачи Celery уходят только после фиксации, иначе воркер прочитает данные, которых ещё нет.
От взаимных блокировок спасает единый порядок захвата строк, например по возрастанию pk. Уникальность держите ограничением в базе: get_or_create защищён от дублей, только когда оно есть.
Если в проде такой задачи не было, скажите честно и опишите, как воспроизвели бы гонку тестом с двумя потоками и чем бы её закрыли.
Где спотыкаются
Проверяют решение на SQLite. Там select_for_update() игнорируется, тесты зелёные, а на PostgreSQL гонка остаётся.
Ловят исключение базы внутри atomic и продолжают запросы. Транзакция уже сломана, нужен вложенный atomic вокруг опасного участка.
Держат блокировку, пока ходят во внешний API. Строка заблокирована секундами, запросы выстраиваются в очередь.