Opened 2 months ago

Last modified 4 weeks ago

#35757 closed Bug

memcached.PyMemcacheCache reentrancy problem with ASGI-based runserver — at Version 6

Reported by: Harm Verhagen Owned by:
Component: Core (Cache system) Version: 5.1
Severity: Normal Keywords: asgi, async
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Harm Verhagen)

I found that using memcached.PyMemcacheCache with runserver has reentrancy problems.

I implemented a json api in django, when issuing multiple request simultaneously from javascript, the requests sometimes crash.

I have urls that do something like this.

from django.core.cache import cache

def example_url(request):
    cache.incr("some key", 1)   # count requests

    return JsonResponse(data=data, status=201)

When issuing 5-10 requests simultaneously on a runserver, it often (not always) get this crash


  File "..../views/api.py", line 2512, in example_url
    cache.incr("some key", 1)
  File "./venv/lib/python3.12/site-packages/django/core/cache/backends/memcached.py", line 110, in incr
    val = self._cache.incr(key, delta)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "./venv/lib/python3.12/site-packages/pymemcache/client/hash.py", line 350, in incr
    return self._run_cmd("incr", key, False, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "./venv/lib/python3.12/site-packages/pymemcache/client/hash.py", line 322, in _run_cmd
    return self._safely_run_func(client, func, default_val, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "./venv/lib/python3.12/site-packages/pymemcache/client/hash.py", line 211, in _safely_run_func
    result = func(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^
  File "./venv/lib/python3.12/site-packages/pymemcache/client/base.py", line 827, in incr
    results = self._misc_cmd([cmd], b"incr", noreply)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "./venv/lib/python3.12/site-packages/pymemcache/client/base.py", line 1291, in _misc_cmd
    buf, line = _reader(self.sock, buf)
                ^^^^^^^^^^^^^^^^^^^^^^^
  File "./venv/lib/python3.12/site-packages/pymemcache/client/base.py", line 1658, in _readline
    buf = _recv(sock, RECV_SIZE)
          ^^^^^^^^^^^^^^^^^^^^^^
  File "./venv/lib/python3.12/site-packages/pymemcache/client/base.py", line 1750, in _recv
    return sock.recv(size)

 OSError: [Errno 9] Bad file descriptor

The following code looked a bit suspicious.

cache.backends.memcached.py:99 BaseMemcachedCache

    def close(self, **kwargs):
        # Many clients don't clean up connections properly.
        self._cache.disconnect_all()

Doesn't this race? closing a all connections, might close a connection of a parallel request that was just opened but not yet finished.

Notes

I'm not putting a heavy load on runserver, its a simple javascript app, that happens to do 10 requests on a single page. Django should be able to handle that, also in a dev environment

Workaround

Adding the --noasgi option seems to help.

./manage.py runserver --noasgi

Weird enough the (to me) more obvious, --nothreading option does _not_ help. That crashes in the same way.

NB: this workaround breaks django channels, so if you have websockets in your app, then this workaround is not usable.

Version info

Django==5.1.1
pymemcache==4.0.0
python 3.12.6

Change History (6)

comment:1 by Harm Verhagen, 2 months ago

Description: modified (diff)

comment:2 by Harm Verhagen, 2 months ago

Description: modified (diff)

comment:3 by Harm Verhagen, 2 months ago

Description: modified (diff)

comment:4 by Harm Verhagen, 2 months ago

Summary: memcached.PyMemcacheCache reentrancy problem (OSError: [Errno 9] Bad file descriptor)memcached.PyMemcacheCache reentrancy problem with ASGI-based runserver

comment:5 by Harm Verhagen, 2 months ago

Description: modified (diff)

comment:6 by Harm Verhagen, 2 months ago

Description: modified (diff)
Note: See TracTickets for help on using tickets.
Back to Top