#33516: QuerySet update() under postgres with 
select_for_update(skip_locked=True)
results in neither FOR UPDATE nor SKIP LOCKED
-------------------------------------+-------------------------------------
     Reporter:  Grant Gainey         |                    Owner:  nobody
         Type:  Bug                  |                   Status:  closed
    Component:  Database layer       |                  Version:  3.2
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:  invalid
     Keywords:                       |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------

Comment (by Grant Gainey):

 Ah-ha! Perfect, that explains everything!

 Two comments/suggestions:
 * Could it be possible for select_for_update() to throw an exception when
 it's not going to be emitted? Would have helped me target the problem way
 sooner if the method yelled at me for using it incorrectly
 * Similarly - when called 'correctly', select_for_update() throws an
 exception if you're not in a transaction. When called incorrectly, it
 doesn't. Would be great if the behavior was consistent.

 For posterity's sake, below is my test-script using the suggested changes,
 that works on a 20K table with no deadlocks. Thanks for the pointer!

 {{{
 import statistics
 import time
 from threading import Thread
 from pulpcore.app.models import Content
 from django.db import transaction
 from django.utils.timezone import now

 durations = []

 def update_timestamp(index):

     print(">>>in update_timedstamp index {}".format(index))
     start = time.time()
     with transaction.atomic():
         content_q = (
             Content.objects.filter()
                 .order_by("pk")
                 .select_for_update(skip_locked=True)
                 .values_list("pk", flat=True)
         )
 Content.objects.filter(pk__in=content_q).update(timestamp_of_interest=now())
     end = time.time()
     durations.append(end-start)
     print(">>>done")

 for r in range(10):
     threads = []
     for i in range(10):
         threads.append(Thread(target=update_timestamp, args=(i,)))
     for t in threads:
         t.start()
     for t in threads:
         t.join()

 print("Avg time : {}".format(sum(durations) / len(durations)))
 print("Median time : {}".format(statistics.median(durations)))
 print("StdDev : {}".format(statistics.stdev(durations)))
 }}}

 And the emitted SQL:
 {{{
 2022-02-16 13:46:02.517 UTC [368804] LOG:  statement: UPDATE
 "core_content" SET "timestamp_of_interest" =
 '2022-02-16T13:46:02.516956+00:00'::timestamptz WHERE
 "core_content"."pulp_id" IN (SELECT U0."pulp_id" FROM "core_content" U0
 ORDER BY U0."pulp_id" ASC FOR UPDATE SKIP LOCKED)
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/33516#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/065.d8ff2698b66c51745a1a8208d3fe1444%40djangoproject.com.

Reply via email to