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.