Django select_for_update приобретает блокировку отношения
У меня есть пример кода, который должен принимать блокировку строки (кортежа) в postgres, однако он, похоже, принимает блокировку таблицы (отношения) вместо этого:
with transaction.Atomic(savepoint=True, durable=False):
record = MyModel.objects.select_for_update().filter(pk='1234')
record.delete()
time.sleep(5)
raise Exception
Просматривая pg_locks во время транзакции, я вижу:
select locktype, database, relation::regclass, pid, mode, granted from pg_locks where pid <> pg_backend_pid();
По моим знаниям, я должен был увидеть "tuple" в типе блокировки, поскольку я блокирую только конкретную строку/ы, а не всю таблицу
Первые дела
Вы фактически не выполнили SELECT FOR UPDATE запрос.
record = MyModel.objects.select_for_update().filter(pk='1234')возвращаетQuerySet, запрос не выполняется.record.delete()выполняет толькоDELETEзапрос.
- Запрос
SELECT FOR UPDATEполучил быrelationRowShareLock.- Вы можете проверить это, выполнив
QuerySetс.first(), т.е.record = MyModel.objects.select_for_update().filter(pk='1234').first().
- Вы можете проверить это, выполнив
- Вы можете проверить это с помощью log all sql queries.
Блокировки кортежей
Блокировка уровня строки FOR UPDATE получена, но не показана в вашем представлении pg_locks (она не показана и на моем). Вместо этого мы видим transactionid ExclusiveLock (и virtualxid ExclusiveLock).
From https://www.postgresql.org/docs/9.3/view-pg-locks.html:
Хотя кортежи являются блокируемым типом объекта, информация о блокировках на уровне строки хранится на диске, а не в памяти, и поэтому блокировки на уровне строки обычно не появляются в этом представлении. Если транзакция ожидает блокировки на уровне строки, она обычно отображается в представлении как ожидающая постоянного идентификатора транзакции текущего владельца этой блокировки строки.
From https://www.postgresql.org/docs/9.4/explicit-locking.html:
FOR UPDATE...
Режим блокировки
FOR UPDATEтакже приобретается любымDELETEв ряду ...
Вы можете проверить это эмпирически, выполнив в терминале psql:
- перед
record.delete()SELECT FROM mymodel WHERE id='1' FOR UPDATE;работает.SELECT FROM mymodel WHERE id='1234' FOR UPDATE;работает.
- после
record.delete()SELECT FROM mymodel WHERE id='1' FOR UPDATE;работает.SELECT FROM mymodel WHERE id='1234' FOR UPDATE;не работает.
Блокировки отношений
- Судя по всему,
relationAccessShareLockполучен дляSELECTзапроса, который вы не показали в примере кода, например,MyModel.objects.filter(pk='1234').first(). -
relationRowExclusiveLockприобретается дляDELETEзапроса.
From https://www.postgresql.org/docs/9.4/explicit-locking.html:
ACCESS SHAREКонфликтует с ...
Команда
SELECTприобретает блокировку этого режима на ссылающихся таблицах. В общем, любой запрос, который только читает таблицу и не изменяет ее, приобретает этот режим блокировки.
ROW EXCLUSIVEКонфликты с ...
Команды
UPDATE,DELETEиINSERTприобретают этот режим блокировки на целевой таблице (в дополнение кACCESS SHAREблокировкам на любых других ссылающихся таблицах). В общем, этот режим блокировки будет приобретен любой командой, изменяющей данные в таблице.
