Opened 10 years ago

Last modified 10 years ago

#23074 closed Bug

django.db.transaction.atomic() leaks savepoints on exception — at Version 2

Reported by: hyperair@… Owned by: Aymeric Augustin
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description (last modified by Aymeric Augustin)

When savepoints are rolled back after an exception in a transaction.atomic() block, the savepoint is leaked. Specifically, "ROLLBACK TO SAVEPOINT xx" is done, but not "RELEASE SAVEPOINT xx". The savepoint ID is then thrown away by Django and leaked.

See http://www.postgresql.org/docs/9.1/static/sql-release-savepoint.html and http://www.postgresql.org/docs/9.1/static/sql-rollback-to.html for more information.

When spinning in a get_or_create() loop in a single transaction, this has the potential to max out the shared memory of the PostgreSQL instance resulting in this:

  File "/usr/lib/python2.7/dist-packages/django/db/models/manager.py", line 157, in create
    return self.get_queryset().create(**kwargs)
  File "/usr/lib/python2.7/dist-packages/django/db/models/query.py", line 319, in create
    obj.save(force_insert=True, using=self.db)
  File "/usr/lib/python2.7/dist-packages/django/db/models/base.py", line 545, in save
    force_update=force_update, update_fields=update_fields)
  File "/usr/lib/python2.7/dist-packages/django/db/models/base.py", line 573, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/usr/lib/python2.7/dist-packages/django/db/models/base.py", line 654, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/usr/lib/python2.7/dist-packages/django/db/models/base.py", line 687, in _do_insert
    using=using, raw=raw)
  File "/usr/lib/python2.7/dist-packages/django/db/models/manager.py", line 232, in _insert
    return insert_query(self.model, objs, fields, **kwargs)
  File "/usr/lib/python2.7/dist-packages/django/db/models/query.py", line 1511, in insert_query
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/usr/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 898, in execute_sql
    cursor.execute(sql, params)
  File "/usr/lib/python2.7/dist-packages/django/db/backends/util.py", line 69, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "/usr/lib/python2.7/dist-packages/django/db/backends/util.py", line 53, in execute
    return self.cursor.execute(sql, params)
  File "/usr/lib/python2.7/dist-packages/django/db/utils.py", line 99, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/usr/lib/python2.7/dist-packages/django/db/backends/util.py", line 53, in execute
    return self.cursor.execute(sql, params)
django.db.utils.OperationalError: out of shared memory
HINT:  You might need to increase max_locks_per_transaction.

Change History (3)

by Chow Loong Jin <hyperair@…>, 10 years ago

Patch to call savepoint_commit after savepoint_rollback in Atomic.exit

comment:1 by Chow Loong Jin <hyperair@…>, 10 years ago

Has patch: set

comment:2 by Aymeric Augustin, 10 years ago

Description: modified (diff)
Owner: changed from nobody to Aymeric Augustin
Status: newassigned
Note: See TracTickets for help on using tickets.
Back to Top