Dear Django group,

we try to understand and solve a concurrency problem, and would kindly like to ask for your help.

We use Django 1.4, and a model like this:

    class MonatsSalden(models.Model):
        id    = models.AutoField(primary_key=True)
        key   = models.ForeignKey(Mitarbeiter)
        jahr  = models.SmallIntegerField()
        monat = models.SmallIntegerField()
        saldo = models.DecimalField(max_digits=10, decimal_places=2)

        class Meta:
            unique_together = ('key', 'jahr', 'monat')


This is our code (for clarity, reduced to the essentials):

    try:
        CE = MonatsSalden.objects.get(jahr=Jahr, monat=Monat)

        if meets_requirements(CE):
            return CE
    except MonatsSalden.DoesNotExist:
        CE = MonatsSalden(key=self, jahr=Jahr, monat=Monat)

    ### Long and expensive computation to set the other fields of CE...
    ### (after it, meets_requirements(CE) returns True)
    ### ...

    CE.save()
    return CE


The code essentially implements the query of a cache ("CE" == "cache entry"), and returns the cached entry if it is not yet expired ("meets_requirements"), or else completes and updates the cache.

The problem is that the long and expensive computation that is indicated with the ### comment lines can run long enough so that another view request can enter the same code in the meanwhile.

As a result, CE.save() can be called twice. (If CE was created anew, following the DoesNotExist exception, only the unique_together constraint prevents that duplicate rows are inserted, and raises a nice database error exception instead.)


To fix this, our plan was to first enable the TransactionMiddleware, because as we understand it, using select_for_update() is not very useful with the default open transaction behaviour, because the auto-commit that follows the end of the query immediately expires the locks. (Is this right??)

With the TransactionMiddleware, we would next add select_for_update():

CE = MonatsSalden.objects.select_for_update().get(jahr=Jahr, monat=Monat)

The intention is that if the code is re-entered in another process, it has to wait until the first process has finished dealing with its CE.

It seems like things work well when the DoesNotExist exception *not* occurs, because CE is locked, eventually returned, the view request is elsewhere soon completed, the transaction committed, and things are ok.

But... how does it behave when DoesNotExist occurs?

Will the previously established lock cover the newly created CE object even though it did not exist at the time of the select_for_update(), or not?

Many thanks in advance, any help is very much appreciated!
:-)

Best regards,
Carsten



--
   Cafu - the open-source Game and Graphics Engine
for multiplayer, cross-platform, real-time 3D Action
          Learn more at http://www.cafu.de

--
You received this message because you are subscribed to the Google Groups "Django 
users" group.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.

Reply via email to