嵌套的 transaction.atomic() 上下文管理器在外部事务失败时避免回滚

Posted

技术标签:

【中文标题】嵌套的 transaction.atomic() 上下文管理器在外部事务失败时避免回滚【英文标题】:Nested transaction.atomic() context manager avoid rollback when outer transaction fail 【发布时间】:2015-11-11 07:57:20 【问题描述】:

我有以下场景:

@transaction.atomic()
def some_method():
    # some database logic
    a.save()  # Success. Eligible for Rollback.

    with transaction.atomic():
        # some more database logic
        # Success. Shouldn't be rollbacked
        b.save()

    raise Exception

我想要做的是,当我引发异常时,不应该回滚内部事务块的更改,而应该回滚在外部块中完成的更改。

这意味着,回滚 - a.save(),但提交 b.save()。那可能吗?我猜那里的上下文管理器会创建一个保存点。那么,如果内部块成功运行,它不应该提交到保存点吗?在这种情况下它的表现如何?

我读了docs here,上面写着:

原子块可以嵌套。在这种情况下,当一个内部块 成功完成,它的效果可以仍然可以回滚 稍后会在外部块中引发异常。

它说可以回滚,但不是回滚。它到底是什么意思?回滚确定吗?

【问题讨论】:

根据事务的定义,如果事务本身失败,您不能提交b.save()。您需要重新考虑交易/保存点块的结构。 如果包含事务的任何地方出现异常,它回滚。可以这样读:“当一个内部块成功完成时,它的影响仍然可以回滚。具体来说,如果稍后在外部块中引发异常,整个事务将被回滚,包括内部块。 " 【参考方案1】:

首先,由于您将@transaction.atomic 添加到some_method,如果Exception 出现在其中的任何位置,所有内容都将回滚。

现在,如果您移除装饰器并在原子块内提高 Exception,您或多或少会得到您想要的:b 将被提交,a 不会。

def some_method(request):
    b.save()

    with transaction.atomic():
        a.save()
        raise Exception('')

在这种情况下,如果您在块外引发Exception,所有(在Exception 之前)都将被提交(不会在事务内引发Exception

我认为文档摘录是指以下案例:

def funA():
    with transaction.atomic():
        funB()
        #code A

def funB():
    with transaction.atomic():
        #code B

如果在代码A中出现Exception,则代码B中的所有内容都将被回滚。

【讨论】:

以上是关于嵌套的 transaction.atomic() 上下文管理器在外部事务失败时避免回滚的主要内容,如果未能解决你的问题,请参考以下文章

可以在 Django transaction.atomic() 中捕获并重新引发异常吗?

Django - 特定时间后的transaction.atomic回滚

django 信号是不是也包含在 transaction.atomic 装饰器中?

Django - 在引发错误后对transaction.atomic块中的数据库进行操作

Django使用transaction.atomic“死锁”

Django中MySQL事务的使用