Commit a305695f authored by Michael Manfre's avatar Michael Manfre
Browse files

Merge pull request #3481 from manfre/ticket-16358

Fixed #16358 - Made memcache backend delete old value on a failure to set.
parents 3a550bb6 bc8abe36
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -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)
+4 −0
Original line number Diff line number Diff line
@@ -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
+18 −0
Original line number Diff line number Diff line
@@ -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',