mirror of
https://github.com/jazzband/django-constance.git
synced 2026-05-28 16:58:21 +00:00
Add caching redis backend (#466)
* Add caching redis backend * fix mget implementation * fix lock
This commit is contained in:
parent
01a8dc54d3
commit
8b34b63fd0
4 changed files with 69 additions and 3 deletions
|
|
@ -1,10 +1,12 @@
|
|||
from pickle import loads, dumps
|
||||
from threading import RLock
|
||||
from time import monotonic
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from . import Backend
|
||||
from .. import settings, utils, signals, config
|
||||
|
||||
from pickle import loads, dumps
|
||||
|
||||
|
||||
class RedisBackend(Backend):
|
||||
|
||||
|
|
@ -48,3 +50,44 @@ class RedisBackend(Backend):
|
|||
signals.config_updated.send(
|
||||
sender=config, key=key, old_value=old_value, new_value=value
|
||||
)
|
||||
|
||||
|
||||
class CachingRedisBackend(RedisBackend):
|
||||
_sentinel = object()
|
||||
_lock = RLock()
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._timeout = settings.REDIS_CACHE_TIMEOUT
|
||||
self._cache = {}
|
||||
self._sentinel = object()
|
||||
|
||||
def _has_expired(self, value):
|
||||
return value[0] <= monotonic()
|
||||
|
||||
def _cache_value(self, key, new_value):
|
||||
self._cache[key] = (monotonic() + self._timeout, new_value)
|
||||
|
||||
def get(self, key):
|
||||
value = self._cache.get(key, self._sentinel)
|
||||
|
||||
if value is self._sentinel or self._has_expired(value):
|
||||
with self._lock:
|
||||
new_value = super().get(key)
|
||||
self._cache_value(key, new_value)
|
||||
return new_value
|
||||
|
||||
return value[1]
|
||||
|
||||
def set(self, key, value):
|
||||
with self._lock:
|
||||
super().set(key, value)
|
||||
self._cache_value(key, value)
|
||||
|
||||
def mget(self, keys):
|
||||
if not keys:
|
||||
return
|
||||
for key in keys:
|
||||
value = self.get(key)
|
||||
if value is not None:
|
||||
yield key, value
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ DATABASE_PREFIX = getattr(settings, 'CONSTANCE_DATABASE_PREFIX', '')
|
|||
|
||||
REDIS_PREFIX = getattr(settings, 'CONSTANCE_REDIS_PREFIX', 'constance:')
|
||||
|
||||
REDIS_CACHE_TIMEOUT = getattr(settings, 'CONSTANCE_REDIS_CACHE_TIMEOUT', 60)
|
||||
|
||||
REDIS_CONNECTION_CLASS = getattr(
|
||||
settings,
|
||||
'CONSTANCE_REDIS_CONNECTION_CLASS',
|
||||
|
|
|
|||
|
|
@ -23,6 +23,14 @@ to add it to your project settings::
|
|||
|
||||
CONSTANCE_BACKEND = 'constance.backends.redisd.RedisBackend'
|
||||
|
||||
Default redis backend retrieves values every time. There is another redis backend with local cache.
|
||||
`CachingRedisBackend` stores the value from a redis to memory at first access and checks a value ttl at next.
|
||||
Configuration installation is simple::
|
||||
|
||||
CONSTANCE_BACKEND = 'constance.backends.redisd.CachingRedisBackend'
|
||||
# optionally set a value ttl
|
||||
CONSTANCE_REDIS_CACHE_TIMEOUT = 60
|
||||
|
||||
.. _`redis-py`: https://pypi.python.org/pypi/redis
|
||||
|
||||
Settings
|
||||
|
|
@ -77,6 +85,12 @@ objects when storing in the Redis database. Defaults to ``pickle.DEFAULT_PROTOCO
|
|||
You might want to pin this value to a specific protocol number, since ``pickle.DEFAULT_PROTOCOL``
|
||||
means different things between versions of Python.
|
||||
|
||||
``CONSTANCE_REDIS_CACHE_TIMEOUT``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The (optional) ttl of values in seconds used by `CachingRedisBackend` for storing in a local cache.
|
||||
Defaults to `60` seconds.
|
||||
|
||||
Database
|
||||
--------
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,19 @@ from tests.storage import StorageTestsMixin
|
|||
|
||||
class TestRedis(StorageTestsMixin, TestCase):
|
||||
|
||||
_BACKEND = 'constance.backends.redisd.RedisBackend'
|
||||
|
||||
def setUp(self):
|
||||
self.old_backend = settings.BACKEND
|
||||
settings.BACKEND = 'constance.backends.redisd.RedisBackend'
|
||||
settings.BACKEND = self._BACKEND
|
||||
super().setUp()
|
||||
self.config._backend._rd.clear()
|
||||
|
||||
def tearDown(self):
|
||||
self.config._backend._rd.clear()
|
||||
settings.BACKEND = self.old_backend
|
||||
|
||||
|
||||
class TestCachingRedis(TestRedis):
|
||||
|
||||
_BACKEND = 'constance.backends.redisd.CachingRedisBackend'
|
||||
|
|
|
|||
Loading…
Reference in a new issue