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
получил быrelation
RowShareLock
.- Вы можете проверить это, выполнив
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;
не работает.
Блокировки отношений
- Судя по всему,
relation
AccessShareLock
получен дляSELECT
запроса, который вы не показали в примере кода, например,MyModel.objects.filter(pk='1234').first()
. -
relation
RowExclusiveLock
приобретается дляDELETE
запроса.
From https://www.postgresql.org/docs/9.4/explicit-locking.html:
ACCESS SHARE
Конфликтует с ...
Команда
SELECT
приобретает блокировку этого режима на ссылающихся таблицах. В общем, любой запрос, который только читает таблицу и не изменяет ее, приобретает этот режим блокировки.
ROW EXCLUSIVE
Конфликты с ...
Команды
UPDATE
,DELETE
иINSERT
приобретают этот режим блокировки на целевой таблице (в дополнение кACCESS SHARE
блокировкам на любых других ссылающихся таблицах). В общем, этот режим блокировки будет приобретен любой командой, изменяющей данные в таблице.