Added database cache autofill feature.

This refs #47 and #45.
This commit is contained in:
Jannis Leidel 2014-11-21 20:08:20 +01:00
parent 16bb8d50e6
commit 8b0ed361d6
6 changed files with 72 additions and 29 deletions

View file

@ -125,17 +125,9 @@ you need to install this library, too. E.g.::
Alternatively follow the backend specific installation instructions above.
The database backend has the ability to automatically cache the config
values and clear them when saving. You need to set the following setting
to enable this feature::
CONSTANCE_DATABASE_CACHE_BACKEND = 'memcached://127.0.0.1:11211/'
.. note:: This won't work with a cache backend that doesn't support
cross-process caching, because correct cache invalidation
can't be guaranteed.
Starting in Django 1.3 you can alternatively use the name of an entry of
the ``CACHES`` setting. E.g.::
values and clear them when saving. Assuming you have a ``CACHES`` setting set
you need to set the the ``CONSTANCE_DATABASE_CACHE_BACKEND`` setting to enable
this feature::
CACHES = {
'default': {
@ -145,6 +137,14 @@ the ``CACHES`` setting. E.g.::
}
CONSTANCE_DATABASE_CACHE_BACKEND = 'default'
.. note:: This won't work with a cache backend that doesn't support
cross-process caching, because correct cache invalidation
can't be guaranteed.
.. note:: By default Constance will autofill the cache on startup and after
saving any of the config values. If you want to disable the cache simply
set the ``CONSTANCE_DATABASE_CACHE_AUTOFILL_TIMEOUT`` setting to ``None``.
Just like the Redis backend you can set an optional prefix that is used during
database interactions. To keep backward compatibility it defaults to ``''``
(an empty string). To use something else do this::
@ -229,6 +229,13 @@ Screenshots
Changelog
---------
v1.0 (unreleased)
~~~~~~~~~~~~~~~~~
* Added new autofill feature for the database backend cache which is enabled
by default.
v0.6 (2013/04/12)
~~~~~~~~~~~~~~~~~

View file

@ -1,7 +1,7 @@
from constance.config import Config
try:
from django.apps import AppConfig
from django.apps import AppConfig # noqa
except ImportError:
config = Config()
else:

View file

@ -2,6 +2,7 @@ from django.apps import AppConfig
from constance.config import Config
from django.utils.translation import ugettext_lazy as _
class ConstanceConfig(AppConfig):
name = 'constance'
verbose_name = _('Constance')

View file

@ -10,32 +10,49 @@ except ImportError:
from constance.backends import Backend
from constance import settings
db_cache = None
if settings.DATABASE_CACHE_BACKEND:
db_cache = get_cache(settings.DATABASE_CACHE_BACKEND)
if isinstance(db_cache, LocMemCache):
raise ImproperlyConfigured(
"The CONSTANCE_DATABASE_CACHE_BACKEND setting refers to a "
"subclass of Django's local-memory backend (%r). Please set "
"it to a backend that supports cross-process caching."
% settings.DATABASE_CACHE_BACKEND)
class DatabaseBackend(Backend):
def __init__(self):
from constance.backends.database.models import Constance
self._model = Constance
self._prefix = settings.DATABASE_PREFIX
self._autofill_timeout = settings.DATABASE_CACHE_AUTOFILL_TIMEOUT
self._autofill_cachekey = 'autofilled'
if not self._model._meta.installed:
raise ImproperlyConfigured(
"The constance.backends.database app isn't installed "
"correctly. Make sure it's in your INSTALLED_APPS setting.")
if settings.DATABASE_CACHE_BACKEND:
self._cache = get_cache(settings.DATABASE_CACHE_BACKEND)
if isinstance(self._cache, LocMemCache):
raise ImproperlyConfigured(
"The CONSTANCE_DATABASE_CACHE_BACKEND setting refers to a "
"subclass of Django's local-memory backend (%r). Please set "
"it to a backend that supports cross-process caching."
% settings.DATABASE_CACHE_BACKEND)
else:
self._cache = None
self.autofill()
# Clear simple cache.
post_save.connect(self.clear, sender=self._model)
def add_prefix(self, key):
return "%s%s" % (self._prefix, key)
def autofill(self):
if not self._autofill_timeout or not self._cache:
return
full_cachekey = self.add_prefix(self._autofill_cachekey)
if self._cache.get(full_cachekey):
return
autofill_values = {}
autofill_values[full_cachekey] = 1
for key, value in self.mget(settings.CONFIG.keys()):
autofill_values[self.add_prefix(key)] = value
self._cache.set_many(autofill_values, timeout=self._autofill_timeout)
def mget(self, keys):
if not keys:
return
@ -46,17 +63,18 @@ class DatabaseBackend(Backend):
def get(self, key):
key = self.add_prefix(key)
value = None
if db_cache:
value = db_cache.get(key)
if self._cache:
value = self._cache.get(key)
else:
value = None
if value is None:
try:
value = self._model._default_manager.get(key=key).value
except self._model.DoesNotExist:
pass
else:
if db_cache:
db_cache.add(key, value)
if self._cache:
self._cache.add(key, value)
return value
def set(self, key, value):
@ -68,5 +86,9 @@ class DatabaseBackend(Backend):
constance.save()
def clear(self, sender, instance, created, **kwargs):
if db_cache and not created:
db_cache.delete_many(self.add_prefix(k) for k in settings.CONFIG.keys())
if self._cache and not created:
keys = [self.add_prefix(k)
for k in settings.CONFIG.keys()]
keys.append(self.add_prefix(self._autofill_cachekey))
self._cache.delete_many(keys)
self.autofill()

View file

@ -22,6 +22,10 @@ REDIS_CONNECTION = getattr(settings, 'CONSTANCE_REDIS_CONNECTION',
DATABASE_CACHE_BACKEND = getattr(settings, 'CONSTANCE_DATABASE_CACHE_BACKEND',
None)
DATABASE_CACHE_AUTOFILL_TIMEOUT = getattr(settings,
'CONSTANCE_DATABASE_CACHE_AUTOFILL_TIMEOUT',
60 * 60 * 24)
DATABASE_PREFIX = getattr(settings, 'CONSTANCE_DATABASE_PREFIX', '')
SUPERUSER_ONLY = getattr(settings, 'CONSTANCE_SUPERUSER_ONLY', True)

View file

@ -116,3 +116,12 @@ CONSTANCE_CONFIG = {
}
CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend'
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
CONSTANCE_DATABASE_CACHE_BACKEND = 'default'