Django嵌套事务 - “with transaction.atomic()” - 寻求澄清

时间:2021-09-30 22:11:45

In Django nested transactions - “with transaction.atomic()” the question is, given this...

在Django嵌套事务中 - “with transaction.atomic()”问题是,鉴于此......

def functionA():
    with transaction.atomic():
        #save something
        functionB()

def functionB():
    with transaction.atomic():
        #save another thing

If functionB fails and rolls back, does functionA roll back too?

如果functionB失败并回滚,functionA也会回滚吗?

Kevin Christopher Henry replies with, "Yes, if an exception happens in either function they will both be rolled back." He then quotes the docs, which state:

凯文克里斯托弗亨利回答说:“是的,如果任何一个功能发生异常,他们都会回滚。”然后他引用了文档,其中说明:

atomic blocks can be nested. In this case, when an inner block completes successfully, its effects can still be rolled back if an exception is raised in the outer block at a later point.

原子块可以嵌套。在这种情况下,当内部块成功完成时,如果稍后在外部块中引发异常,则仍可以回滚其效果。

This documentation quote doesn't seem to address the original question. The doc is saying that when the INNER BLOCK (which is functionB) completes successfully, its effects can still be rolled back if the OUTER block (which is functionA) raises an exception. But the question refers to the opposite scenario. The question asks, if the INNER block (functionB) FAILS, is the OUTER block (functionA) rolled back? This doc quote doesn't address that scenario.

此文档引用似乎没有解决原始问题。文档说当INNER BLOCK(函数B)成功完成时,如果OUTER块(函数A)引发异常,它的效果仍然可以回滚。但问题是相反的情况。问题是,如果INNER块(functionB)FAILS是OUTER块(functionA)回滚?该文档引用并未涉及该场景。

However, further down in the doc we see this example...

但是,在doc中我们看到这个例子......

from django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    create_parent()

    try:
        with transaction.atomic():
            generate_relationships()
    except IntegrityError:
        handle_exception()

    add_children()

...followed by this commentary...

......接着是这篇评论......

In this example, even if generate_relationships() causes a database error by breaking an integrity constraint, you can execute queries in add_children(), and the changes from create_parent() are still there.

在此示例中,即使generate_relationships()通过破坏完整性约束导致数据库错误,您也可以在add_children()中执行查询,并且create_parent()中的更改仍然存在。

If I'm reading the doc correctly it's saying the call to generate_relationships() (which is analogous to the call to functionB in the original question) can FAIL and the changes made in create_parent() and add_children() will be committed to the database. This seems to contradict Kevin Christopher Henry's answer.

如果我正确地阅读了doc,那就是对generate_relationships()的调用(类似于对原始问题中对functionB的调用)可能会失败并且create_parent()和add_children()中的更改将被提交到数据库。这似乎与Kevin Christopher Henry的回答相矛盾。

What's puzzling to me is that I see the same question/answer in Django nested Transaction.atomic.

让我感到困惑的是,我在Django嵌套的Transaction.atomic中看到了相同的问题/答案。

I'm new to both Django and *, so I don't have a lot of confidence in my reading of the doc, but it seems to contradict both of these responses. I'm looking for some clarification from someone more experienced. Thanks you so much.

我是Django和*的新手,所以我对我阅读文档没有很大的信心,但它似乎与这两种反应都相矛盾。我正在寻找更有经验的人的澄清。非常感谢。

2 个解决方案

#1


7  

Here is some pseudo-code with nested transaction blocks and database operations X, Y, and Z:

这是一些具有嵌套事务块和数据库操作X,Y和Z的伪代码:

with transaction.atomic():
    X
    with transaction.atomic():
        Y
    Z

If X raises an exception then obviously none of the operations will get the chance to commit in the first place.

如果X引发异常,那么显然没有任何操作会有机会在第一时间提交。

If Y raises an exception—which was the question you referenced—then the outer block will also roll back. That doesn't have anything to do with nested transactions, per se, it happens because the Python exception bubbles up. The outer block will be exited by an exception, which always causes a rollback. That's true regardless of what caused the exception in the first place.

如果Y引发异常 - 这是您引用的问题 - 那么外部块也将回滚。这与嵌套事务没有任何关系,本身就是因为Python异常冒泡而发生。外部块将由异常退出,该异常总是导致回滚。无论最初导致异常的原因是什么都是如此。

The non-obvious case is what happens when Z raises an exception, and that's why the documentation calls it out for special attention. As referenced, both X and Y will be rolled back:

不明显的情况是当Z引发异常时会发生什么,这就是为什么文档要求它特别注意的原因。如上所述,X和Y都将回滚:

When an inner block completes successfully, its effects can still be rolled back if an exception is raised in the outer block at a later point.

当内部块成功完成时,如果稍后在外部块中引发异常,则仍可以回滚其效果。

Now, it's also possible to catch the exception raised by the nested operation.

现在,还可以捕获嵌套操作引发的异常。

with transaction.atomic():
    X
    try:
        with transaction.atomic():
            Y
    except IntgrityError:
        pass
    Z

In this case, if Y throws an exception the inner block will be rolled back (because it exits with an exception) but the outer block won't (because it doesn't).

在这种情况下,如果Y抛出异常,内部块将被回滚(因为它以异常退出)但外部块不会(因为它没有)。

That doesn't contradict the information in either of the two answers you mentioned since those addressed specific questions (with code examples) that didn't involve catching any exceptions.

这与您提到的两个答案中的任何一个中的信息都不矛盾,因为那些答案解决了特定问题(使用代码示例),这些问题不涉及任何例外情况。

In any case, thanks for the feedback and the chance to give a more comprehensive answer.

无论如何,感谢您提供反馈并有机会提供更全面的答案。

#2


1  

Second example is catching IntegrityError so outer transaction atomic wont be aware of the following error occurring

第二个例子是捕获IntegrityError,因此外部事务原子不会意识到发生以下错误

Wrapping atomic in a try/except block allows for natural handling of integrity errors

在try / except块中包装原子允许自然处理完整性错误

Which basically says if you want following block not to raise outer transaction rollback just try/catch integrity errors

这基本上说如果你想跟随块不提出外部事务回滚只是尝试/捕获完整性错误

As you already stated the following from documentation

正如您已从文档中说明以下内容

atomic blocks can be nested. In this case, when an inner block completes successfully, its effects can still be rolled back if an exception is raised in the outer block at a later point.

原子块可以嵌套。在这种情况下,当内部块成功完成时,如果稍后在外部块中引发异常,则仍可以回滚其效果。

So by default rollback will happen as error will propagate, and second example is a way to silence outer transaction rollback

因此,默认情况下,回滚将在错误传播时发生,第二个示例是一种静默外部事务回滚的方法

#1


7  

Here is some pseudo-code with nested transaction blocks and database operations X, Y, and Z:

这是一些具有嵌套事务块和数据库操作X,Y和Z的伪代码:

with transaction.atomic():
    X
    with transaction.atomic():
        Y
    Z

If X raises an exception then obviously none of the operations will get the chance to commit in the first place.

如果X引发异常,那么显然没有任何操作会有机会在第一时间提交。

If Y raises an exception—which was the question you referenced—then the outer block will also roll back. That doesn't have anything to do with nested transactions, per se, it happens because the Python exception bubbles up. The outer block will be exited by an exception, which always causes a rollback. That's true regardless of what caused the exception in the first place.

如果Y引发异常 - 这是您引用的问题 - 那么外部块也将回滚。这与嵌套事务没有任何关系,本身就是因为Python异常冒泡而发生。外部块将由异常退出,该异常总是导致回滚。无论最初导致异常的原因是什么都是如此。

The non-obvious case is what happens when Z raises an exception, and that's why the documentation calls it out for special attention. As referenced, both X and Y will be rolled back:

不明显的情况是当Z引发异常时会发生什么,这就是为什么文档要求它特别注意的原因。如上所述,X和Y都将回滚:

When an inner block completes successfully, its effects can still be rolled back if an exception is raised in the outer block at a later point.

当内部块成功完成时,如果稍后在外部块中引发异常,则仍可以回滚其效果。

Now, it's also possible to catch the exception raised by the nested operation.

现在,还可以捕获嵌套操作引发的异常。

with transaction.atomic():
    X
    try:
        with transaction.atomic():
            Y
    except IntgrityError:
        pass
    Z

In this case, if Y throws an exception the inner block will be rolled back (because it exits with an exception) but the outer block won't (because it doesn't).

在这种情况下,如果Y抛出异常,内部块将被回滚(因为它以异常退出)但外部块不会(因为它没有)。

That doesn't contradict the information in either of the two answers you mentioned since those addressed specific questions (with code examples) that didn't involve catching any exceptions.

这与您提到的两个答案中的任何一个中的信息都不矛盾,因为那些答案解决了特定问题(使用代码示例),这些问题不涉及任何例外情况。

In any case, thanks for the feedback and the chance to give a more comprehensive answer.

无论如何,感谢您提供反馈并有机会提供更全面的答案。

#2


1  

Second example is catching IntegrityError so outer transaction atomic wont be aware of the following error occurring

第二个例子是捕获IntegrityError,因此外部事务原子不会意识到发生以下错误

Wrapping atomic in a try/except block allows for natural handling of integrity errors

在try / except块中包装原子允许自然处理完整性错误

Which basically says if you want following block not to raise outer transaction rollback just try/catch integrity errors

这基本上说如果你想跟随块不提出外部事务回滚只是尝试/捕获完整性错误

As you already stated the following from documentation

正如您已从文档中说明以下内容

atomic blocks can be nested. In this case, when an inner block completes successfully, its effects can still be rolled back if an exception is raised in the outer block at a later point.

原子块可以嵌套。在这种情况下,当内部块成功完成时,如果稍后在外部块中引发异常,则仍可以回滚其效果。

So by default rollback will happen as error will propagate, and second example is a way to silence outer transaction rollback

因此,默认情况下,回滚将在错误传播时发生,第二个示例是一种静默外部事务回滚的方法