#30375: Use "NO KEY" when doing select_for_update for PostgreSQL
-------------------------------------+-------------------------------------
     Reporter:  mudetz               |                    Owner:  nobody
         Type:                       |                   Status:  new
  Cleanup/optimization               |
    Component:  Database layer       |                  Version:  2.2
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:  postgres, lock,      |             Triage Stage:
  database, operation                |  Unreviewed
    Has patch:  1                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Changes (by mudetz):

 * version:  1.11 => 2.2


Old description:

> Currently Django compiles select for update into one of
> - FOR UPDATE
> - FOR UPDATE NOWAIT
> - FOR UPDATE SKIP LOCKED
>
> All of these acquire a lock which conflicts with `KEY SHARE` locks. This
> means that updates on tables which references a row locked by `FOR
> UPDATE` will have to wait until the lock is released. This is to avoid
> any unexpected changes on `PRIMARY KEY` fields but, **in Django, primary
> key fields are read-only, so locking them makes no sense**.
>
> There is no need to acquire these kind of locks, instead, we should (at
> least be able to) use
> - FOR NO KEY UPDATE
> - FOR NO KEY UPDATE NOWAIT
> - FOR NO KEY UPDATE SKIP LOCKED
>
> which can be easily done by overriding BaseOperation.for_update_sql in
> Postgres DatabaseOperation
>

> {{{
> def for_update_sql(self, nowait=False, skip_locked=False):
>     """
>     Returns the FOR UPDATE SQL clause to lock rows for an update
> operation.
>     """
>     if nowait:
>         return 'FOR NO KEY UPDATE NOWAIT'
>     elif skip_locked:
>         return 'FOR NO KEY UPDATE SKIP LOCKED'
>     else:
>         return 'FOR NO KEY UPDATE'
> }}}
>
> Not doing so causes lock contention in our use case.

New description:

 Currently Django compiles select for update into one of
 - FOR UPDATE
 - FOR UPDATE NOWAIT
 - FOR UPDATE SKIP LOCKED

 All of these acquire a lock which conflicts with `KEY SHARE` locks. This
 means that updates on tables which references a row locked by `FOR UPDATE`
 will have to wait until the lock is released. This is to avoid any
 unexpected changes on `PRIMARY KEY` fields but, **in Django, primary key
 fields are read-only, so locking them makes no sense**.

 There is no need to acquire these kind of locks, instead, we should (at
 least be able to) use
 - FOR NO KEY UPDATE
 - FOR NO KEY UPDATE NOWAIT
 - FOR NO KEY UPDATE SKIP LOCKED

 which can be easily done by overriding BaseOperation.for_update_sql in
 Postgres DatabaseOperation

 For 1.11
 {{{
 def for_update_sql(self, nowait=False, skip_locked=False):
     """
     Returns the FOR UPDATE SQL clause to lock rows for an update
 operation.
     """
     if nowait:
         return 'FOR NO KEY UPDATE NOWAIT'
     elif skip_locked:
         return 'FOR NO KEY UPDATE SKIP LOCKED'
     else:
         return 'FOR NO KEY UPDATE'
 }}}

 For 2.2

 {{{
 def for_update_sql(self, nowait=False, skip_locked=False, of=()):
     """
     Return the FOR UPDATE SQL clause to lock rows for an update operation.
     """
     return 'FOR NO KEY UPDATE%s%s%s' % (
         ' OF %s' % ', '.join(of) if of else '',
         ' NOWAIT' if nowait else '',
         ' SKIP LOCKED' if skip_locked else '',
     )
 }}}


 Not doing so causes lock contention in our use case.

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30375#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/064.6fb0ede73bda3aff2142e8a991d96584%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to