|
1 "Memcached cache backend" |
|
2 |
|
3 import time |
|
4 |
|
5 from django.core.cache.backends.base import BaseCache, InvalidCacheBackendError |
|
6 from django.utils.encoding import smart_unicode, smart_str |
|
7 |
|
8 try: |
|
9 import cmemcache as memcache |
|
10 import warnings |
|
11 warnings.warn( |
|
12 "Support for the 'cmemcache' library has been deprecated. Please use python-memcached instead.", |
|
13 PendingDeprecationWarning |
|
14 ) |
|
15 except ImportError: |
|
16 try: |
|
17 import memcache |
|
18 except: |
|
19 raise InvalidCacheBackendError("Memcached cache backend requires either the 'memcache' or 'cmemcache' library") |
|
20 |
|
21 class CacheClass(BaseCache): |
|
22 def __init__(self, server, params): |
|
23 BaseCache.__init__(self, params) |
|
24 self._cache = memcache.Client(server.split(';')) |
|
25 |
|
26 def _get_memcache_timeout(self, timeout): |
|
27 """ |
|
28 Memcached deals with long (> 30 days) timeouts in a special |
|
29 way. Call this function to obtain a safe value for your timeout. |
|
30 """ |
|
31 timeout = timeout or self.default_timeout |
|
32 if timeout > 2592000: # 60*60*24*30, 30 days |
|
33 # See http://code.google.com/p/memcached/wiki/FAQ |
|
34 # "You can set expire times up to 30 days in the future. After that |
|
35 # memcached interprets it as a date, and will expire the item after |
|
36 # said date. This is a simple (but obscure) mechanic." |
|
37 # |
|
38 # This means that we have to switch to absolute timestamps. |
|
39 timeout += int(time.time()) |
|
40 return timeout |
|
41 |
|
42 def add(self, key, value, timeout=0): |
|
43 if isinstance(value, unicode): |
|
44 value = value.encode('utf-8') |
|
45 return self._cache.add(smart_str(key), value, self._get_memcache_timeout(timeout)) |
|
46 |
|
47 def get(self, key, default=None): |
|
48 val = self._cache.get(smart_str(key)) |
|
49 if val is None: |
|
50 return default |
|
51 return val |
|
52 |
|
53 def set(self, key, value, timeout=0): |
|
54 self._cache.set(smart_str(key), value, self._get_memcache_timeout(timeout)) |
|
55 |
|
56 def delete(self, key): |
|
57 self._cache.delete(smart_str(key)) |
|
58 |
|
59 def get_many(self, keys): |
|
60 return self._cache.get_multi(map(smart_str,keys)) |
|
61 |
|
62 def close(self, **kwargs): |
|
63 self._cache.disconnect_all() |
|
64 |
|
65 def incr(self, key, delta=1): |
|
66 try: |
|
67 val = self._cache.incr(key, delta) |
|
68 |
|
69 # python-memcache responds to incr on non-existent keys by |
|
70 # raising a ValueError. Cmemcache returns None. In both |
|
71 # cases, we should raise a ValueError though. |
|
72 except ValueError: |
|
73 val = None |
|
74 if val is None: |
|
75 raise ValueError("Key '%s' not found" % key) |
|
76 |
|
77 return val |
|
78 |
|
79 def decr(self, key, delta=1): |
|
80 try: |
|
81 val = self._cache.decr(key, delta) |
|
82 |
|
83 # python-memcache responds to decr on non-existent keys by |
|
84 # raising a ValueError. Cmemcache returns None. In both |
|
85 # cases, we should raise a ValueError though. |
|
86 except ValueError: |
|
87 val = None |
|
88 if val is None: |
|
89 raise ValueError("Key '%s' not found" % key) |
|
90 return val |
|
91 |
|
92 def set_many(self, data, timeout=0): |
|
93 safe_data = {} |
|
94 for key, value in data.items(): |
|
95 if isinstance(value, unicode): |
|
96 value = value.encode('utf-8') |
|
97 safe_data[smart_str(key)] = value |
|
98 self._cache.set_multi(safe_data, self._get_memcache_timeout(timeout)) |
|
99 |
|
100 def delete_many(self, keys): |
|
101 self._cache.delete_multi(map(smart_str, keys)) |
|
102 |
|
103 def clear(self): |
|
104 self._cache.flush_all() |