Commit 3792b2cd authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

[1.1.X] Fixed #12399 -- Added handling for memcache timeouts longer than 30...

[1.1.X] Fixed #12399 -- Added handling for memcache timeouts longer than 30 days. Thanks to houdinihound for the report, and gciotta for the patch.

Backport of r12408 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.1.X@12412 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent ad08f077
Loading
Loading
Loading
Loading
+20 −2
Original line number Diff line number Diff line
"Memcached cache backend"

import time

from django.core.cache.backends.base import BaseCache, InvalidCacheBackendError
from django.utils.encoding import smart_unicode, smart_str

@@ -16,10 +18,26 @@ class CacheClass(BaseCache):
        BaseCache.__init__(self, params)
        self._cache = memcache.Client(server.split(';'))

    def _get_memcache_timeout(self, timeout):
        """
        Memcached deals with long (> 30 days) timeouts in a special
        way. Call this function to obtain a safe value for your timeout.
        """
        timeout = timeout or self.default_timeout
        if timeout > 2592000: # 60*60*24*30, 30 days
            # See http://code.google.com/p/memcached/wiki/FAQ
            # "You can set expire times up to 30 days in the future. After that
            # memcached interprets it as a date, and will expire the item after
            # said date. This is a simple (but obscure) mechanic."
            #
            # This means that we have to switch to absolute timestamps.
            timeout += int(time.time())
        return timeout

    def add(self, key, value, timeout=0):
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        return self._cache.add(smart_str(key), value, timeout or self.default_timeout)
        return self._cache.add(smart_str(key), value, self._get_memcache_timeout(timeout))

    def get(self, key, default=None):
        val = self._cache.get(smart_str(key))
@@ -34,7 +52,7 @@ class CacheClass(BaseCache):
    def set(self, key, value, timeout=0):
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        self._cache.set(smart_str(key), value, timeout or self.default_timeout)
        self._cache.set(smart_str(key), value, self._get_memcache_timeout(timeout))

    def delete(self, key):
        self._cache.delete(smart_str(key))
+16 −0
Original line number Diff line number Diff line
@@ -278,6 +278,22 @@ class BaseCacheTests(object):
            self.cache.set(key, value)
            self.assertEqual(self.cache.get(key), value)

    def test_long_timeout(self):
        '''
        Using a timeout greater than 30 days makes memcached think
        it is an absolute expiration timestamp instead of a relative
        offset. Test that we honour this convention. Refs #12399.
        '''
        self.cache.set('key1', 'eggs', 60*60*24*30 + 1) #30 days + 1 second
        self.assertEqual(self.cache.get('key1'), 'eggs')

        self.cache.add('key2', 'ham', 60*60*24*30 + 1)
        self.assertEqual(self.cache.get('key2'), 'ham')

        self.cache.set_many({'key3': 'sausage', 'key4': 'lobster bisque'}, 60*60*24*30 + 1)
        self.assertEqual(self.cache.get('key3'), 'sausage')
        self.assertEqual(self.cache.get('key4'), 'lobster bisque')

class DBCacheTests(unittest.TestCase, BaseCacheTests):
    def setUp(self):
        management.call_command('createcachetable', 'test_cache_table', verbosity=0, interactive=False)