Pymemcache OSError: [Errno 99] Невозможно присвоить запрашиваемый адрес

Контекст:

У нас есть приложение django, запущенное в контейнере на нашем облачном инстансе. Недавно мы начали видеть ошибки, когда пытаемся получить доступ к значению из кэша django в конечной точке api.

cache.get('key')

К этой конечной точке api очень часто обращаются наши пользователи.

Полная версия ошибки, которую мы видим, прикреплена ниже.

Трассировка ошибки

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/views/decorators/csrf.py", line 56, in wrapper_view
    return view_func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/srv/www/iridize/tipcms/views.py", line 2141, in cross_app_new
    cache_value = cache.get(cache_key, {})
                  ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/core/cache/backends/memcached.py", line 75, in get
    return self._cache.get(key, default)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pymemcache/client/hash.py", line 347, in get
    return self._run_cmd("get", key, default, default=default, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pymemcache/client/hash.py", line 322, in _run_cmd
    return self._safely_run_func(client, func, default_val, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pymemcache/client/hash.py", line 211, in _safely_run_func
    result = func(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pymemcache/client/base.py", line 1494, in get
    return client.get(key, default)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pymemcache/client/base.py", line 687, in get
    return self._fetch_cmd(b"get", [key], False, key_prefix=self.key_prefix).get(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/pymemcache/client/base.py", line 1133, in _fetch_cmd
    self._connect()
  File "/usr/local/lib/python3.11/site-packages/pymemcache/client/base.py", line 424, in _connect
    sock.connect(sockaddr)
OSError: [Errno 99] Cannot assign requested address

Мы используем memcached для кэширования, и наш конфиг кэша в django выглядит следующим образом.

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
        'LOCATION': os.environ.get('MEMCACHE_SERVICE', '127.0.0.1:11211'),
        'OPTIONS': {
            "use_pooling": True
        }
    }
}

Мой коллега заподозрил, что у нас в капсуле могут быть не открыты порты, и провел небольшое расследование

Это можно легко проверить, подсчитав занятые порты в стручке с помощью:

cat /proc/net/tcp|wc -l В pods мы наблюдали 25k занятых портов или даже больше, когда максимальное количество портов составляет около 30k.

Также более конкретная команда ищет номер порта memcache (11211 = hex 2BCB) может быть выполнена с помощью этого, Также добавлен фильтр 03:, который учитывает только не освобожденные порты (они не заняты больше не заняты, но WAIT_TIME все еще в процессе, поэтому они еще не освобождены)

cat /proc/net/tcp|grep 2BCB|grep « 03:»|wc -l

Некоторый контекст относительно вышеприведенного блока: Если мы пытаемся выполнить на нашем сервере несколько сотен запросов, количество портов, связанных с memcached, продолжает расти.

Вопрос:

Почему pymemcached открывает новое соединение для каждого запроса cache.get, поскольку на наш сервер поступает огромное количество запросов, у нас заканчиваются порты, так как соединения открываются очень часто. Мы подумали, что pymemcached открывает несколько соединений и использует их повторно вместо того, чтобы каждый раз открывать новые соединения. Мы пытались контролировать это с помощью параметров max_pool_size, pool_idle_timeout, где параметр timeout, имеющий значение 0, не должен отбрасывать соединения, и мы предполагали, что произойдет некоторое повторное использование соединений, но этого не происходит doc link: https://pymemcache.readthedocs.io/en/latest/apidoc/pymemcache.client.hash.html, но все еще не удается предотвратить множество соединений, открытых pymemcached.

Версии программного обеспечения: Django==4.2.15 pymemcache==4.0.0

Вернуться на верх