Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this a safe way to increment and get the value of a counter in Django?

I'm using MySQL with the InnoDB engine and REPEATABLE-READ isolation level.

I have written a function which I think should atomically increment an IntegerField, and give me the value after the increment. I would like opinions on whether my code is robust against concurrency issues.

My code looks like this:

class MyModel(models.Model):
  version = models.IntegerField()

  @staticmethod
  @transaction.commit_on_success
  def acquire_version(pk):
    MyModel.objects.filter(pk = pk).update(version = F('version') + 1)
    return MyModel.objects.get(pk = pk).version

My thinking is that in two concurrent calls, the UPDATEs will mutually exclude one another because of a write lock, and then REPEATABLE-READ should guarantee that my subsequent .get will give me the value after the UPDATE. Am I right?

(This is not a general "how do I do an atomic increment?" question, there's already one of those. This is about whether this one particular way is valid.)

like image 842
kdt Avatar asked Aug 22 '11 14:08

kdt


1 Answers

A general rule-of-thumb is that any sort of custom queries or customized query behavior should go into manager methods. This makes sense in your case, because you can increment, save and return the version number at a very low level: during the actual query.

Thus, use a manager (object = MyManager()) and write a SQL query that increments the version number (UPDATE mytable SET version=(version+1) WHERE pk=pk, see here), and immediately return the incremented version of the model instance, before any other call can be executed.

See also Managers

like image 52
Remi Avatar answered Nov 10 '22 04:11

Remi