嵌套的 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块中的数据库进行操作