Hi Carsten,
Did you try using select_for_update() with get_or_create()[1] in an
atomic()[2] context?
@transation.atomic
def demonstrate_the_problem():
d = date.today()
t = TestModel.objects.select_for_update().get_or_create(
jahr=d.year, monat=d.month
)
# ... long `some_value` computation
t.some_value = 123
t.save(update_fields={'some_value'})
return t
Note that in this case if another thread tries to select_for_update() it is
going to block at the get_of_create() until the first thread's transaction
commits.
If you'd like to prevent other threads from blocking you might want to use
the
`nowait` option of select_for_update() and catch the `OperationalError` that
might be raised in order to return something else.
Cheers,
Simon
[1]
https://docs.djangoproject.com/en/1.9/ref/models/querysets/#get-or-create
[2]
https://docs.djangoproject.com/en/1.9/topics/db/transactions/#django.db.transaction.atomic
Le mercredi 11 mai 2016 09:44:12 UTC-4, Carsten Fuchs a écrit :
>
> Dear Django group,
>
> please consider this code:
>
>
> from datetime import date
> from django.db import models
>
>
> class TestModel(models.Model):
> jahr = models.SmallIntegerField()
> monat = models.SmallIntegerField()
> some_value = models.SmallIntegerField()
>
> class Meta:
> unique_together = ('jahr', 'monat')
>
>
> def demonstrate_the_problem():
> d = date.today()
>
> try:
> t = TestModel.objects.get(jahr=d.year, monat=d.month)
> # t exists, no need to create or modify it.
> return t.some_value
> except TestModel.DoesNotExist:
> # t did not yet exist, so we have to create it anew.
> # Note that there is a "unique together" constraint in place
> # that makes sure that the tuple (jahr, monat) only exists
> once.
> # Thus we create a new instance, then lock it with
> # select_for_update() -- but this is still not atomic!
> TestModel(jahr=d.year, monat=d.month).save()
> t = TestModel.objects.get(
> jahr=d.year, monat=d.month).select_for_update()
>
> # A long computation, eventually setting fields in the new t,
> # then saving it for the next call to this function.
> t.some_value = 123
> t.save()
> return t.some_value
>
>
> The problem is that another thread too may have created a TestModel with
> the
> same (jahr, monat) in the timespan between our "does not exist" and
> "lock",
> triggering a violation of the "unique" constraint.
>
> Thus, the question is how we can make the sequence "does not exist –
> create anew
> – lock" atomic?
>
> Any feedback would very much be appreciated!
>
> Many thanks and best regards,
> Carsten
>
>
--
You received this message because you are subscribed to the Google Groups
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-users/433d0f20-513c-4e3d-9e41-777e3bdc8407%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.