diff options
| author | Michael Manfre <mmanfre@gmail.com> | 2014-11-06 23:47:04 -0500 |
|---|---|---|
| committer | Michael Manfre <mmanfre@gmail.com> | 2014-11-13 00:46:03 -0500 |
| commit | bc8abe36ba5508fe788f6d49a26749268d368583 (patch) | |
| tree | 8ba53f287e0fef913f60905a20abf34fa6db011b | |
| parent | bfb11b95626f39e2f5e18d97d7761c6f93dcc1a9 (diff) | |
Fixed #16358 - Made memcache backend delete old value on a failure to set.
Default Memcached configuration allows for a maximum object of 1MB and
will fail to set the key if it is too large. The key will be deleted from
memcached if it fails to be set. This is needed to avoid an issue with
cache_db session backend using the old value stored in memcached, instead
of the newer value stored in the database.
| -rw-r--r-- | django/core/cache/backends/memcached.py | 4 | ||||
| -rw-r--r-- | docs/releases/1.8.txt | 4 | ||||
| -rw-r--r-- | tests/cache/tests.py | 18 |
3 files changed, 25 insertions, 1 deletions
diff --git a/django/core/cache/backends/memcached.py b/django/core/cache/backends/memcached.py index b32ab704ec..87f05f973b 100644 --- a/django/core/cache/backends/memcached.py +++ b/django/core/cache/backends/memcached.py @@ -86,7 +86,9 @@ class BaseMemcachedCache(six.with_metaclass(BaseMemcachedCacheMethods, BaseCache def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): key = self.make_key(key, version=version) - self._cache.set(key, value, self.get_backend_timeout(timeout)) + if not self._cache.set(key, value, self.get_backend_timeout(timeout)): + # make sure the key doesn't keep its old value in case of failure to set (memcached's 1MB limit) + self._cache.delete(key) def delete(self, key, version=None): key = self.make_key(key, version=version) diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index d3232d8235..fec2e2b4bc 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -729,6 +729,10 @@ Miscellaneous .. _universal newlines: http://www.python.org/dev/peps/pep-0278 +* The Memcached cache backends ``MemcachedCache`` and ``PyLibMCCache`` will + delete a key if ``set()`` fails. This is necessary to ensure the ``cache_db`` + session store always fetches the most current session data. + .. _deprecated-features-1.8: Features deprecated in 1.8 diff --git a/tests/cache/tests.py b/tests/cache/tests.py index c7604f131e..9dfd7af15d 100644 --- a/tests/cache/tests.py +++ b/tests/cache/tests.py @@ -1133,6 +1133,24 @@ class MemcachedCacheTests(BaseCacheTests, TestCase): # culling isn't implemented, memcached deals with it. pass + def test_memcached_deletes_key_on_failed_set(self): + # By default memcached allows objects up to 1MB. For the cache_db session + # backend to always use the current session, memcached needs to delete + # the old key if it fails to set. + # pylibmc doesn't seem to have SERVER_MAX_VALUE_LENGTH as far as I can + # tell from a quick check of its source code. This is falling back to + # the default value exposed by python-memcached on my system. + max_value_length = getattr(cache._lib, 'SERVER_MAX_VALUE_LENGTH', 1048576) + + cache.set('small_value', 'a') + self.assertEqual(cache.get('small_value'), 'a') + + large_value = 'a' * (max_value_length + 1) + cache.set('small_value', large_value) + # small_value should be deleted, or set if configured to accept larger values + value = cache.get('small_value') + self.assertTrue(value is None or value == large_value) + @override_settings(CACHES=caches_setting_for_tests( BACKEND='django.core.cache.backends.filebased.FileBasedCache', |
