Cache access to cache object

This commit is contained in:
Raphael Michel
2017-10-17 18:00:07 +02:00
parent 7e1e259897
commit 03133dc1fd
12 changed files with 115 additions and 23 deletions

View File

@@ -11,17 +11,19 @@ class NamespacedCache:
def __init__(self, prefixkey: str, cache: str='default'):
self.cache = caches[cache]
self.prefixkey = prefixkey
self._last_prefix = None
def _prefix_key(self, original_key: str) -> str:
def _prefix_key(self, original_key: str, known_prefix=None) -> str:
# Race conditions can happen here, but should be very very rare.
# We could only handle this by going _really_ lowlevel using
# memcached's `add` keyword instead of `set`.
# See also:
# https://code.google.com/p/memcached/wiki/NewProgrammingTricks#Namespacing
prefix = self.cache.get(self.prefixkey)
prefix = known_prefix or self.cache.get(self.prefixkey)
if prefix is None:
prefix = int(time.time())
self.cache.set(self.prefixkey, prefix)
self._last_prefix = prefix
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
@@ -32,6 +34,7 @@ class NamespacedCache:
return key.split(":", 2 + self.prefixkey.count(":"))[-1]
def clear(self) -> None:
self._last_prefix = None
try:
prefix = self.cache.incr(self.prefixkey, 1)
except ValueError:
@@ -42,7 +45,7 @@ class NamespacedCache:
return self.cache.set(self._prefix_key(key), value, timeout)
def get(self, key: str) -> str:
return self.cache.get(self._prefix_key(key))
return self.cache.get(self._prefix_key(key, known_prefix=self._last_prefix))
def get_many(self, keys: List[str]) -> Dict[str, str]:
values = self.cache.get_many([self._prefix_key(key) for key in keys])

View File

@@ -264,7 +264,7 @@ class Event(EventMixin, LoggedModel):
def save(self, *args, **kwargs):
obj = super().save(*args, **kwargs)
self.get_cache().clear()
self.cache.clear()
return obj
def get_plugins(self) -> "list[str]":
@@ -281,6 +281,19 @@ class Event(EventMixin, LoggedModel):
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.
.. deprecated:: 1.9
Use the property ``cache`` instead.
"""
return self.cache
@cached_property
def cache(self):
"""
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 ObjectRelatedCache
@@ -578,6 +591,16 @@ class SubEvent(EventMixin, LoggedModel):
data.update({v.property.name: v.value for v in self.meta_values.select_related('property').all()})
return data
def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
if self.event:
self.event.cache.clear()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.event:
self.event.cache.clear()
def generate_invite_token():
return get_random_string(length=32, allowed_chars=string.ascii_lowercase + string.digits)
@@ -677,6 +700,16 @@ class EventMetaValue(LoggedModel):
class Meta:
unique_together = ('event', 'property')
def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
if self.event:
self.event.cache.clear()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.event:
self.event.cache.clear()
class SubEventMetaValue(LoggedModel):
"""
@@ -697,3 +730,13 @@ class SubEventMetaValue(LoggedModel):
class Meta:
unique_together = ('subevent', 'property')
def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
if self.subevent:
self.subevent.event.cache.clear()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.subevent:
self.subevent.event.cache.clear()

View File

@@ -66,12 +66,12 @@ class ItemCategory(LoggedModel):
def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
if self.event:
self.event.get_cache().clear()
self.event.cache.clear()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.event:
self.event.get_cache().clear()
self.event.cache.clear()
@property
def sortkey(self):
@@ -104,6 +104,16 @@ class SubEventItem(models.Model):
item = models.ForeignKey('Item', on_delete=models.CASCADE)
price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
if self.subevent:
self.subevent.event.cache.clear()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.subevent:
self.subevent.event.cache.clear()
class SubEventItemVariation(models.Model):
"""
@@ -121,6 +131,16 @@ class SubEventItemVariation(models.Model):
variation = models.ForeignKey('ItemVariation', on_delete=models.CASCADE)
price = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True)
def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
if self.subevent:
self.subevent.event.cache.clear()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.subevent:
self.subevent.event.cache.clear()
class Item(LoggedModel):
"""
@@ -290,12 +310,12 @@ class Item(LoggedModel):
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.event:
self.event.get_cache().clear()
self.event.cache.clear()
def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
if self.event:
self.event.get_cache().clear()
self.event.cache.clear()
def tax(self, price=None, base_price_is='auto'):
price = price if price is not None else self.default_price
@@ -418,12 +438,12 @@ class ItemVariation(models.Model):
def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
if self.item:
self.item.event.get_cache().clear()
self.item.event.cache.clear()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.item:
self.item.event.get_cache().clear()
self.item.event.cache.clear()
def check_quotas(self, ignored_quotas=None, count_waitinglist=True, subevent=None, _cache=None) -> Tuple[int, int]:
"""
@@ -595,12 +615,12 @@ class Question(LoggedModel):
def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
if self.event:
self.event.get_cache().clear()
self.event.cache.clear()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.event:
self.event.get_cache().clear()
self.event.cache.clear()
@property
def sortkey(self):
@@ -719,13 +739,13 @@ class Quota(LoggedModel):
def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
if self.event:
self.event.get_cache().clear()
self.event.cache.clear()
def save(self, *args, **kwargs):
clear_cache = kwargs.pop('clear_cache', True)
super().save(*args, **kwargs)
if self.event and clear_cache:
self.event.get_cache().clear()
self.event.cache.clear()
def rebuild_cache(self, now_dt=None):
self.cached_availability_time = None

