forked from CGM_Public/pretix_original
Generalized EventRelatedCache to ObjectRelatedCache
This commit is contained in:
@@ -2,29 +2,14 @@ import hashlib
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from django.core.cache import caches
|
from django.core.cache import caches
|
||||||
|
from django.db.models import Model
|
||||||
from pretix.base.models import Event
|
|
||||||
|
|
||||||
|
|
||||||
class EventRelatedCache:
|
class NamespacedCache:
|
||||||
"""
|
|
||||||
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.
|
|
||||||
|
|
||||||
The EventRelatedCache instance itself is stateless, all state is
|
def __init__(self, prefixkey, cache: str='default'):
|
||||||
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)
|
|
||||||
self.cache = caches[cache]
|
self.cache = caches[cache]
|
||||||
self.event = event
|
self.prefixkey = prefixkey
|
||||||
self.prefixkey = 'event:%s' % self.event.pk
|
|
||||||
|
|
||||||
def _prefix_key(self, original_key: str) -> str:
|
def _prefix_key(self, original_key: str) -> str:
|
||||||
# Race conditions can happen here, but should be very very rare.
|
# Race conditions can happen here, but should be very very rare.
|
||||||
@@ -36,15 +21,14 @@ class EventRelatedCache:
|
|||||||
if prefix is None:
|
if prefix is None:
|
||||||
prefix = int(time.time())
|
prefix = int(time.time())
|
||||||
self.cache.set(self.prefixkey, prefix)
|
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
|
if len(key) > 200: # Hash long keys, as memcached has a length limit
|
||||||
# TODO: Use a more efficient, non-cryptographic hash algorithm
|
# TODO: Use a more efficient, non-cryptographic hash algorithm
|
||||||
key = hashlib.sha256(key.encode("UTF-8")).hexdigest()
|
key = hashlib.sha256(key.encode("UTF-8")).hexdigest()
|
||||||
return key
|
return key
|
||||||
|
|
||||||
@staticmethod
|
def _strip_prefix(self, key: str) -> str:
|
||||||
def _strip_prefix(key: str) -> str:
|
return key.split(":", 2 + self.prefixkey.count(":"))[-1]
|
||||||
return key.split(":", 3)[-1] if 'event:' in key else key
|
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
try:
|
try:
|
||||||
@@ -86,3 +70,22 @@ class EventRelatedCache:
|
|||||||
|
|
||||||
def close(self): # NOQA
|
def close(self): # NOQA
|
||||||
pass
|
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)
|
||||||
|
|||||||
@@ -260,6 +260,11 @@ class Organizer(Versionable):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
obj = super().save(*args, **kwargs)
|
||||||
|
self.get_cache().clear()
|
||||||
|
return obj
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def settings(self) -> SettingsProxy:
|
def settings(self) -> SettingsProxy:
|
||||||
"""
|
"""
|
||||||
@@ -267,6 +272,17 @@ class Organizer(Versionable):
|
|||||||
"""
|
"""
|
||||||
return SettingsProxy(self, type=OrganizerSetting)
|
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):
|
class OrganizerPermission(Versionable):
|
||||||
"""
|
"""
|
||||||
@@ -414,16 +430,16 @@ class Event(Versionable):
|
|||||||
"DATETIME_FORMAT" if self.settings.show_times else "DATE_FORMAT"
|
"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
|
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
|
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.
|
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
|
@cached_property
|
||||||
def settings(self) -> SettingsProxy:
|
def settings(self) -> SettingsProxy:
|
||||||
|
|||||||
Reference in New Issue
Block a user