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.

Reply via email to