Opened 3 hours ago

Last modified 3 hours ago

#37406 new Uncategorized

BaseDatabaseSchemaEditor.__exit__ doesn't exit its atomic block when deferred SQL raises — at Version 1

Reported by: Gavin Wahl(vendor) Owned by:
Component: Database layer (models, ORM) Version: 6.1
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Gavin Wahl(vendor))

BaseDatabaseSchemaEditor (normally accessed through connection.schema_editor) implements the context manager protocol and wraps transaction.atomic() in its __enter__ and __exit__. In __exit__, it runs the deferred SQL and then calls self.atomic.__exit__. If the deferred SQL raises, the second call never happens and the atomic block stays open.

After that the connection is stuck. in_atomic_block is True with no atomic block on the stack, so nothing will ever commit. Once anything sets needs_rollback you get TransactionManagementError on every query. close() doesn't help because inside an atomic block it keeps the dead connection object around and waits for an Atomic.__exit__ to reset it.

This doesn't affect manage.py migrate from a shell, since the process exits and the server rolls back. It matters if you catch the error and keep going, e.g. tests, call_command("migrate"), or a tool using schema_editor directly.

There is an existing test_migrations_not_applied_on_deferred_sql_failure but it has atomic = False so it doesn't hit this.

Minimal reproducer attached.

Related #29721

Change History (2)

by Gavin Wahl(vendor), 3 hours ago

Attachment: deferred_fail_atomic.py​ added

comment:1 by Gavin Wahl(vendor), 3 hours ago

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