#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 [email protected].
To post to this group, send email to [email protected].
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