I'm a bit confused about how I should handle transactions in a particular situation.
I've got some code that boils down to this:
from django.db import transaction
@transaction.commit_on_success
def process_post():
#do stuff with database
for reply in post_replies:
process_post_reply(reply)
@transaction.commit_on_success
def process_post_reply(reply):
#do stuff with database
I want to know what happens if a process_post_reply()
fails.
How does commit_on_success handle being nested? Will it understand to commit each process_post_reply()
or if one fails the whole process_post()
rolls back?
Django provides a single API to control database transactions. 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.
This marks a point in the transaction that is known to be in a “good” state. Returns the savepoint ID (sid). Updates the savepoint to include any operations that have been performed since the savepoint was created, or since the last commit.
Django's transaction. on_commit() allows you to run a function after the current database transaction is committed.
Django provides a single API to control database transactions. 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.
Here's the source code of it: https://github.com/django/django/blob/1.2.4/django/db/transaction.py#L286
And enter_transaction_management is as simple as putting new transaction handling mode on the thread stack.
So, in your case, if process_post_reply()
fails (i.e. exception occurs), then the transaction is rolled back in its entirety, and then the exception propagates upwards from process_post()
as well but there is nothing to roll back.
And no, if one process_post_reply()
fails then the whole process_post()
is not being rolled back - there's no magic there, only COMMIT and ROLLBACK on the database level, which means that what gets rolled back is only what has been written to the DB after the last commited process_post_reply()
.
Summarizing, I think that what you need is just a single commit_on_success()
around process_post
, possibly supported by transaction savepoints - which unfortunately are available only in PostgreSQL backend, even though MySQL 5.x supports them as well.
EDIT 10 Apr 2012: Savepoint support for MySQL is now available in Django 1.4
EDIT 2 Jul 2014: Transaction management has been completely rewritten in Django 1.6 - https://docs.djangoproject.com/en/1.6/topics/db/transactions/ and commit_on_success
has been deprecated.
To gain more control on the transaction management, it's good to use transaction.commit_manually()
:
@transaction.commit_on_success
def process_post(reply):
do_stuff_with_database()
for reply in post_replies:
process_post_reply(transaction_commit_on_success=False)
def process_post_reply(reply, **kwargs):
if kwargs.get('transaction_commit_on_success', True):
with transaction.commit_manually():
try:
do_stuff_with_database()
except Exception, e:
transaction.rollback()
raise e
else:
transaction.commit()
else:
do_stuff_with_database()
Here you can decide depending on circumstances, commit transaction or not.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With