123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- "Thread-safe in-memory cache backend."
- import pickle
- import time
- from collections import OrderedDict
- from threading import Lock
-
- from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache
-
- # Global in-memory store of cache data. Keyed by name, to provide
- # multiple named local memory caches.
- _caches = {}
- _expire_info = {}
- _locks = {}
-
-
- class LocMemCache(BaseCache):
- pickle_protocol = pickle.HIGHEST_PROTOCOL
-
- def __init__(self, name, params):
- super().__init__(params)
- self._cache = _caches.setdefault(name, OrderedDict())
- self._expire_info = _expire_info.setdefault(name, {})
- self._lock = _locks.setdefault(name, Lock())
-
- def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
- key = self.make_key(key, version=version)
- self.validate_key(key)
- pickled = pickle.dumps(value, self.pickle_protocol)
- with self._lock:
- if self._has_expired(key):
- self._set(key, pickled, timeout)
- return True
- return False
-
- def get(self, key, default=None, version=None):
- key = self.make_key(key, version=version)
- self.validate_key(key)
- with self._lock:
- if self._has_expired(key):
- self._delete(key)
- return default
- pickled = self._cache[key]
- self._cache.move_to_end(key, last=False)
- return pickle.loads(pickled)
-
- def _set(self, key, value, timeout=DEFAULT_TIMEOUT):
- if len(self._cache) >= self._max_entries:
- self._cull()
- self._cache[key] = value
- self._cache.move_to_end(key, last=False)
- self._expire_info[key] = self.get_backend_timeout(timeout)
-
- def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
- key = self.make_key(key, version=version)
- self.validate_key(key)
- pickled = pickle.dumps(value, self.pickle_protocol)
- with self._lock:
- self._set(key, pickled, timeout)
-
- def touch(self, key, timeout=DEFAULT_TIMEOUT, version=None):
- key = self.make_key(key, version=version)
- with self._lock:
- if self._has_expired(key):
- return False
- self._expire_info[key] = self.get_backend_timeout(timeout)
- return True
-
- def incr(self, key, delta=1, version=None):
- key = self.make_key(key, version=version)
- self.validate_key(key)
- with self._lock:
- if self._has_expired(key):
- self._delete(key)
- raise ValueError("Key '%s' not found" % key)
- pickled = self._cache[key]
- value = pickle.loads(pickled)
- new_value = value + delta
- pickled = pickle.dumps(new_value, self.pickle_protocol)
- self._cache[key] = pickled
- self._cache.move_to_end(key, last=False)
- return new_value
-
- def has_key(self, key, version=None):
- key = self.make_key(key, version=version)
- self.validate_key(key)
- with self._lock:
- if self._has_expired(key):
- self._delete(key)
- return False
- return True
-
- def _has_expired(self, key):
- exp = self._expire_info.get(key, -1)
- return exp is not None and exp <= time.time()
-
- def _cull(self):
- if self._cull_frequency == 0:
- self._cache.clear()
- self._expire_info.clear()
- else:
- count = len(self._cache) // self._cull_frequency
- for i in range(count):
- key, _ = self._cache.popitem()
- del self._expire_info[key]
-
- def _delete(self, key):
- try:
- del self._cache[key]
- del self._expire_info[key]
- except KeyError:
- pass
-
- def delete(self, key, version=None):
- key = self.make_key(key, version=version)
- self.validate_key(key)
- with self._lock:
- self._delete(key)
-
- def clear(self):
- with self._lock:
- self._cache.clear()
- self._expire_info.clear()
|