【问题标题】:Why do I get SQLAlchemy nested rollback error?为什么我会收到 SQLAlchemy 嵌套回滚错误?
【发布时间】:2012-02-10 19:35:15
【问题描述】:

我的 python 代码中出现如下错误(它收集 twitter 状态并存储在数据库中)。

sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back by a nested rollback() call.  To begin a new transaction, issue Session.rollback() first.

我想知道问题是什么,为什么会出现,我该如何解决。

我不知道nested rollback。有没有发生nested rollback的简单例子?

【问题讨论】:

  • 因为这还没有发布答案,但它现在在问题中:总结是利用一个try-except块,你的session.commit()try和一个@ 987654326@ 在异常情况下。 @fbessho,您真的应该发布您的编辑作为答案。
  • @bsplosion 感谢您的建议。我将编辑部分作为答案。请注意,我还更改了问题标题,因为原来的“SQLAlchemy 嵌套回滚错误”不符合堆栈溢出的当前要求。

标签: python sqlalchemy


【解决方案1】:

问题解决了。 在这种情况下,关键是在我们显式调用回滚之前不会执行回滚,所以当我们包含commit()时,我们应该将它写在try语句中,并将rollback()写在异常语句中(大多数情况下)写在https://docs.sqlalchemy.org/en/13/faq/sessions.html#this-session-s-transaction-has-been-rolled-back-due-to-a-previous-exception-during-flush-or-similar

而且,这是正确的代码示例。我从上面的链接中引用了这个。

try:
    <use session>
    session.commit()
except:
    session.rollback()
    raise
finally:
    session.close()  # optional, depends on use case

【讨论】:

  • 不使用此代码,我仍然得到同样的错误
  • 我不得不删除数据库并让烧瓶再次生成它,然后它就可以工作了
【解决方案2】:

正如上面@fbessho 所指出的,这确实是正确的模式:

try:
    <use session>
    session.commit()
except:
    session.rollback()

但是,有些细微之处可能会破坏错误处理。

在这个例子中(一个虚构的唯一约束违反),回滚不会发生:

class Thing1(Base):
   id = Column(BigInteger, primary_key=True)


class Thing2(Base):
   id = Column(BigInteger, primary_key=True)


def do_something(s: session, thing_1: Thing1, duplicate_id):

   # imagine this violates a unique constraint on Thing2
   thing_2 = Thing2(id=duplicate_id)
   s.add(thing_2)

   try:
       # the exception will occur when the commit statement is executed
       s.commit()
   except Exception as ex:
       # this will log details of the exception
       logger.error(f"{ex.__class__.__name__}: {ex}")
       # referencing thing_1.id will raise a second exception
       logger.error(f"Commit failed.  Thing1 id was {thing_1.id}.")
       s.rollback()

即使 thing_1 与失败的插入无关,也会发生第二个异常。仅引用 thing_1 会引发第二个异常,从而阻止执行回滚。

解决方案 1

这需要更多的开销,但总能奏效。

def do_something_1(s: session, thing_1: Thing1, duplicate_id):

   # create a reference that does not rely on the data object
   id_for_thing = thing_1.id

   # imagine this violates a unique constraint on Thing2
   thing_2 = Thing2(id=duplicate_id)
   s.add(thing_2)

   try:
       # the exception will occur when the commit statement is executed
       s.commit()
   except Exception as ex:
       logger.error(f"{ex.__class__.__name__}: {ex}")
       # no direct reference to thing_1
       logger.error(f"Commit failed.  Thing1 id was {id_for_thing}.")
       s.rollback()

解决方案 2

只要 thing_1 不受回滚影响,这将起作用。

def do_something_2(s: session, thing_1: Thing1, duplicate_id):

   # imagine this violates a unique constraint on Thing2
   thing_2 = Thing2(id=duplicate_id)
   s.add(thing_2)

   try:
       # the exception will occur when the commit statement is executed
       s.commit()
   except Exception as ex:
       logger.error(f"{ex.__class__.__name__}: {ex}")
       s.rollback()
       # thing_1.id can be referenced after rollback
       logger.error(f"Commit failed.  Thing1 id was {thing_1.id}.")

【讨论】:

    猜你喜欢
    • 2020-11-05
    • 2020-11-05
    • 2018-06-22
    • 2020-01-12
    • 2016-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多