1 | "Thread-safe in-memory cache backend."
|
---|
2 |
|
---|
3 | import time
|
---|
4 | try:
|
---|
5 | import cPickle as pickle
|
---|
6 | except ImportError:
|
---|
7 | import pickle
|
---|
8 |
|
---|
9 | from django.core.cache.backends.base import BaseCache
|
---|
10 | from django.utils.synch import RWLock
|
---|
11 |
|
---|
12 | class CacheClass(BaseCache):
|
---|
13 | def __init__(self, _, params):
|
---|
14 | BaseCache.__init__(self, params)
|
---|
15 | self._cache = {}
|
---|
16 | self._expire_info = {}
|
---|
17 |
|
---|
18 | if "nopickle" in params and params["nopickle"] == "True":
|
---|
19 | self.nopickle = True
|
---|
20 | else:
|
---|
21 | self.nopickle = False
|
---|
22 |
|
---|
23 | max_entries = params.get('max_entries', 300)
|
---|
24 | try:
|
---|
25 | self._max_entries = int(max_entries)
|
---|
26 | except (ValueError, TypeError):
|
---|
27 | self._max_entries = 300
|
---|
28 |
|
---|
29 | cull_frequency = params.get('cull_frequency', 3)
|
---|
30 | try:
|
---|
31 | self._cull_frequency = int(cull_frequency)
|
---|
32 | except (ValueError, TypeError):
|
---|
33 | self._cull_frequency = 3
|
---|
34 |
|
---|
35 | self._lock = RWLock()
|
---|
36 |
|
---|
37 | def _add(self, key, value, timeout=None):
|
---|
38 | if len(self._cache) >= self._max_entries:
|
---|
39 | self._cull()
|
---|
40 | if timeout is None:
|
---|
41 | timeout = self.default_timeout
|
---|
42 | if key not in self._cache.keys():
|
---|
43 | self._cache[key] = value
|
---|
44 | self._expire_info[key] = time.time() + timeout
|
---|
45 |
|
---|
46 | def add(self, key, value, timeout=None):
|
---|
47 | self._lock.writer_enters()
|
---|
48 | # Python 2.3 and 2.4 don't allow combined try-except-finally blocks.
|
---|
49 | try:
|
---|
50 | try:
|
---|
51 | self._add(key, pickle.dumps(value), timeout)
|
---|
52 | except pickle.PickleError:
|
---|
53 | pass
|
---|
54 | finally:
|
---|
55 | self._lock.writer_leaves()
|
---|
56 |
|
---|
57 | def get(self, key, default=None):
|
---|
58 | should_delete = False
|
---|
59 | self._lock.reader_enters()
|
---|
60 | try:
|
---|
61 | now = time.time()
|
---|
62 | exp = self._expire_info.get(key)
|
---|
63 | if exp is None:
|
---|
64 | return default
|
---|
65 | elif exp < now:
|
---|
66 | should_delete = True
|
---|
67 | else:
|
---|
68 | if self.nopickle:
|
---|
69 | return self._cache[key]
|
---|
70 | else:
|
---|
71 | try:
|
---|
72 | return pickle.loads(self._cache[key])
|
---|
73 | except pickle.PickleError:
|
---|
74 | return default
|
---|
75 | finally:
|
---|
76 | self._lock.reader_leaves()
|
---|
77 | if should_delete:
|
---|
78 | self._lock.writer_enters()
|
---|
79 | try:
|
---|
80 | del self._cache[key]
|
---|
81 | del self._expire_info[key]
|
---|
82 | return default
|
---|
83 | finally:
|
---|
84 | self._lock.writer_leaves()
|
---|
85 |
|
---|
86 | def _set(self, key, value, timeout=None):
|
---|
87 | if len(self._cache) >= self._max_entries:
|
---|
88 | self._cull()
|
---|
89 | if timeout is None:
|
---|
90 | timeout = self.default_timeout
|
---|
91 | self._cache[key] = value
|
---|
92 | self._expire_info[key] = time.time() + timeout
|
---|
93 |
|
---|
94 | def set(self, key, value, timeout=None):
|
---|
95 | self._lock.writer_enters()
|
---|
96 | # Python 2.3 and 2.4 don't allow combined try-except-finally blocks.
|
---|
97 | try:
|
---|
98 | if self.nopickle:
|
---|
99 | self._set(key, value, timeout)
|
---|
100 | else:
|
---|
101 | try:
|
---|
102 | self._set(key, pickle.dumps(value), timeout)
|
---|
103 | except pickle.PickleError:
|
---|
104 | pass
|
---|
105 | finally:
|
---|
106 | self._lock.writer_leaves()
|
---|
107 |
|
---|
108 | def has_key(self, key):
|
---|
109 | return key in self._cache
|
---|
110 |
|
---|
111 | def _cull(self):
|
---|
112 | if self._cull_frequency == 0:
|
---|
113 | self._cache.clear()
|
---|
114 | self._expire_info.clear()
|
---|
115 | else:
|
---|
116 | doomed = [k for (i, k) in enumerate(self._cache) if i % self._cull_frequency == 0]
|
---|
117 | for k in doomed:
|
---|
118 | self.delete(k)
|
---|
119 |
|
---|
120 | def _delete(self, key):
|
---|
121 | try:
|
---|
122 | del self._cache[key]
|
---|
123 | except KeyError:
|
---|
124 | pass
|
---|
125 | try:
|
---|
126 | del self._expire_info[key]
|
---|
127 | except KeyError:
|
---|
128 | pass
|
---|
129 |
|
---|
130 | def delete(self, key):
|
---|
131 | self._lock.writer_enters()
|
---|
132 | try:
|
---|
133 | self._delete(key)
|
---|
134 | finally:
|
---|
135 | self._lock.writer_leaves()
|
---|