You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
6.2 KiB
216 lines
6.2 KiB
5 years ago
|
import operator
|
||
|
from ..util.compat import py3k
|
||
|
|
||
|
|
||
|
class NoValue(object):
|
||
|
"""Describe a missing cache value.
|
||
|
|
||
|
The :attr:`.NO_VALUE` module global
|
||
|
should be used.
|
||
|
|
||
|
"""
|
||
|
@property
|
||
|
def payload(self):
|
||
|
return self
|
||
|
|
||
|
def __repr__(self):
|
||
|
"""Ensure __repr__ is a consistent value in case NoValue is used to
|
||
|
fill another cache key.
|
||
|
|
||
|
"""
|
||
|
return '<dogpile.cache.api.NoValue object>'
|
||
|
|
||
|
if py3k:
|
||
|
def __bool__(self): # pragma NO COVERAGE
|
||
|
return False
|
||
|
else:
|
||
|
def __nonzero__(self): # pragma NO COVERAGE
|
||
|
return False
|
||
|
|
||
|
|
||
|
NO_VALUE = NoValue()
|
||
|
"""Value returned from ``get()`` that describes
|
||
|
a key not present."""
|
||
|
|
||
|
|
||
|
class CachedValue(tuple):
|
||
|
"""Represent a value stored in the cache.
|
||
|
|
||
|
:class:`.CachedValue` is a two-tuple of
|
||
|
``(payload, metadata)``, where ``metadata``
|
||
|
is dogpile.cache's tracking information (
|
||
|
currently the creation time). The metadata
|
||
|
and tuple structure is pickleable, if
|
||
|
the backend requires serialization.
|
||
|
|
||
|
"""
|
||
|
payload = property(operator.itemgetter(0))
|
||
|
"""Named accessor for the payload."""
|
||
|
|
||
|
metadata = property(operator.itemgetter(1))
|
||
|
"""Named accessor for the dogpile.cache metadata dictionary."""
|
||
|
|
||
|
def __new__(cls, payload, metadata):
|
||
|
return tuple.__new__(cls, (payload, metadata))
|
||
|
|
||
|
def __reduce__(self):
|
||
|
return CachedValue, (self.payload, self.metadata)
|
||
|
|
||
|
|
||
|
class CacheBackend(object):
|
||
|
"""Base class for backend implementations."""
|
||
|
|
||
|
key_mangler = None
|
||
|
"""Key mangling function.
|
||
|
|
||
|
May be None, or otherwise declared
|
||
|
as an ordinary instance method.
|
||
|
|
||
|
"""
|
||
|
|
||
|
def __init__(self, arguments): # pragma NO COVERAGE
|
||
|
"""Construct a new :class:`.CacheBackend`.
|
||
|
|
||
|
Subclasses should override this to
|
||
|
handle the given arguments.
|
||
|
|
||
|
:param arguments: The ``arguments`` parameter
|
||
|
passed to :func:`.make_registry`.
|
||
|
|
||
|
"""
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
@classmethod
|
||
|
def from_config_dict(cls, config_dict, prefix):
|
||
|
prefix_len = len(prefix)
|
||
|
return cls(
|
||
|
dict(
|
||
|
(key[prefix_len:], config_dict[key])
|
||
|
for key in config_dict
|
||
|
if key.startswith(prefix)
|
||
|
)
|
||
|
)
|
||
|
|
||
|
def has_lock_timeout(self):
|
||
|
return False
|
||
|
|
||
|
def get_mutex(self, key):
|
||
|
"""Return an optional mutexing object for the given key.
|
||
|
|
||
|
This object need only provide an ``acquire()``
|
||
|
and ``release()`` method.
|
||
|
|
||
|
May return ``None``, in which case the dogpile
|
||
|
lock will use a regular ``threading.Lock``
|
||
|
object to mutex concurrent threads for
|
||
|
value creation. The default implementation
|
||
|
returns ``None``.
|
||
|
|
||
|
Different backends may want to provide various
|
||
|
kinds of "mutex" objects, such as those which
|
||
|
link to lock files, distributed mutexes,
|
||
|
memcached semaphores, etc. Whatever
|
||
|
kind of system is best suited for the scope
|
||
|
and behavior of the caching backend.
|
||
|
|
||
|
A mutex that takes the key into account will
|
||
|
allow multiple regenerate operations across
|
||
|
keys to proceed simultaneously, while a mutex
|
||
|
that does not will serialize regenerate operations
|
||
|
to just one at a time across all keys in the region.
|
||
|
The latter approach, or a variant that involves
|
||
|
a modulus of the given key's hash value,
|
||
|
can be used as a means of throttling the total
|
||
|
number of value recreation operations that may
|
||
|
proceed at one time.
|
||
|
|
||
|
"""
|
||
|
return None
|
||
|
|
||
|
def get(self, key): # pragma NO COVERAGE
|
||
|
"""Retrieve a value from the cache.
|
||
|
|
||
|
The returned value should be an instance of
|
||
|
:class:`.CachedValue`, or ``NO_VALUE`` if
|
||
|
not present.
|
||
|
|
||
|
"""
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
def get_multi(self, keys): # pragma NO COVERAGE
|
||
|
"""Retrieve multiple values from the cache.
|
||
|
|
||
|
The returned value should be a list, corresponding
|
||
|
to the list of keys given.
|
||
|
|
||
|
.. versionadded:: 0.5.0
|
||
|
|
||
|
"""
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
def set(self, key, value): # pragma NO COVERAGE
|
||
|
"""Set a value in the cache.
|
||
|
|
||
|
The key will be whatever was passed
|
||
|
to the registry, processed by the
|
||
|
"key mangling" function, if any.
|
||
|
The value will always be an instance
|
||
|
of :class:`.CachedValue`.
|
||
|
|
||
|
"""
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
def set_multi(self, mapping): # pragma NO COVERAGE
|
||
|
"""Set multiple values in the cache.
|
||
|
|
||
|
``mapping`` is a dict in which
|
||
|
the key will be whatever was passed
|
||
|
to the registry, processed by the
|
||
|
"key mangling" function, if any.
|
||
|
The value will always be an instance
|
||
|
of :class:`.CachedValue`.
|
||
|
|
||
|
When implementing a new :class:`.CacheBackend` or cutomizing via
|
||
|
:class:`.ProxyBackend`, be aware that when this method is invoked by
|
||
|
:meth:`.Region.get_or_create_multi`, the ``mapping`` values are the
|
||
|
same ones returned to the upstream caller. If the subclass alters the
|
||
|
values in any way, it must not do so 'in-place' on the ``mapping`` dict
|
||
|
-- that will have the undesirable effect of modifying the returned
|
||
|
values as well.
|
||
|
|
||
|
.. versionadded:: 0.5.0
|
||
|
|
||
|
"""
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
def delete(self, key): # pragma NO COVERAGE
|
||
|
"""Delete a value from the cache.
|
||
|
|
||
|
The key will be whatever was passed
|
||
|
to the registry, processed by the
|
||
|
"key mangling" function, if any.
|
||
|
|
||
|
The behavior here should be idempotent,
|
||
|
that is, can be called any number of times
|
||
|
regardless of whether or not the
|
||
|
key exists.
|
||
|
"""
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
def delete_multi(self, keys): # pragma NO COVERAGE
|
||
|
"""Delete multiple values from the cache.
|
||
|
|
||
|
The key will be whatever was passed
|
||
|
to the registry, processed by the
|
||
|
"key mangling" function, if any.
|
||
|
|
||
|
The behavior here should be idempotent,
|
||
|
that is, can be called any number of times
|
||
|
regardless of whether or not the
|
||
|
key exists.
|
||
|
|
||
|
.. versionadded:: 0.5.0
|
||
|
|
||
|
"""
|
||
|
raise NotImplementedError()
|