Code

Ticket #6124: locmem.py

File locmem.py, 4.0 KB (added by Luke Loeffler <lukerl@…>, 6 years ago)
Line 
1"Thread-safe in-memory cache backend."
2
3import time
4try:
5    import cPickle as pickle
6except ImportError:
7    import pickle
8
9from django.core.cache.backends.base import BaseCache
10from django.utils.synch import RWLock
11
12class 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()