Search code examples
pythondjangotransactionsdjango-database

What types of requests make good candidate for `@transaction.non_atomic_requests`?


A common way to handle transactions on the web is to wrap each request in a transaction. In Django, you can set ATOMIC_REQUESTS to True in the configuration of each database for which you want to enable this behavior.

It works like this. Before calling a view function, Django starts a transaction. If the response is produced without problems, Django commits the transaction. If the view produces an exception, Django rolls back the transaction.

While the simplicity of this transaction model is appealing, it also makes it inefficient when traffic increases. Opening a transaction for every view has some overhead.

For requests that do not need to be wrapped in transactions, you can apply the @transaction.non_atomic_requests decorator.

Given a Django view.py with several View classes and request methods. How might I go about deciding which requests would make good candidates for the non-atomic decorator?

What "gotchas" might be lurking?

I could see POST methods not making good candidates or anything with a .save(), but what else should I look out for?


Solution

  • Unfortunately I don't think there are many hints, gotchas, and so forth that are generally applicable. The question is just: does this sequence of operations require a database transaction to work correctly, given all the database operations I might perform? If the answer is yes (or you're unsure), use a transaction; if not, don't.

    It's a very application-specific question. Taking your example, there are many situations where a save() will require a transaction and many where it won't. I don't know of any substitute to just sitting down and thinking through the possibilities.

    The most important thing is to understand database transactions well, particularly isolation levels. People often wrongly think that they're safe from some concurrency problem because their mental model of transactions matches the SERIALIZED isolation level, when in fact their operations are using READ COMMITTED (the Django default).

    It's also important to understand and consider the other available tools you can use in addition to plain transactions, like locks (e.g. Django's select_for_update()) and optimistic concurrency.