Generalized EventRelatedCache to ObjectRelatedCache

This commit is contained in:
Raphael Michel
2015-10-18 12:17:01 +02:00
parent 651d82c128
commit d7d6e74c04
2 changed files with 46 additions and 27 deletions

View File

@@ -2,29 +2,14 @@ import hashlib
import time
from django.core.cache import caches
from pretix.base.models import Event
from django.db.models import Model
class EventRelatedCache:
"""
This object behaves exactly like the cache implementations by Django
but with one important difference: It stores all keys related to a
certain event, so you pass an event when creating this object and if
you store data in this cache, it is only stored for this event. The
main purpose of this is to be able to flush all cached data related
to this event at once.
class NamespacedCache:
The EventRelatedCache instance itself is stateless, all state is
stored in the cache backend, so you can instantiate this class as many
times as you want.
"""
def __init__(self, event: Event, cache: str='default'):
assert isinstance(event, Event)
def __init__(self, prefixkey, cache: str='default'):
self.cache = caches[cache]
self.event = event
self.prefixkey = 'event:%s' % self.event.pk
self.prefixkey = prefixkey
def _prefix_key(self, original_key: str) -> str:
# Race conditions can happen here, but should be very very rare.
@@ -36,15 +21,14 @@ class EventRelatedCache:
if prefix is None:
prefix = int(time.time())
self.cache.set(self.prefixkey, prefix)
key = 'event:%s:%d:%s' % (self.event.pk, prefix, original_key)
key = '%s:%d:%s' % (self.prefixkey, prefix, original_key)
if len(key) > 200: # Hash long keys, as memcached has a length limit
# TODO: Use a more efficient, non-cryptographic hash algorithm
key = hashlib.sha256(key.encode("UTF-8")).hexdigest()
return key
@staticmethod
def _strip_prefix(key: str) -> str:
return key.split(":", 3)[-1] if 'event:' in key else key
def _strip_prefix(self, key: str) -> str:
return key.split(":", 2 + self.prefixkey.count(":"))[-1]
def clear(self):
try:
@@ -86,3 +70,22 @@ class EventRelatedCache:
def close(self): # NOQA
pass
class ObjectRelatedCache(NamespacedCache):
"""
This object behaves exactly like the cache implementations by Django
but with one important difference: It stores all keys related to a
certain object, so you pass an object when creating this object and if
you store data in this cache, it is only stored for this object. The
main purpose of this is to be able to flush all cached data related
to this object at once.
The ObjectRelatedCache instance itself is stateless, all state is
stored in the cache backend, so you can instantiate this class as many
times as you want.
"""
def __init__(self, obj, cache: str='default'):
assert isinstance(obj, Model)
super().__init__('%s:%s' % (obj._meta.object_name, obj.pk), cache)

View File

@@ -260,6 +260,11 @@ class Organizer(Versionable):
def __str__(self):
return self.name
def save(self, *args, **kwargs):
obj = super().save(*args, **kwargs)
self.get_cache().clear()
return obj
@cached_property
def settings(self) -> SettingsProxy:
"""
@@ -267,6 +272,17 @@ class Organizer(Versionable):
"""
return SettingsProxy(self, type=OrganizerSetting)
def get_cache(self) -> "pretix.base.cache.ObjectRelatedCache":
"""
Returns an :py:class:`ObjectRelatedCache` object. This behaves equivalent to
Django's built-in cache backends, but puts you into an isolated environment for
this organizer, so you don't have to prefix your cache keys. In addition, the cache
is being cleared every time the organizer changes.
"""
from pretix.base.cache import ObjectRelatedCache
return ObjectRelatedCache(self)
class OrganizerPermission(Versionable):
"""
@@ -414,16 +430,16 @@ class Event(Versionable):
"DATETIME_FORMAT" if self.settings.show_times else "DATE_FORMAT"
)
def get_cache(self) -> "pretix.base.cache.EventRelatedCache":
def get_cache(self) -> "pretix.base.cache.ObjectRelatedCache":
"""
Returns an :py:class:`EventRelatedCache` object. This behaves equivalent to
Returns an :py:class:`ObjectRelatedCache` object. This behaves equivalent to
Django's built-in cache backends, but puts you into an isolated environment for
this event, so you don't have to prefix your cache keys. In addition, the cache
is being cleared every time the event or one of its related objects change.
"""
from pretix.base.cache import EventRelatedCache
from pretix.base.cache import ObjectRelatedCache
return EventRelatedCache(self)
return ObjectRelatedCache(self)
@cached_property
def settings(self) -> SettingsProxy: