Opened 5 years ago

Last modified 4 years ago

#30375 closed Cleanup/optimization

Use "NO KEY" when doing select_for_update for PostgreSQL — at Version 1

Reported by: Manuel Weitzman Owned by: nobody
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords: postgres, lock, database, operation
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Manuel Weitzman)

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.

Change History (1)

comment:1 by Manuel Weitzman, 5 years ago

Description: modified (diff)
Version: 1.112.2
Note: See TracTickets for help on using tickets.
Back to Top