Django best practices - ensuring atomicity while executing business logic

Viewed 278

I am refactoring some legacy code on a Python/Django application and I'm looking for a styling/best practices recommendation about performing actions on a SQL Database.

Right now I am trying to rework the following code:

def fun():
  try:
    with transaction.atomic():
      # query DB

      # do business logic

      # update DB entries with new values
  except:
    # handle exception

The thing is, there is a fair amount of business logic between my query and my update. However I feel like it doesn't make sense to throw additional code under another indentation/i.e defining as part of a DB transaction. Ideally I'd like to clean this up and only perform certain things as atomic. I suppose it wouldn't actually cut down on my lines of code, but I'm curious what transaction.atomic() is actually accomplishing?

From my understanding of atomicity, reads don't actually affect the state of a datastore, so I'm not entirely sure the initial read belongs under the atomic() anyway.

thanks in advance.

1 Answers

If the reads are just plain SELECT statements, and you are using the default READ COMMITTED transaction isolation level, and the update is a single SQL statement, you are right.

But:

  • If there are more than one data modification statements (say, you modify rows in more than one table), it may be essential to use a transaction, so that it can never happen that only one of these statements is executed.

    In that case it would be best to start the transaction right before you modify the data.

  • With transaction isolation level REPEATABLE READ or above, it is essential to include the SELECT statements in the transaction, because the “snapshot” of the database (the state of the database that applied to the transaction) is taken when the first SQL statement in the transaction is executed. You want your UPDATE so see the same state of the database as your SELECT.

  • If the SELECT has a FOR UPDATE clause, this actually locks table rows to prevent concurrent modifications. In that case you need to have the SELECT in the same transaction as the data modification, because locks are released when a transaction ends.

That all said, it is best to keep database transactions as short as possible. That reduces the risk of deadlocks and makes schema changes painless.

Related