Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #12399 -- Added handling for memcache timeouts longer than 30 d…

…ays. Thanks to houdinihound for the report, and gciotta for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12408 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 75ab212d12058494eaf171acfc762206d8a62d47 1 parent be57541
Russell Keith-Magee authored February 11, 2010
24  django/core/cache/backends/memcached.py
... ...
@@ -1,5 +1,7 @@
1 1
 "Memcached cache backend"
2 2
 
  3
+import time
  4
+
3 5
 from django.core.cache.backends.base import BaseCache, InvalidCacheBackendError
4 6
 from django.utils.encoding import smart_unicode, smart_str
5 7
 
@@ -16,10 +18,26 @@ def __init__(self, server, params):
16 18
         BaseCache.__init__(self, params)
17 19
         self._cache = memcache.Client(server.split(';'))
18 20
 
  21
+    def _get_memcache_timeout(self, timeout):
  22
+        """
  23
+        Memcached deals with long (> 30 days) timeouts in a special
  24
+        way. Call this function to obtain a safe value for your timeout.
  25
+        """
  26
+        timeout = timeout or self.default_timeout
  27
+        if timeout > 2592000: # 60*60*24*30, 30 days
  28
+            # See http://code.google.com/p/memcached/wiki/FAQ
  29
+            # "You can set expire times up to 30 days in the future. After that
  30
+            # memcached interprets it as a date, and will expire the item after
  31
+            # said date. This is a simple (but obscure) mechanic."
  32
+            #
  33
+            # This means that we have to switch to absolute timestamps.
  34
+            timeout += int(time.time())
  35
+        return timeout
  36
+
19 37
     def add(self, key, value, timeout=0):
20 38
         if isinstance(value, unicode):
21 39
             value = value.encode('utf-8')
22  
-        return self._cache.add(smart_str(key), value, timeout or self.default_timeout)
  40
+        return self._cache.add(smart_str(key), value, self._get_memcache_timeout(timeout))
23 41
 
24 42
     def get(self, key, default=None):
25 43
         val = self._cache.get(smart_str(key))
@@ -34,7 +52,7 @@ def get(self, key, default=None):
34 52
     def set(self, key, value, timeout=0):
35 53
         if isinstance(value, unicode):
36 54
             value = value.encode('utf-8')
37  
-        self._cache.set(smart_str(key), value, timeout or self.default_timeout)
  55
+        self._cache.set(smart_str(key), value, self._get_memcache_timeout(timeout))
38 56
 
39 57
     def delete(self, key):
40 58
         self._cache.delete(smart_str(key))
@@ -78,7 +96,7 @@ def set_many(self, data, timeout=0):
78 96
             if isinstance(value, unicode):
79 97
                 value = value.encode('utf-8')
80 98
             safe_data[smart_str(key)] = value
81  
-        self._cache.set_multi(safe_data, timeout or self.default_timeout)
  99
+        self._cache.set_multi(safe_data, self._get_memcache_timeout(timeout))
82 100
 
83 101
     def delete_many(self, keys):
84 102
         self._cache.delete_multi(map(smart_str, keys))
16  tests/regressiontests/cache/tests.py
@@ -324,6 +324,22 @@ def test_clear(self):
324 324
         self.assertEqual(self.cache.get("key1"), None)
325 325
         self.assertEqual(self.cache.get("key2"), None)
326 326
 
  327
+    def test_long_timeout(self):
  328
+        '''
  329
+        Using a timeout greater than 30 days makes memcached think
  330
+        it is an absolute expiration timestamp instead of a relative
  331
+        offset. Test that we honour this convention. Refs #12399.
  332
+        '''
  333
+        self.cache.set('key1', 'eggs', 60*60*24*30 + 1) #30 days + 1 second
  334
+        self.assertEqual(self.cache.get('key1'), 'eggs')
  335
+
  336
+        self.cache.add('key2', 'ham', 60*60*24*30 + 1)
  337
+        self.assertEqual(self.cache.get('key2'), 'ham')
  338
+
  339
+        self.cache.set_many({'key3': 'sausage', 'key4': 'lobster bisque'}, 60*60*24*30 + 1)
  340
+        self.assertEqual(self.cache.get('key3'), 'sausage')
  341
+        self.assertEqual(self.cache.get('key4'), 'lobster bisque')
  342
+
327 343
 class DBCacheTests(unittest.TestCase, BaseCacheTests):
328 344
     def setUp(self):
329 345
         management.call_command('createcachetable', 'test_cache_table', verbosity=0, interactive=False)

0 notes on commit 75ab212

Please sign in to comment.
Something went wrong with that request. Please try again.