2006-02-24 14:07:01 +08:00
|
|
|
"Base Cache class."
|
2013-09-24 00:40:19 +08:00
|
|
|
import time
|
2010-09-13 02:45:26 +08:00
|
|
|
import warnings
|
|
|
|
|
2015-01-28 20:35:27 +08:00
|
|
|
from django.core.exceptions import DjangoRuntimeWarning, ImproperlyConfigured
|
2014-01-21 04:15:14 +08:00
|
|
|
from django.utils.module_loading import import_string
|
2006-02-24 14:07:01 +08:00
|
|
|
|
2012-07-18 20:00:31 +08:00
|
|
|
|
2006-02-24 14:07:01 +08:00
|
|
|
class InvalidCacheBackendError(ImproperlyConfigured):
|
|
|
|
pass
|
|
|
|
|
2012-07-18 20:00:31 +08:00
|
|
|
|
2010-09-13 02:45:26 +08:00
|
|
|
class CacheKeyWarning(DjangoRuntimeWarning):
|
|
|
|
pass
|
|
|
|
|
2012-11-12 04:23:45 +08:00
|
|
|
|
2013-05-18 18:54:59 +08:00
|
|
|
# Stub class to ensure not passing in a `timeout` argument results in
|
|
|
|
# the default timeout
|
|
|
|
DEFAULT_TIMEOUT = object()
|
|
|
|
|
2010-09-13 02:45:26 +08:00
|
|
|
# Memcached does not accept keys longer than this.
|
|
|
|
MEMCACHE_MAX_KEY_LENGTH = 250
|
|
|
|
|
2012-07-18 20:00:31 +08:00
|
|
|
|
2010-11-19 23:39:35 +08:00
|
|
|
def default_key_func(key, key_prefix, version):
|
2010-12-21 23:19:19 +08:00
|
|
|
"""
|
|
|
|
Default function to generate keys.
|
2010-11-19 23:39:35 +08:00
|
|
|
|
|
|
|
Constructs the key used by all other methods. By default it prepends
|
2010-12-21 23:19:19 +08:00
|
|
|
the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
|
2010-11-19 23:39:35 +08:00
|
|
|
function with custom key making behavior.
|
|
|
|
"""
|
2012-11-12 04:23:45 +08:00
|
|
|
return '%s:%s:%s' % (key_prefix, version, key)
|
2010-11-19 23:39:35 +08:00
|
|
|
|
2012-07-18 20:00:31 +08:00
|
|
|
|
2010-12-21 23:19:19 +08:00
|
|
|
def get_key_func(key_func):
|
|
|
|
"""
|
|
|
|
Function to decide which key function to use.
|
|
|
|
|
|
|
|
Defaults to ``default_key_func``.
|
|
|
|
"""
|
|
|
|
if key_func is not None:
|
|
|
|
if callable(key_func):
|
|
|
|
return key_func
|
|
|
|
else:
|
2014-01-21 04:15:14 +08:00
|
|
|
return import_string(key_func)
|
2010-12-21 23:19:19 +08:00
|
|
|
return default_key_func
|
|
|
|
|
2012-07-18 20:00:31 +08:00
|
|
|
|
2017-01-19 15:39:46 +08:00
|
|
|
class BaseCache:
|
2010-12-21 23:19:19 +08:00
|
|
|
def __init__(self, params):
|
|
|
|
timeout = params.get('timeout', params.get('TIMEOUT', 300))
|
2014-02-24 06:58:26 +08:00
|
|
|
if timeout is not None:
|
|
|
|
try:
|
|
|
|
timeout = int(timeout)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
timeout = 300
|
2006-02-24 14:07:01 +08:00
|
|
|
self.default_timeout = timeout
|
|
|
|
|
2010-12-21 23:19:19 +08:00
|
|
|
options = params.get('OPTIONS', {})
|
|
|
|
max_entries = params.get('max_entries', options.get('MAX_ENTRIES', 300))
|
2010-11-02 13:55:08 +08:00
|
|
|
try:
|
|
|
|
self._max_entries = int(max_entries)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
self._max_entries = 300
|
|
|
|
|
2010-12-21 23:19:19 +08:00
|
|
|
cull_frequency = params.get('cull_frequency', options.get('CULL_FREQUENCY', 3))
|
2010-11-02 13:55:08 +08:00
|
|
|
try:
|
|
|
|
self._cull_frequency = int(cull_frequency)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
self._cull_frequency = 3
|
|
|
|
|
2012-08-13 18:09:20 +08:00
|
|
|
self.key_prefix = params.get('KEY_PREFIX', '')
|
2010-12-21 23:19:19 +08:00
|
|
|
self.version = params.get('VERSION', 1)
|
2015-05-14 02:51:18 +08:00
|
|
|
self.key_func = get_key_func(params.get('KEY_FUNCTION'))
|
2010-11-19 23:39:35 +08:00
|
|
|
|
2013-09-24 00:40:19 +08:00
|
|
|
def get_backend_timeout(self, timeout=DEFAULT_TIMEOUT):
|
|
|
|
"""
|
|
|
|
Returns the timeout value usable by this backend based upon the provided
|
|
|
|
timeout.
|
|
|
|
"""
|
|
|
|
if timeout == DEFAULT_TIMEOUT:
|
|
|
|
timeout = self.default_timeout
|
|
|
|
elif timeout == 0:
|
|
|
|
# ticket 21147 - avoid time.time() related precision issues
|
|
|
|
timeout = -1
|
|
|
|
return None if timeout is None else time.time() + timeout
|
|
|
|
|
2010-11-19 23:39:35 +08:00
|
|
|
def make_key(self, key, version=None):
|
|
|
|
"""Constructs the key used by all other methods. By default it
|
|
|
|
uses the key_func to generate a key (which, by default,
|
2015-09-16 21:19:31 +08:00
|
|
|
prepends the `key_prefix' and 'version'). A different key
|
2010-11-19 23:39:35 +08:00
|
|
|
function can be provided at the time of cache construction;
|
|
|
|
alternatively, you can subclass the cache backend to provide
|
|
|
|
custom key making behavior.
|
|
|
|
"""
|
|
|
|
if version is None:
|
|
|
|
version = self.version
|
|
|
|
|
|
|
|
new_key = self.key_func(key, self.key_prefix, version)
|
|
|
|
return new_key
|
|
|
|
|
2013-05-18 18:54:59 +08:00
|
|
|
def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
|
2007-10-20 23:16:34 +08:00
|
|
|
"""
|
2007-11-30 13:30:43 +08:00
|
|
|
Set a value in the cache if the key does not already exist. If
|
2007-10-20 23:16:34 +08:00
|
|
|
timeout is given, that timeout will be used for the key; otherwise
|
|
|
|
the default cache timeout will be used.
|
2008-08-10 11:52:21 +08:00
|
|
|
|
|
|
|
Returns True if the value was stored, False otherwise.
|
2007-10-20 23:16:34 +08:00
|
|
|
"""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of BaseCache must provide an add() method')
|
2007-10-20 23:16:34 +08:00
|
|
|
|
2010-11-19 23:39:35 +08:00
|
|
|
def get(self, key, default=None, version=None):
|
2006-02-24 14:07:01 +08:00
|
|
|
"""
|
2007-11-30 13:30:43 +08:00
|
|
|
Fetch a given key from the cache. If the key does not exist, return
|
2006-02-24 14:07:01 +08:00
|
|
|
default, which itself defaults to None.
|
|
|
|
"""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of BaseCache must provide a get() method')
|
2006-02-24 14:07:01 +08:00
|
|
|
|
2013-05-18 18:54:59 +08:00
|
|
|
def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None):
|
2006-02-24 14:07:01 +08:00
|
|
|
"""
|
2007-11-30 13:30:43 +08:00
|
|
|
Set a value in the cache. If timeout is given, that timeout will be
|
2006-02-24 14:07:01 +08:00
|
|
|
used for the key; otherwise the default cache timeout will be used.
|
|
|
|
"""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of BaseCache must provide a set() method')
|
2006-02-24 14:07:01 +08:00
|
|
|
|
2010-11-19 23:39:35 +08:00
|
|
|
def delete(self, key, version=None):
|
2006-02-24 14:07:01 +08:00
|
|
|
"""
|
|
|
|
Delete a key from the cache, failing silently.
|
|
|
|
"""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of BaseCache must provide a delete() method')
|
2006-02-24 14:07:01 +08:00
|
|
|
|
2010-11-19 23:39:35 +08:00
|
|
|
def get_many(self, keys, version=None):
|
2006-02-24 14:07:01 +08:00
|
|
|
"""
|
2007-11-30 13:30:43 +08:00
|
|
|
Fetch a bunch of keys from the cache. For certain backends (memcached,
|
2006-02-24 14:07:01 +08:00
|
|
|
pgsql) this can be *much* faster when fetching multiple values.
|
|
|
|
|
2007-11-30 13:30:43 +08:00
|
|
|
Returns a dict mapping each key in keys to its value. If the given
|
2006-02-24 14:07:01 +08:00
|
|
|
key is missing, it will be missing from the response dict.
|
|
|
|
"""
|
|
|
|
d = {}
|
|
|
|
for k in keys:
|
2010-11-19 23:39:35 +08:00
|
|
|
val = self.get(k, version=version)
|
2006-02-24 14:07:01 +08:00
|
|
|
if val is not None:
|
|
|
|
d[k] = val
|
|
|
|
return d
|
|
|
|
|
2016-06-23 02:28:43 +08:00
|
|
|
def get_or_set(self, key, default, timeout=DEFAULT_TIMEOUT, version=None):
|
2014-11-24 02:33:07 +08:00
|
|
|
"""
|
|
|
|
Fetch a given key from the cache. If the key does not exist,
|
|
|
|
the key is added and set to the default value. The default value can
|
|
|
|
also be any callable. If timeout is given, that timeout will be used
|
|
|
|
for the key; otherwise the default cache timeout will be used.
|
|
|
|
|
2016-03-06 18:34:23 +08:00
|
|
|
Return the value of the key stored or retrieved.
|
2014-11-24 02:33:07 +08:00
|
|
|
"""
|
|
|
|
val = self.get(key, version=version)
|
2016-06-23 02:28:43 +08:00
|
|
|
if val is None and default is not None:
|
2014-11-24 02:33:07 +08:00
|
|
|
if callable(default):
|
|
|
|
default = default()
|
2016-03-06 18:34:23 +08:00
|
|
|
self.add(key, default, timeout=timeout, version=version)
|
|
|
|
# Fetch the value again to avoid a race condition if another caller
|
|
|
|
# added a value between the first get() and the add() above.
|
|
|
|
return self.get(key, default, version=version)
|
2014-11-24 02:33:07 +08:00
|
|
|
return val
|
|
|
|
|
2010-11-19 23:39:35 +08:00
|
|
|
def has_key(self, key, version=None):
|
2006-02-24 14:07:01 +08:00
|
|
|
"""
|
|
|
|
Returns True if the key is in the cache and has not expired.
|
|
|
|
"""
|
2010-11-19 23:39:35 +08:00
|
|
|
return self.get(key, version=version) is not None
|
2007-05-08 12:13:46 +08:00
|
|
|
|
2010-11-19 23:39:35 +08:00
|
|
|
def incr(self, key, delta=1, version=None):
|
2009-03-11 21:27:03 +08:00
|
|
|
"""
|
|
|
|
Add delta to value in the cache. If the key does not exist, raise a
|
|
|
|
ValueError exception.
|
|
|
|
"""
|
2010-11-19 23:39:35 +08:00
|
|
|
value = self.get(key, version=version)
|
|
|
|
if value is None:
|
2010-01-11 02:36:20 +08:00
|
|
|
raise ValueError("Key '%s' not found" % key)
|
2010-11-19 23:39:35 +08:00
|
|
|
new_value = value + delta
|
|
|
|
self.set(key, new_value, version=version)
|
2009-03-11 21:27:03 +08:00
|
|
|
return new_value
|
|
|
|
|
2010-11-19 23:39:35 +08:00
|
|
|
def decr(self, key, delta=1, version=None):
|
2009-03-11 21:27:03 +08:00
|
|
|
"""
|
|
|
|
Subtract delta from value in the cache. If the key does not exist, raise
|
|
|
|
a ValueError exception.
|
|
|
|
"""
|
2010-11-19 23:39:35 +08:00
|
|
|
return self.incr(key, -delta, version=version)
|
2009-03-11 21:27:03 +08:00
|
|
|
|
2008-07-26 11:58:31 +08:00
|
|
|
def __contains__(self, key):
|
|
|
|
"""
|
|
|
|
Returns True if the key is in the cache and has not expired.
|
|
|
|
"""
|
|
|
|
# This is a separate method, rather than just a copy of has_key(),
|
|
|
|
# so that it always has the same functionality as has_key(), even
|
|
|
|
# if a subclass overrides it.
|
|
|
|
return self.has_key(key)
|
2010-01-27 16:21:35 +08:00
|
|
|
|
2013-05-18 18:54:59 +08:00
|
|
|
def set_many(self, data, timeout=DEFAULT_TIMEOUT, version=None):
|
2010-01-27 16:21:35 +08:00
|
|
|
"""
|
|
|
|
Set a bunch of values in the cache at once from a dict of key/value
|
|
|
|
pairs. For certain backends (memcached), this is much more efficient
|
|
|
|
than calling set() multiple times.
|
|
|
|
|
|
|
|
If timeout is given, that timeout will be used for the key; otherwise
|
|
|
|
the default cache timeout will be used.
|
|
|
|
"""
|
|
|
|
for key, value in data.items():
|
2010-11-19 23:39:35 +08:00
|
|
|
self.set(key, value, timeout=timeout, version=version)
|
2010-01-27 16:21:35 +08:00
|
|
|
|
2010-11-19 23:39:35 +08:00
|
|
|
def delete_many(self, keys, version=None):
|
2010-01-27 16:21:35 +08:00
|
|
|
"""
|
2015-12-30 07:42:05 +08:00
|
|
|
Delete a bunch of values in the cache at once. For certain backends
|
2010-01-27 16:21:35 +08:00
|
|
|
(memcached), this is much more efficient than calling delete() multiple
|
|
|
|
times.
|
|
|
|
"""
|
|
|
|
for key in keys:
|
2010-11-19 23:39:35 +08:00
|
|
|
self.delete(key, version=version)
|
2010-01-27 16:21:35 +08:00
|
|
|
|
|
|
|
def clear(self):
|
|
|
|
"""Remove *all* values from the cache at once."""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of BaseCache must provide a clear() method')
|
2010-09-13 02:45:26 +08:00
|
|
|
|
|
|
|
def validate_key(self, key):
|
|
|
|
"""
|
|
|
|
Warn about keys that would not be portable to the memcached
|
|
|
|
backend. This encourages (but does not force) writing backend-portable
|
|
|
|
cache code.
|
|
|
|
"""
|
|
|
|
if len(key) > MEMCACHE_MAX_KEY_LENGTH:
|
2016-03-29 06:33:29 +08:00
|
|
|
warnings.warn(
|
|
|
|
'Cache key will cause errors if used with memcached: %r '
|
|
|
|
'(longer than %s)' % (key, MEMCACHE_MAX_KEY_LENGTH), CacheKeyWarning
|
|
|
|
)
|
2010-09-13 02:45:26 +08:00
|
|
|
for char in key:
|
|
|
|
if ord(char) < 33 or ord(char) == 127:
|
2016-03-29 06:33:29 +08:00
|
|
|
warnings.warn(
|
|
|
|
'Cache key contains characters that will cause errors if '
|
|
|
|
'used with memcached: %r' % key, CacheKeyWarning
|
|
|
|
)
|
2016-04-05 03:20:55 +08:00
|
|
|
break
|
2010-09-13 02:45:26 +08:00
|
|
|
|
2010-11-19 23:39:35 +08:00
|
|
|
def incr_version(self, key, delta=1, version=None):
|
|
|
|
"""Adds delta to the cache version for the supplied key. Returns the
|
|
|
|
new version.
|
|
|
|
"""
|
|
|
|
if version is None:
|
|
|
|
version = self.version
|
|
|
|
|
|
|
|
value = self.get(key, version=version)
|
|
|
|
if value is None:
|
|
|
|
raise ValueError("Key '%s' not found" % key)
|
|
|
|
|
2013-11-04 02:08:55 +08:00
|
|
|
self.set(key, value, version=version + delta)
|
2010-11-19 23:39:35 +08:00
|
|
|
self.delete(key, version=version)
|
2013-11-04 02:08:55 +08:00
|
|
|
return version + delta
|
2010-11-19 23:39:35 +08:00
|
|
|
|
|
|
|
def decr_version(self, key, delta=1, version=None):
|
2015-11-03 17:55:10 +08:00
|
|
|
"""Subtracts delta from the cache version for the supplied key. Returns
|
2010-11-19 23:39:35 +08:00
|
|
|
the new version.
|
|
|
|
"""
|
|
|
|
return self.incr_version(key, -delta, version)
|
2012-07-18 20:00:31 +08:00
|
|
|
|
|
|
|
def close(self, **kwargs):
|
|
|
|
"""Close the cache connection"""
|
|
|
|
pass
|