从 PendingRollbackError 中恢复并允许后续查询

Posted

技术标签:

【中文标题】从 PendingRollbackError 中恢复并允许后续查询【英文标题】:Recover from PendingRollbackError and allow subsequent queries 【发布时间】:2021-11-27 15:10:31 【问题描述】: 我们有一个pyramid 网络应用程序。 我们在 Zope 交易中使用 SQLAlchemy@1.4

在我们的应用程序中,在刷新as described here 期间可能会发生错误,这会导致任何后续使用 SQLAlchemy 会话抛出PendingRollbackError。刷新期间发生的错误是无意的(错误),并被引发到我们的异常处理视图......它尝试使用来自 SQLAlchemy 会话的数据,然后抛出 PendingRollbackError

如果您没有正确构建事务管理,是否可以从PendingRollbackError“恢复”? SQLAclhemy 文档说要避免这种情况,您基本上“只需要以正确的方式做事”。不幸的是,这是一个庞大的代码库,开发人员并不总是遵循正确的事务管理。如果使用保存点/嵌套事务,这个问题也很复杂。

def some_view():
    # constraint violation
    session.add_all([Foo(id=1), Foo(id=1)])
    session.commit()  # Error is raised during flush
    return 'data': 'some data'


def exception_handling_view():  # Wired in via pyramid framework, error ^ enters here.
    session.query(... does a query to get some data)  # This throws a `PendingRollbackError`

我想知道我们是否可以做类似下面的事情,但不了解pyramid + SQLAlchemy + Zope 事务足以了解其含义(考虑嵌套事务等的可能性时)。

def exception_handling_view():  # Wired in via pyramid framework, error ^ enters here.
    def _query():
        session.query(... does a query to get some data)

    try:
        _query()
    except PendingRollbackError:
        session.rollback()
        _query()

【问题讨论】:

是否应该 PendingRollbackError 导致 HTTP 请求重试几次,直到永久失败? @aaron 问题在于它是一个遗留代码库,事务和保存点的使用并不一致——而且新开发人员不知道如何使用它们。此外,异常没有得到正确处理,因此未处理的异常可能导致嵌套事务绕过回滚(如果未使用上下文管理器)。 【参考方案1】:

与其尝试执行查询,不如尝试获取连接:

def exception_handling_view():
    try:
        _ = session.connection()
    except PendingRollbackError:
        session.rollback()

    session.query(...)

session.rollback() 只回滚最里面的事务,正如通常预期的那样——假设嵌套事务是通过显式 session.begin_nested() 有意使用的。

您不必回滚父事务,但如果您决定这样做,您可以:

while session.registry().in_transaction():
    session.rollback()

【讨论】:

以上是关于从 PendingRollbackError 中恢复并允许后续查询的主要内容,如果未能解决你的问题,请参考以下文章

webmin /var/www 文件夹删除,同时删除用户

源码解读抽丝剥茧的分析ViewModel的核心原理

源码解读抽丝剥茧的分析ViewModel的核心原理

redis持久化机制之AOF与RDB

源码解读抽丝剥茧的分析ViewModel的核心原理

从 NIB 与从代码加载自定义滑块:从代码加载时不存在子视图