Hi Simon,
that's awesome! This problem has been bothering me for a long time because I
never quite understood how to get the locking / blocking and the transactions right.
Your's was a huge help, thank you very much for it!
:-)
Best regards,
Carsten
Am 12.05.2016 um 17:10 schrieb Simon Charette:
Hi Carsten,
> Why will the other thread block?
> (Both threads may enter the "create" case, so the select_for_update() may not
> yet be effective for the other thread?)
> I looked into get_or_create()'s source code and the
_create_object_from_params()
> method that it calls. Is this due to the "second"
> return self.get(**lookup), False
> near its end?
Exactly. Only one thread will succeed in creating the object. The other one will
get an `IntegrityError` and try to `.get()` the existing object which is going
to use `select_for_update(nowait=False)`-- a blocking call.
> Also, I understand the purpose of wrapping demonstrate_the_problem() in
> atomic(), accounting for possibly unrelated exceptions in "long `some_value`
> computation". But why does _create_object_from_params() wrap its single call
to
> `create()` in atomic(), too? Isn't create() inherently atomic?
The create() method is atomic but in order to recover from an integrity error
it could raise on conflictual data it must be wraped in a transaction
(if autocommit is on) or use a savepoint in your case because a transaction
is already started by the demonstrate_the_problem() atomic wrapper. Else the
connection is left in an unusable state by the integrity error.
Cheers,
Simon
Le jeudi 12 mai 2016 10:48:30 UTC-4, Carsten Fuchs a écrit :
Hi Simon,
many thanks for your reply!
Please see below for some follow-up questions.
Am 11.05.2016 um 16:04 schrieb Simon Charette:
> 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.
Why will the other thread block?
(Both threads may enter the "create" case, so the select_for_update() may
not
yet be effective for the other thread?)
I looked into get_or_create()'s source code and the
_create_object_from_params()
method that it calls. Is this due to the "second"
return self.get(**lookup), False
near its end?
Also, I understand the purpose of wrapping demonstrate_the_problem() in
atomic(), accounting for possibly unrelated exceptions in "long `some_value`
computation". But why does _create_object_from_params() wrap its single
call to
`create()` in atomic(), too? Isn't create() inherently atomic?
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/5734A9DA.1010100%40cafu.de.
For more options, visit https://groups.google.com/d/optout.