Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django select_for_update with nowait=True not raising DatabaseError for conflicting transctions

According to Django docs:

Usually, if another transaction has already acquired a lock on one of the selected rows, the query will block until the lock is released. If this is not the behavior you want, call select_for_update(nowait=True). This will make the call non-blocking. If a conflicting lock is already acquired by another transaction, DatabaseError will be raised when the queryset is evaluated.

I was experimenting with this but found a strange behavior. Below code when run simultaneously doesn't raise the error but works in a blocking manner.

def test4():
with transaction.atomic():
    WorkflowCallContact.objects.select_for_update(nowait=True).filter(id__in=[1,2,3,4]).update(number="22222")
    time.sleep(10)
    list(WorkflowCallContact.objects.filter(id__in=[1,2,3,4]))

To test, I am just opening to shells and calling the same function parallelly. But instead of throwing DatabaseError on the second call because of nowait=True, the call becomes blocked for 10 secs.

Database I'm using: postgres Django version: 1.9

like image 886
priyankvex Avatar asked Jan 24 '26 23:01

priyankvex


1 Answers

select_for_update() and update() can't be chained. You have to force the QuerySet to be evaluated, for example by converting it to a list():

with transaction.atomic():
list(WorkflowCallContact.objects.select_for_update(nowait=True).filter(id__in=[1,2,3,4]))
WorkflowCallContact.objects.filter(id__in=[1,2,3,4]).update(number="22222")
like image 85
Dylan Tack Avatar answered Jan 27 '26 11:01

Dylan Tack



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!