Author: russellm Date: 2010-02-11 07:09:56 -0600 (Thu, 11 Feb 2010) New Revision: 12412
Modified: django/branches/releases/1.1.X/django/core/cache/backends/memcached.py django/branches/releases/1.1.X/tests/regressiontests/cache/tests.py Log: [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. Modified: django/branches/releases/1.1.X/django/core/cache/backends/memcached.py =================================================================== --- django/branches/releases/1.1.X/django/core/cache/backends/memcached.py 2010-02-11 12:26:34 UTC (rev 12411) +++ django/branches/releases/1.1.X/django/core/cache/backends/memcached.py 2010-02-11 13:09:56 UTC (rev 12412) @@ -1,5 +1,7 @@ "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 @@ 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 @@ 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)) Modified: django/branches/releases/1.1.X/tests/regressiontests/cache/tests.py =================================================================== --- django/branches/releases/1.1.X/tests/regressiontests/cache/tests.py 2010-02-11 12:26:34 UTC (rev 12411) +++ django/branches/releases/1.1.X/tests/regressiontests/cache/tests.py 2010-02-11 13:09:56 UTC (rev 12412) @@ -278,6 +278,22 @@ 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) -- You received this message because you are subscribed to the Google Groups "Django updates" group. To post to this group, send email to django-upda...@googlegroups.com. To unsubscribe from this group, send email to django-updates+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-updates?hl=en.