Skip to content

Problematic rollback semantics with nested transactions #2767

Closed
@rgalanakis

Description

Hi there, thanks for building this library!

I am trying to do something like this:

with db.transaction():
  Thing.create()
  try:
    with db.transaction():
      raise SomeException()
  except SomeException as ex:
    assert len(Thing) == 1
    raise ex

But what's happening is:

  • BEGIN
  • Noop because we're in a transaction
  • INSERT
  • Exception is raised
  • ROLLBACK
  • ROLLBACK

That is, the BEGIN and ROLLBACK are unbalanced.

I believe you are aware this is the case, as you have recommended folks use atomic, which handles this balancing. However I do not want to use atomic, since it must create a savepoint, and I don't want to use savepoints (I find it makes the semantics of the transaction far more difficult to reason about, Python ecosystem seems to disagree but I'm not here to argue).

What I'd humbly suggest/ask is either:

  1. Add support for atomic(savepoint=False), so we can use atomic without savepoints, to get the balanced transaction behavior.

  2. Modify _transaction.__exit__ so it only rolls back if we're dealing with the top transaction, something like:

    def __exit__(self, exc_type, exc_val, exc_tb):
        try:
            if self.db.transaction_depth() == 1:
                try:
                  if exc_type: # This logic was outside the transaction_depth() == 1 check
                    self.rollback(False)
                  else:
                    self.commit(False)
                except:
                    self.rollback(False)
                    raise
        finally:
            self.db.pop_transaction()

I don't believe the current transaction rollback behavior is desirable since it breaks the semantics of context managers by leaking actions across contexts (the inner context manager rolls back the transaction begun by the outer transaction manager).

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions