Is "transaction.atomic" same as "transaction.commit_on_success"?

35,335

Solution 1

Yes. You should use atomic in the places where you previously used commit_on_success.

Since the new transaction system is designed to be more robust and consistent, though, it's possible that you could see different behavior. For example, if you catch database errors and try to continue on you will see a TransactionManagementError, whereas the previous behavior was undefined and probably case-dependent.

But, if you're doing things properly, everything should continue to work the same way.

Solution 2

Based on the documentation I have read on the subject, there is a significant difference when these decorators are nested.

Nesting two atomic blocks does not work the same as nesting two commit_on_success blocks.

The problem is that there are two guarantees that you would like to have from these blocks.

  • You would like the content of the block to be atomic, either everything inside the block is committed, or nothing is committed.
  • You would like durability, once you have left the block without an exception you are guaranteed, that everything you wrote inside the block is persistent.

It is impossible to provide both guarantees when blocks are nested. If an exception is raised after leaving the innermost block but before leaving the outermost block, you will have to fail in one of two ways:

  • Fail to provide durability for the innermost block.
  • Fail to provide atomicity for the outermost block.

Here is where you find the difference. Using commit_on_success would give durability for the innermost block, but no atomicity for the outermost block. Using atomic would give atomicity for the outermost block, but no durability for the innermost block.

Simply raising an exception in case of nesting could prevent you from running into the problem. The innermost block would always raise an exception, thus it never promises any durability. But this loses some flexibility.

A better solution would be to have more granularity about what you are asking for. If you can separately ask for atomicity and durability, then you can perform nesting. You just have to ensure that every block requesting durability is outside those requesting atomicity. Requesting durability inside a block requesting atomicity would have to raise an exception.

atomic is supposed to provide the atomicity part. As far as I can tell django 1.6.1 does not have a decorator, which can ask for durability. I tried to write one, and posted it on codereview.

Share:
35,335
Joseph Victor Zammit
Author by

Joseph Victor Zammit

Web aficionado.

Updated on September 01, 2020

Comments

  • Joseph Victor Zammit
    Joseph Victor Zammit almost 4 years

    Django 1.6 proposes @transaction.atomic as part of the rehaul in the transaction management from 1.5.

    I have a function which is called by a Django management command which is in turn called by cron, i.e. no HTTP request triggering transactions in this case. Snippet:

    from django.db import transaction
    
    @transaction.commit_on_success
    def my_function():
        # code here
    

    In the above code block commit_on_success uses a single transaction for all the work done in my_function.

    Does replacing @transaction.commit_on_success with @transaction.atomic result in the identical behaviour? @transaction.atomic docs state:

    Atomicity is the defining property of database transactions. atomic allows us to create a block of code within which the atomicity on the database is guaranteed. If the block of code is successfully completed, the changes are committed to the database. If there is an exception, the changes are rolled back.

    I take it that they result in the same behaviour; correct?

  • Joseph Victor Zammit
    Joseph Victor Zammit about 10 years
    Thanks @kasperd, +1ed for the additional detail in addition to what the first answer provides (which I had marked as correct).
  • Kevin Christopher Henry
    Kevin Christopher Henry almost 10 years
    That's an interesting analysis, but what you're calling durability simply isn't a guarantee offered by SQL databases. The fact that commit_on_success worked the way you describe was a bug (see ticket 2227, opened 8 years ago and only fixed with the introduction of the new savepoint-based transaction system).
  • Kevin Christopher Henry
    Kevin Christopher Henry almost 10 years
    "Nested transactions are implemented differently in different databases. However, they have in common that the changes are not made visible to any unrelated transactions until the outermost transaction has committed. This means that a commit in an inner transaction does not necessary persist updates to the database." (Wikipedia)
  • kasperd
    kasperd almost 10 years
    @KevinChristopherHenry Durability is a fundamental requirement for database transactions to make sense in the first place. A database in which transactions are not guaranteed to be durable, is mostly useless. You are suggesting that django has been using nested transactions in the database itself, which sounds highly dubious to me. As far as I can tell, postgres does not have any support for nesting transactions, and I would consider it a bug if django tried to do it. And as I pointed out, you need to separate the different guarantees to do nesting in the first place.
  • Kevin Christopher Henry
    Kevin Christopher Henry almost 10 years
    You're confusing transactions and blocks. If you define a transaction as something that has both atomicity and durability (in your sense), then nested transactions can't exist, and Django can't support them. But your answer, and my comment, were about nested blocks. Django does support those; it does so by using the standard savepoint feature that exists in all the supported SQL databases; and those nested blocks do not have any durability guarantees. Whether they are persisted to the database depends on the fate of the transaction they are contained within.
  • kasperd
    kasperd almost 10 years
    @KevinChristopherHenry No, I am not confusing blocks and transactions. You were the one to mention transactions, that word is not even in my answer. Any sensible definition of transaction would imply both atomicity and durability. Yes, such constructs cannot be nested, which is also explained in my answer. commit_on_success provided durability for the code block it was applied to, but not atomicity. atomic as implied by the name provides atomicity, but not durability. Apparently there is no longer a way to specify that a block of code need durability.