Database updates without race conditions

You might come across situations where updating a shared state is unavoidable. You can use row-level locks if your database supports it or Django F() objects. Notably, MySQL using MyISAM engine does not have support for row-level locks.

Row-level locks are done in Django by calling select_for_update() on your QuerySet within a transaction. Consider this example:

with transaction.atomic(): 
    feed = Feed.objects.select_for_update().get(id=id) 
    feed.html = sanitize(feed.html) 
    feed.save() 

By using select_for_update, we lock the Feed object's row until the transaction is done. If another thread or process has already locked the same row, the query will be waiting or blocked until the lock is freed. This behavior can be changed to throw an exception or skip it if locked, using the select_for_update keyword parameters.

If the operation on the field can be done within the database using SQL, it is better to use F() expressions to avoid a race condition. F() expressions avoid the need to pull the value from the database to Python memory and back. Consider the following instance:

from django.db.models import F 
 
feed = Feed.objects.get(id=id) 
feed.subscribers = F('subscribers') + 1 
feed.save() 

It is only when the save() operation is performed that the increment operation is converted to an SQL expression and executed within the database. At no point is the number of feed subscribers retrieved from the database. As the database updates the new value based on the old, there is hardly a chance for a race condition between multiple threads.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.136.18.48