#32708: Django cron file lock breaks with django 3.2
-------------------------------------+-------------------------------------
     Reporter:  daillouf             |                    Owner:  nobody
         Type:  Bug                  |                   Status:  new
    Component:  Core (Other)         |                  Version:  3.2
     Severity:  Normal               |               Resolution:
     Keywords:  file lock, too many  |             Triage Stage:
  connections, django_cron           |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Description changed by daillouf:

Old description:

> Hello everyone !
>
> After I upgraded django to 3.2, I noticed that I had the following error
> popping up
>
> `django.db.utils.OperationalError: FATAL:  sorry, too many clients
> already`
>
> I then inspected the postgresql connections, and my processes, and it
> turned out, that was my django cron jobs that were running multiple times
> at the same time, thus opening more connections than necessary.
>

>
> [Django cron has a lock mecanism to avoid this,](https://github.com/Tivix
> /django-cron/blob/v0.5.1/django_cron/backends/lock/file.py) that uses the
> lock function from django :
> ` from django.core.files import locks`
>
> But in django3.2 with latest django_cron version, that check totally
> fails and we can run a cron multiple times.  I looked up a bit the code
> to see what's the issue.
>
> In django 3.2 [the code for that lock function is
> ](https://github.com/django/django/blob/main/django/core/files/locks.py)
>

>
> {{{
>         def lock(f, flags):
>             try:
>                 fcntl.flock(_fd(f), flags)
>                 return True
>             except BlockingIOError:
>                 return False
>
>         def unlock(f):
>             fcntl.flock(_fd(f), fcntl.LOCK_UN)
>             return True
> }}}
>

> So  BlockingIOError are catched, and the function then returns false.
>
> [This changes the behavior from 3.1.7 :
> ](https://github.com/django/django/blob/stable/3.1.x/django/core/files/locks.py)
>

> {{{
>
>         def lock(f, flags):
>             ret = fcntl.flock(_fd(f), flags)
>             return ret == 0
>
>         def unlock(f):
>             ret = fcntl.flock(_fd(f), fcntl.LOCK_UN)
>             return ret == 0
>

> }}}
>

> in previous behaviour, checking for «ret==0» was indeed useless, because
> when flock fails, it raises a IOError,
>
> But in 3.1, the lock function didn't catch the error, and django_cron
> made good use of that behaviour.
>
> I believe that lock function is not documented though, so it's up to you
> to decide,
>
> whether it's Django or Django_cron that needs fixing.
>
> have a good day !

New description:

 Hello everyone !

 After I upgraded django to 3.2, I noticed that I had the following error
 popping up

 `django.db.utils.OperationalError: FATAL:  sorry, too many clients
 already`

 I then inspected the postgresql connections, and my processes, and it
 turned out, that was my django cron jobs that were running multiple times
 at the same time, thus opening more connections than necessary.



 [Django cron has a lock mecanism to avoid this,](https://github.com/Tivix
 /django-cron/blob/v0.5.1/django_cron/backends/lock/file.py) that uses the
 lock function from django :
 ` from django.core.files import locks`

 But in django3.2 with latest django_cron version, that check totally fails
 and we can run a cron multiple times.  I looked up a bit the code to see
 what's the issue.

 In django 3.2 [the code for that lock function is
 ](https://github.com/django/django/blob/main/django/core/files/locks.py)



 {{{
         def lock(f, flags):
             try:
                 fcntl.flock(_fd(f), flags)
                 return True
             except BlockingIOError:
                 return False

         def unlock(f):
             fcntl.flock(_fd(f), fcntl.LOCK_UN)
             return True
 }}}


 So  BlockingIOError are catched, and the function then returns false.

 [This changes the behavior from 3.1.7 :
 
](https://github.com/django/django/blob/stable/3.1.x/django/core/files/locks.py)


 {{{

         def lock(f, flags):
             ret = fcntl.flock(_fd(f), flags)
             return ret == 0

         def unlock(f):
             ret = fcntl.flock(_fd(f), fcntl.LOCK_UN)
             return ret == 0


 }}}


 in previous behaviour, checking for «ret==0» was indeed useless, because
 when flock fails, it raises a IOError,

 But in 3.1, the lock function didn't catch the error, and django_cron made
 good use of that behaviour.

 Edit: that problem was spotted at code review but ignored :

 [ https://github.com/django/django/pull/13410#discussion_r624988346]

 have a good day !

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/32708#comment:1>
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/066.5783ca2c48c0d26d8549d59128e9bfa0%40djangoproject.com.

Reply via email to