django-constance/constance/backends/redisd.py

94 lines
2.8 KiB
Python
Raw Normal View History

from threading import RLock
from time import monotonic
from django.core.exceptions import ImproperlyConfigured
2024-07-05 14:38:26 +00:00
from constance import config
from constance import settings
from constance import signals
from constance import utils
from constance.backends import Backend
from constance.codecs import dumps
from constance.codecs import loads
class RedisBackend(Backend):
def __init__(self):
super().__init__()
self._prefix = settings.REDIS_PREFIX
2014-11-25 21:19:52 +00:00
connection_cls = settings.REDIS_CONNECTION_CLASS
if connection_cls is not None:
self._rd = utils.import_module_attr(connection_cls)()
else:
try:
import redis
except ImportError:
2024-07-05 14:38:26 +00:00
raise ImproperlyConfigured('The Redis backend requires redis-py to be installed.') from None
if isinstance(settings.REDIS_CONNECTION, str):
self._rd = redis.from_url(settings.REDIS_CONNECTION)
else:
self._rd = redis.Redis(**settings.REDIS_CONNECTION)
def add_prefix(self, key):
return f'{self._prefix}{key}'
def get(self, key):
value = self._rd.get(self.add_prefix(key))
if value:
return loads(value)
return None
def mget(self, keys):
if not keys:
return
prefixed_keys = [self.add_prefix(key) for key in keys]
for key, value in zip(keys, self._rd.mget(prefixed_keys)):
if value:
yield key, loads(value)
def set(self, key, value):
old_value = self.get(key)
self._rd.set(self.add_prefix(key), dumps(value))
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