View File

@@ -3,6 +3,7 @@ import string
from django.core.validators import RegexValidator
from django.db import models
from django.utils.crypto import get_random_string
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from pretix.base.models.base import LoggedModel
@@ -62,6 +63,19 @@ class Organizer(LoggedModel):
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.
.. deprecated:: 1.9
Use the property ``cache`` instead.
"""
return self.cache
@cached_property
def cache(self):
"""
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

View File

@@ -182,3 +182,13 @@ class TaxRule(LoggedModel):
# Consumer in different EU country / invalid VAT
return True
def delete(self, *args, **kwargs):
super().delete(*args, **kwargs)
if self.event:
self.event.cache.clear()
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.event:
self.event.cache.clear()

View File

@@ -315,11 +315,11 @@ class Voucher(LoggedModel):
def save(self, *args, **kwargs):
self.code = self.code.upper()
super().save(*args, **kwargs)
self.event.get_cache().set('vouchers_exist', True)
self.event.cache.set('vouchers_exist', True)
def delete(self, using=None, keep_parents=False):
super().delete(using, keep_parents)
self.event.get_cache().delete('vouchers_exist')
self.event.cache.delete('vouchers_exist')
def is_in_cart(self) -> bool:
"""

View File

@@ -68,7 +68,9 @@ class OrganizerUpdateForm(OrganizerForm):
KnownDomain.objects.create(organizer=instance, domainname=self.cleaned_data['domain'])
elif current_domain:
current_domain.delete()
instance.get_cache().clear()
instance.cache.clear()
for ev in instance.events.all():
ev.cache.clear()
return instance

View File

@@ -59,7 +59,7 @@ def eventreverse(obj, name, kwargs=None):
c = None
if not kwargs:
c = obj.get_cache()
c = obj.cache
url = c.get('urlrev_{}'.format(name))
if url:
return url

View File

@@ -25,7 +25,7 @@ def control_nav_import(sender, request=None, **kwargs):
def clear_cache(sender, *args, **kwargs):
cache = sender.get_cache()
cache = sender.cache
cache.delete('statistics_obd_data')
cache.delete('statistics_obp_data')
cache.delete('statistics_rev_data')

View File

@@ -32,7 +32,7 @@ class IndexView(EventPermissionRequiredMixin, ChartContainingView, TemplateView)
except SubEvent.DoesNotExist:
pass
cache = self.request.event.get_cache()
cache = self.request.event.cache
ckey = str(subevent.pk) if subevent else 'all'
# Orders by day

View File

@@ -175,10 +175,10 @@ class EventIndex(EventViewMixin, CartMixin, TemplateView):
context['subevent'] = self.subevent
context['cart'] = self.get_cart()
context['has_addon_choices'] = get_cart(self.request).filter(item__addons__isnull=False).exists()
vouchers_exist = self.request.event.get_cache().get('vouchers_exist')
vouchers_exist = self.request.event.cache.get('vouchers_exist')
if vouchers_exist is None:
vouchers_exist = self.request.event.vouchers.exists()
self.request.event.get_cache().set('vouchers_exist', vouchers_exist)
self.request.event.cache.set('vouchers_exist', vouchers_exist)
context['vouchers_exist'] = vouchers_exist
context['ev'] = self.subevent or self.request.event
if self.subevent: