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.

Reply via email to