Fixed #29887 -- Added a cache backend for pymemcache.
This commit is contained in:
parent
cda0a3d777
commit
b4d46df5ca
|
@ -214,3 +214,17 @@ class PyLibMCCache(BaseMemcachedCache):
|
||||||
# libmemcached manages its own connections. Don't call disconnect_all()
|
# libmemcached manages its own connections. Don't call disconnect_all()
|
||||||
# as it resets the failover state and creates unnecessary reconnects.
|
# as it resets the failover state and creates unnecessary reconnects.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class PyMemcacheCache(BaseMemcachedCache):
|
||||||
|
"""An implementation of a cache binding using pymemcache."""
|
||||||
|
def __init__(self, server, params):
|
||||||
|
import pymemcache.serde
|
||||||
|
super().__init__(server, params, library=pymemcache, value_not_found_exception=KeyError)
|
||||||
|
self._class = self._lib.HashClient
|
||||||
|
self._options = {
|
||||||
|
'allow_unicode_keys': True,
|
||||||
|
'default_noreply': False,
|
||||||
|
'serde': pymemcache.serde.pickle_serde,
|
||||||
|
**self._options,
|
||||||
|
}
|
||||||
|
|
|
@ -158,11 +158,16 @@ The cache backend to use. The built-in cache backends are:
|
||||||
* ``'django.core.cache.backends.locmem.LocMemCache'``
|
* ``'django.core.cache.backends.locmem.LocMemCache'``
|
||||||
* ``'django.core.cache.backends.memcached.MemcachedCache'``
|
* ``'django.core.cache.backends.memcached.MemcachedCache'``
|
||||||
* ``'django.core.cache.backends.memcached.PyLibMCCache'``
|
* ``'django.core.cache.backends.memcached.PyLibMCCache'``
|
||||||
|
* ``'django.core.cache.backends.memcached.PyMemcacheCache'``
|
||||||
|
|
||||||
You can use a cache backend that doesn't ship with Django by setting
|
You can use a cache backend that doesn't ship with Django by setting
|
||||||
:setting:`BACKEND <CACHES-BACKEND>` to a fully-qualified path of a cache
|
:setting:`BACKEND <CACHES-BACKEND>` to a fully-qualified path of a cache
|
||||||
backend class (i.e. ``mypackage.backends.whatever.WhateverCache``).
|
backend class (i.e. ``mypackage.backends.whatever.WhateverCache``).
|
||||||
|
|
||||||
|
.. versionchanged:: 3.2
|
||||||
|
|
||||||
|
The ``PyMemcacheCache`` backend was added.
|
||||||
|
|
||||||
.. setting:: CACHES-KEY_FUNCTION
|
.. setting:: CACHES-KEY_FUNCTION
|
||||||
|
|
||||||
``KEY_FUNCTION``
|
``KEY_FUNCTION``
|
||||||
|
|
|
@ -53,6 +53,16 @@ needed. As a consequence, it's deprecated.
|
||||||
|
|
||||||
See :ref:`configuring-applications-ref` for full details.
|
See :ref:`configuring-applications-ref` for full details.
|
||||||
|
|
||||||
|
``pymemcache`` support
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
The new ``django.core.cache.backends.memcached.PyMemcacheCache`` cache backend
|
||||||
|
allows using the pymemcache_ library for memcached. ``pymemcache`` 3.4.0 or
|
||||||
|
higher is required. For more details, see the :doc:`documentation on caching in
|
||||||
|
Django </topics/cache>`.
|
||||||
|
|
||||||
|
.. _pymemcache: https://pypi.org/project/pymemcache/
|
||||||
|
|
||||||
Minor features
|
Minor features
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|
|
@ -77,17 +77,19 @@ database or filesystem usage.
|
||||||
|
|
||||||
After installing Memcached itself, you'll need to install a Memcached
|
After installing Memcached itself, you'll need to install a Memcached
|
||||||
binding. There are several Python Memcached bindings available; the
|
binding. There are several Python Memcached bindings available; the
|
||||||
two most common are `python-memcached`_ and `pylibmc`_.
|
three most common are `python-memcached`_, `pylibmc`_, and `pymemcache`_.
|
||||||
|
|
||||||
.. _`python-memcached`: https://pypi.org/project/python-memcached/
|
.. _`python-memcached`: https://pypi.org/project/python-memcached/
|
||||||
.. _`pylibmc`: https://pypi.org/project/pylibmc/
|
.. _`pylibmc`: https://pypi.org/project/pylibmc/
|
||||||
|
.. _`pymemcache`: https://pypi.org/project/pymemcache/
|
||||||
|
|
||||||
To use Memcached with Django:
|
To use Memcached with Django:
|
||||||
|
|
||||||
* Set :setting:`BACKEND <CACHES-BACKEND>` to
|
* Set :setting:`BACKEND <CACHES-BACKEND>` to
|
||||||
``django.core.cache.backends.memcached.MemcachedCache`` or
|
``django.core.cache.backends.memcached.MemcachedCache``,
|
||||||
``django.core.cache.backends.memcached.PyLibMCCache`` (depending
|
``django.core.cache.backends.memcached.PyLibMCCache``, or
|
||||||
on your chosen memcached binding)
|
``django.core.cache.backends.memcached.PyMemcacheCache`` (depending on your
|
||||||
|
chosen memcached binding)
|
||||||
|
|
||||||
* Set :setting:`LOCATION <CACHES-LOCATION>` to ``ip:port`` values,
|
* Set :setting:`LOCATION <CACHES-LOCATION>` to ``ip:port`` values,
|
||||||
where ``ip`` is the IP address of the Memcached daemon and ``port`` is the
|
where ``ip`` is the IP address of the Memcached daemon and ``port`` is the
|
||||||
|
@ -159,6 +161,10 @@ permanent storage -- they're all intended to be solutions for caching, not
|
||||||
storage -- but we point this out here because memory-based caching is
|
storage -- but we point this out here because memory-based caching is
|
||||||
particularly temporary.
|
particularly temporary.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.2
|
||||||
|
|
||||||
|
The ``PyMemcacheCache`` backend was added.
|
||||||
|
|
||||||
.. _database-caching:
|
.. _database-caching:
|
||||||
|
|
||||||
Database caching
|
Database caching
|
||||||
|
@ -466,6 +472,24 @@ the binary protocol, SASL authentication, and the ``ketama`` behavior mode::
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Here's an example configuration for a ``pymemcache`` based backend that enables
|
||||||
|
client pooling (which may improve performance by keeping clients connected),
|
||||||
|
treats memcache/network errors as cache misses, and sets the ``TCP_NODELAY``
|
||||||
|
flag on the connection's socket::
|
||||||
|
|
||||||
|
CACHES = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
|
||||||
|
'LOCATION': '127.0.0.1:11211',
|
||||||
|
'OPTIONS': {
|
||||||
|
'no_delay': True,
|
||||||
|
'ignore_exc': True,
|
||||||
|
'max_pool_size': 4,
|
||||||
|
'use_pooling': True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.. _the-per-site-cache:
|
.. _the-per-site-cache:
|
||||||
|
|
||||||
The per-site cache
|
The per-site cache
|
||||||
|
|
|
@ -1277,6 +1277,7 @@ for _cache_params in settings.CACHES.values():
|
||||||
|
|
||||||
MemcachedCache_params = configured_caches.get('django.core.cache.backends.memcached.MemcachedCache')
|
MemcachedCache_params = configured_caches.get('django.core.cache.backends.memcached.MemcachedCache')
|
||||||
PyLibMCCache_params = configured_caches.get('django.core.cache.backends.memcached.PyLibMCCache')
|
PyLibMCCache_params = configured_caches.get('django.core.cache.backends.memcached.PyLibMCCache')
|
||||||
|
PyMemcacheCache_params = configured_caches.get('django.core.cache.backends.memcached.PyMemcacheCache')
|
||||||
|
|
||||||
# The memcached backends don't support cull-related options like `MAX_ENTRIES`.
|
# The memcached backends don't support cull-related options like `MAX_ENTRIES`.
|
||||||
memcached_excluded_caches = {'cull', 'zero_cull'}
|
memcached_excluded_caches = {'cull', 'zero_cull'}
|
||||||
|
@ -1459,6 +1460,36 @@ class PyLibMCCacheTests(BaseMemcachedTests, TestCase):
|
||||||
self.assertEqual(cache.client_servers, [expected])
|
self.assertEqual(cache.client_servers, [expected])
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(PyMemcacheCache_params, 'PyMemcacheCache backend not configured')
|
||||||
|
@override_settings(CACHES=caches_setting_for_tests(
|
||||||
|
base=PyMemcacheCache_params,
|
||||||
|
exclude=memcached_excluded_caches,
|
||||||
|
))
|
||||||
|
class PyMemcacheCacheTests(BaseMemcachedTests, TestCase):
|
||||||
|
base_params = PyMemcacheCache_params
|
||||||
|
|
||||||
|
def test_pymemcache_highest_pickle_version(self):
|
||||||
|
self.assertEqual(
|
||||||
|
cache._cache.default_kwargs['serde']._serialize_func.keywords['pickle_version'],
|
||||||
|
pickle.HIGHEST_PROTOCOL,
|
||||||
|
)
|
||||||
|
for cache_key in settings.CACHES:
|
||||||
|
for client_key, client in caches[cache_key]._cache.clients.items():
|
||||||
|
with self.subTest(cache_key=cache_key, server=client_key):
|
||||||
|
self.assertEqual(
|
||||||
|
client.serde._serialize_func.keywords['pickle_version'],
|
||||||
|
pickle.HIGHEST_PROTOCOL,
|
||||||
|
)
|
||||||
|
|
||||||
|
@override_settings(CACHES=caches_setting_for_tests(
|
||||||
|
base=PyMemcacheCache_params,
|
||||||
|
exclude=memcached_excluded_caches,
|
||||||
|
OPTIONS={'no_delay': True},
|
||||||
|
))
|
||||||
|
def test_pymemcache_options(self):
|
||||||
|
self.assertIs(cache._cache.default_kwargs['no_delay'], True)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(CACHES=caches_setting_for_tests(
|
@override_settings(CACHES=caches_setting_for_tests(
|
||||||
BACKEND='django.core.cache.backends.filebased.FileBasedCache',
|
BACKEND='django.core.cache.backends.filebased.FileBasedCache',
|
||||||
))
|
))
|
||||||
|
|
|
@ -8,6 +8,7 @@ numpy
|
||||||
Pillow >= 6.2.0
|
Pillow >= 6.2.0
|
||||||
# pylibmc/libmemcached can't be built on Windows.
|
# pylibmc/libmemcached can't be built on Windows.
|
||||||
pylibmc; sys.platform != 'win32'
|
pylibmc; sys.platform != 'win32'
|
||||||
|
pymemcache >= 3.4.0
|
||||||
python-memcached >= 1.59
|
python-memcached >= 1.59
|
||||||
pytz
|
pytz
|
||||||
pywatchman; sys.platform != 'win32'
|
pywatchman; sys.platform != 'win32'
|
||||||
|
|
Loading…
Reference in New Issue