Race conditions in django
database, django, locking, python, race-condition
Solution
Django 1.4+ supports select_for_update, in earlier versions you may execute raw SQL queries e.g. `select ... for update` which depending on underlying DB will lock the row from any updates, you can do whatever you want with that row until the end of transaction. e.g.
from django.db import transaction
@transaction.commit_manually()
def add_points(request):
user = User.objects.select_for_update().get(id=request.user.id)
# you can go back at this point if something is not right
if user.points > 1000:
# too many points
return
user.points += calculate_points(user)
user.save()
transaction.commit()
Problem
Here is a simple example of a django view with a potential race condition: ``` # myapp/views.py from django.contrib.auth.models import User from my_libs import calculate_points def add_points(request): user = request.user user.points += calculate_points(user) user.save() ``` The race condition should be fairly obvious: A user can make this request twice, and the application could potentially execute `user = request.user` simultaneously, causing one of the requests to override the other. Suppose the function `calculate_points` is relatively complicated, and makes calculations based on all kinds of weird stuff that cannot be placed in a single `update` and would be difficult to put in a stored procedure. So here is my question: What kind of locking mechanisms are available to django, to deal with situations similar to this?