First things first
You actually did not perform a SELECT FOR UPDATE
query.
record = MyModel.objects.select_for_update().filter(pk='1234')
returns a QuerySet
, no query is executed.
record.delete()
only executes a DELETE
command.
- A
SELECT FOR UPDATE
query would have acquired a relation
RowShareLock
.
- You can verify this by executing the
QuerySet
with .first()
, i.e. record = MyModel.objects.select_for_update().filter(pk='1234').first()
.
- You can verify this with log all sql queries.
Row-level (tuple) locks
A row-level FOR UPDATE
lock is acquired but not shown in your pg_locks
view (it doesn't show on mine too). Instead, we see transactionid
ExclusiveLock
(and virtualxid
ExclusiveLock
).
From https://www.postgresql.org/docs/9.3/view-pg-locks.html:
Although tuples are a lockable type of object, information about row-level locks is stored on disk, not in memory, and therefore row-level locks normally do not appear in this view. If a transaction is waiting for a row-level lock, it will usually appear in the view as waiting for the permanent transaction ID of the current holder of that row lock.
From https://www.postgresql.org/docs/9.4/explicit-locking.html:
FOR UPDATE
...
The FOR UPDATE
lock mode is also acquired by any DELETE
on a row ...
You can verify this empirically by running in your psql terminal:
- before
record.delete()
SELECT FROM mymodel WHERE id='1' FOR UPDATE;
works.
SELECT FROM mymodel WHERE id='1234' FOR UPDATE;
works.
- after
record.delete()
SELECT FROM mymodel WHERE id='1' FOR UPDATE;
works.
SELECT FROM mymodel WHERE id='1234' FOR UPDATE;
doesn't work.
Table-level (relation) locks
- The
relation
AccessShareLock
appears to be acquired for a SELECT
query that you did not show in your code sample, e.g. MyModel.objects.filter(pk='1234').first()
.
- The
relation
RowExclusiveLock
is acquired for the DELETE
command.
While these are table-level locks, they only conflict with EXCLUSIVE
and/or ACCESS EXCLUSIVE
locks, which are not acquired by most other DQL (data query language) and DML (data manipulation language) commands.
From https://www.postgresql.org/docs/9.4/explicit-locking.html:
ACCESS SHARE
Conflicts with the ACCESS EXCLUSIVE
lock mode only.
The SELECT
command acquires a lock of this mode on referenced tables. In general, any query that only reads a table and does not modify it will acquire this lock mode.
ROW EXCLUSIVE
Conflicts with the EXCLUSIVE
and ACCESS EXCLUSIVE
lock modes.
The commands UPDATE
, DELETE
, and INSERT
acquire this lock mode on the target table (in addition to ACCESS SHARE
locks on any other referenced tables). In general, this lock mode will be acquired by any command that modifies data in a table.