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 django-users+unsubscr...@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
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