per_request_cache
This commit is contained in:
parent
06135cedfd
commit
87b7f00740
6 changed files with 56 additions and 21 deletions
|
@ -1,6 +1,7 @@
|
||||||
import re
|
import re
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
from c3nav.mapdata.utils.cache.local import per_request_cache
|
||||||
from c3nav.mapdata.utils.user import get_user_data_lazy
|
from c3nav.mapdata.utils.user import get_user_data_lazy
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,3 +56,15 @@ class UserDataMiddleware:
|
||||||
def __call__(self, request):
|
def __call__(self, request):
|
||||||
request.user_data = get_user_data_lazy(request)
|
request.user_data = get_user_data_lazy(request)
|
||||||
return self.get_response(request)
|
return self.get_response(request)
|
||||||
|
|
||||||
|
|
||||||
|
class RequestCacheMiddleware:
|
||||||
|
"""
|
||||||
|
Resets the request_cache at the start of every request.
|
||||||
|
"""
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
per_request_cache.clear()
|
||||||
|
return self.get_response(request)
|
||||||
|
|
|
@ -14,6 +14,7 @@ from django.utils.translation import ngettext_lazy
|
||||||
|
|
||||||
from c3nav.mapdata.models import MapUpdate
|
from c3nav.mapdata.models import MapUpdate
|
||||||
from c3nav.mapdata.models.base import SerializableMixin, TitledMixin
|
from c3nav.mapdata.models.base import SerializableMixin, TitledMixin
|
||||||
|
from c3nav.mapdata.utils.cache.local import per_request_cache
|
||||||
|
|
||||||
|
|
||||||
class AccessRestriction(TitledMixin, models.Model):
|
class AccessRestriction(TitledMixin, models.Model):
|
||||||
|
@ -38,20 +39,20 @@ class AccessRestriction(TitledMixin, models.Model):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all() -> set[int]:
|
def get_all() -> set[int]:
|
||||||
cache_key = 'all_access_restrictions:%s' % MapUpdate.current_cache_key()
|
cache_key = 'all_access_restrictions:%s' % MapUpdate.current_cache_key()
|
||||||
access_restriction_ids = cache.get(cache_key, None)
|
access_restriction_ids = per_request_cache.get(cache_key, None)
|
||||||
if access_restriction_ids is None:
|
if access_restriction_ids is None:
|
||||||
access_restriction_ids = set(AccessRestriction.objects.values_list('pk', flat=True))
|
access_restriction_ids = set(AccessRestriction.objects.values_list('pk', flat=True))
|
||||||
cache.set(cache_key, access_restriction_ids, 300)
|
per_request_cache.set(cache_key, access_restriction_ids, 300)
|
||||||
return access_restriction_ids
|
return access_restriction_ids
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_public() -> set[int]:
|
def get_all_public() -> set[int]:
|
||||||
cache_key = 'public_access_restrictions:%s' % MapUpdate.current_cache_key()
|
cache_key = 'public_access_restrictions:%s' % MapUpdate.current_cache_key()
|
||||||
access_restriction_ids = cache.get(cache_key, None)
|
access_restriction_ids = per_request_cache.get(cache_key, None)
|
||||||
if access_restriction_ids is None:
|
if access_restriction_ids is None:
|
||||||
access_restriction_ids = set(AccessRestriction.objects.filter(public=True)
|
access_restriction_ids = set(AccessRestriction.objects.filter(public=True)
|
||||||
.values_list('pk', flat=True))
|
.values_list('pk', flat=True))
|
||||||
cache.set(cache_key, access_restriction_ids, 300)
|
per_request_cache.set(cache_key, access_restriction_ids, 300)
|
||||||
return access_restriction_ids
|
return access_restriction_ids
|
||||||
|
|
||||||
|
|
||||||
|
@ -321,14 +322,14 @@ class AccessPermission(models.Model):
|
||||||
return AccessRestriction.get_all()
|
return AccessRestriction.get_all()
|
||||||
|
|
||||||
cache_key = cls.request_access_permission_key(request)+f':{can_grant}'
|
cache_key = cls.request_access_permission_key(request)+f':{can_grant}'
|
||||||
access_restriction_ids = cache.get(cache_key, None)
|
access_restriction_ids = per_request_cache.get(cache_key, None)
|
||||||
if access_restriction_ids is None:
|
if access_restriction_ids is None:
|
||||||
permissions = cls.get_for_request_with_expire_date(request, can_grant=can_grant)
|
permissions = cls.get_for_request_with_expire_date(request, can_grant=can_grant)
|
||||||
|
|
||||||
access_restriction_ids = set(permissions.keys())
|
access_restriction_ids = set(permissions.keys())
|
||||||
|
|
||||||
expire_date = min((e for e in permissions.values() if e), default=timezone.now() + timedelta(seconds=120))
|
expire_date = min((e for e in permissions.values() if e), default=timezone.now() + timedelta(seconds=120))
|
||||||
cache.set(cache_key, access_restriction_ids, min(300, (expire_date - timezone.now()).total_seconds()))
|
per_request_cache.set(cache_key, access_restriction_ids, min(300, (expire_date - timezone.now()).total_seconds()))
|
||||||
return set(access_restriction_ids) | (set() if can_grant else AccessRestriction.get_all_public())
|
return set(access_restriction_ids) | (set() if can_grant else AccessRestriction.get_all_public())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -23,6 +23,7 @@ from c3nav.mapdata.fields import I18nField
|
||||||
from c3nav.mapdata.grid import grid
|
from c3nav.mapdata.grid import grid
|
||||||
from c3nav.mapdata.models.access import AccessRestrictionMixin
|
from c3nav.mapdata.models.access import AccessRestrictionMixin
|
||||||
from c3nav.mapdata.models.base import SerializableMixin, TitledMixin
|
from c3nav.mapdata.models.base import SerializableMixin, TitledMixin
|
||||||
|
from c3nav.mapdata.utils.cache.local import per_request_cache
|
||||||
from c3nav.mapdata.utils.fields import LocationById
|
from c3nav.mapdata.utils.fields import LocationById
|
||||||
from c3nav.mapdata.utils.models import get_submodels
|
from c3nav.mapdata.utils.models import get_submodels
|
||||||
|
|
||||||
|
@ -620,10 +621,10 @@ class Position(CustomLocationProxyMixin, models.Model):
|
||||||
if not user.is_authenticated:
|
if not user.is_authenticated:
|
||||||
return False
|
return False
|
||||||
cache_key = 'user_has_positions:%d' % user.pk
|
cache_key = 'user_has_positions:%d' % user.pk
|
||||||
result = cache.get(cache_key, None)
|
result = per_request_cache.get(cache_key, None)
|
||||||
if result is None:
|
if result is None:
|
||||||
result = cls.objects.filter(owner=user).exists()
|
result = cls.objects.filter(owner=user).exists()
|
||||||
cache.set(cache_key, result, 600)
|
per_request_cache.set(cache_key, result, 600)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def serialize_position(self, request=None):
|
def serialize_position(self, request=None):
|
||||||
|
|
|
@ -16,6 +16,7 @@ from shapely.ops import unary_union
|
||||||
|
|
||||||
from c3nav.mapdata.tasks import process_map_updates
|
from c3nav.mapdata.tasks import process_map_updates
|
||||||
from c3nav.mapdata.utils.cache.changes import GeometryChangeTracker
|
from c3nav.mapdata.utils.cache.changes import GeometryChangeTracker
|
||||||
|
from c3nav.mapdata.utils.cache.local import per_request_cache
|
||||||
|
|
||||||
|
|
||||||
class MapUpdate(models.Model):
|
class MapUpdate(models.Model):
|
||||||
|
@ -48,47 +49,47 @@ class MapUpdate(models.Model):
|
||||||
@classmethod
|
@classmethod
|
||||||
def last_update(cls, force=False):
|
def last_update(cls, force=False):
|
||||||
if not force:
|
if not force:
|
||||||
last_update = cache.get('mapdata:last_update', None)
|
last_update = per_request_cache.get('mapdata:last_update', None)
|
||||||
if last_update is not None:
|
if last_update is not None:
|
||||||
return last_update
|
return last_update
|
||||||
try:
|
try:
|
||||||
with cls.lock():
|
with cls.lock():
|
||||||
last_update = cls.objects.latest().to_tuple
|
last_update = cls.objects.latest().to_tuple
|
||||||
cache.set('mapdata:last_update', last_update, None)
|
per_request_cache.set('mapdata:last_update', last_update, None)
|
||||||
except cls.DoesNotExist:
|
except cls.DoesNotExist:
|
||||||
last_update = (0, 0)
|
last_update = (0, 0)
|
||||||
cache.set('mapdata:last_update', last_update, None)
|
per_request_cache.set('mapdata:last_update', last_update, None)
|
||||||
return last_update
|
return last_update
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def last_processed_update(cls, force=False, lock=True):
|
def last_processed_update(cls, force=False, lock=True):
|
||||||
if not force:
|
if not force:
|
||||||
last_processed_update = cache.get('mapdata:last_processed_update', None)
|
last_processed_update = per_request_cache.get('mapdata:last_processed_update', None)
|
||||||
if last_processed_update is not None:
|
if last_processed_update is not None:
|
||||||
return last_processed_update
|
return last_processed_update
|
||||||
try:
|
try:
|
||||||
with (cls.lock() if lock else nullcontext()):
|
with (cls.lock() if lock else nullcontext()):
|
||||||
last_processed_update = cls.objects.filter(processed=True).latest().to_tuple
|
last_processed_update = cls.objects.filter(processed=True).latest().to_tuple
|
||||||
cache.set('mapdata:last_processed_update', last_processed_update, None)
|
per_request_cache.set('mapdata:last_processed_update', last_processed_update, None)
|
||||||
except cls.DoesNotExist:
|
except cls.DoesNotExist:
|
||||||
last_processed_update = (0, 0)
|
last_processed_update = (0, 0)
|
||||||
cache.set('mapdata:last_processed_update', last_processed_update, None)
|
per_request_cache.set('mapdata:last_processed_update', last_processed_update, None)
|
||||||
return last_processed_update
|
return last_processed_update
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def last_processed_geometry_update(cls, force=False):
|
def last_processed_geometry_update(cls, force=False):
|
||||||
if not force:
|
if not force:
|
||||||
last_processed_geometry_update = cache.get('mapdata:last_processed_geometry_update', None)
|
last_processed_geometry_update = per_request_cache.get('mapdata:last_processed_geometry_update', None)
|
||||||
if last_processed_geometry_update is not None:
|
if last_processed_geometry_update is not None:
|
||||||
return last_processed_geometry_update
|
return last_processed_geometry_update
|
||||||
try:
|
try:
|
||||||
with cls.lock():
|
with cls.lock():
|
||||||
last_processed_geometry_update = cls.objects.filter(processed=True,
|
last_processed_geometry_update = cls.objects.filter(processed=True,
|
||||||
geometries_changed=True).latest().to_tuple
|
geometries_changed=True).latest().to_tuple
|
||||||
cache.set('mapdata:last_processed_geometry_update', last_processed_geometry_update, None)
|
per_request_cache.set('mapdata:last_processed_geometry_update', last_processed_geometry_update, None)
|
||||||
except cls.DoesNotExist:
|
except cls.DoesNotExist:
|
||||||
last_processed_geometry_update = (0, 0)
|
last_processed_geometry_update = (0, 0)
|
||||||
cache.set('mapdata:last_processed_geometry_update', last_processed_geometry_update, None)
|
per_request_cache.set('mapdata:last_processed_geometry_update', last_processed_geometry_update, None)
|
||||||
return last_processed_geometry_update
|
return last_processed_geometry_update
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -239,7 +240,8 @@ class MapUpdate(models.Model):
|
||||||
LevelRenderData.rebuild(geometry_update_cache_key)
|
LevelRenderData.rebuild(geometry_update_cache_key)
|
||||||
|
|
||||||
transaction.on_commit(
|
transaction.on_commit(
|
||||||
lambda: cache.set('mapdata:last_processed_geometry_update', last_geometry_update.to_tuple, None)
|
lambda: per_request_cache.set('mapdata:last_processed_geometry_update',
|
||||||
|
last_geometry_update.to_tuple, None)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.info('No geometries affected.')
|
logger.info('No geometries affected.')
|
||||||
|
@ -282,7 +284,7 @@ class MapUpdate(models.Model):
|
||||||
|
|
||||||
if new:
|
if new:
|
||||||
transaction.on_commit(
|
transaction.on_commit(
|
||||||
lambda: cache.set('mapdata:last_update', self.to_tuple, None)
|
lambda: per_request_cache.set('mapdata:last_update', self.to_tuple, None)
|
||||||
)
|
)
|
||||||
if settings.HAS_CELERY and settings.AUTO_PROCESS_UPDATES:
|
if settings.HAS_CELERY and settings.AUTO_PROCESS_UPDATES:
|
||||||
transaction.on_commit(
|
transaction.on_commit(
|
||||||
|
|
21
src/c3nav/mapdata/utils/cache/local.py
vendored
21
src/c3nav/mapdata/utils/cache/local.py
vendored
|
@ -1,8 +1,7 @@
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
from django.conf import settings
|
||||||
from c3nav.mapdata.models import MapUpdate
|
|
||||||
|
|
||||||
|
|
||||||
class NoneFromCache:
|
class NoneFromCache:
|
||||||
|
@ -42,6 +41,9 @@ class LocalCacheProxy:
|
||||||
self._items.pop(next(iter(self._items.keys())))
|
self._items.pop(next(iter(self._items.keys())))
|
||||||
|
|
||||||
def _check_mapupdate(self):
|
def _check_mapupdate(self):
|
||||||
|
# todo: would be nice to not need this… why do we need this?
|
||||||
|
|
||||||
|
from c3nav.mapdata.models import MapUpdate
|
||||||
mapupdate = MapUpdate.current_cache_key()
|
mapupdate = MapUpdate.current_cache_key()
|
||||||
if self._mapupdate != mapupdate:
|
if self._mapupdate != mapupdate:
|
||||||
self._items = OrderedDict()
|
self._items = OrderedDict()
|
||||||
|
@ -52,3 +54,18 @@ class LocalCacheProxy:
|
||||||
cache.set(key, value, expire)
|
cache.set(key, value, expire)
|
||||||
self._items[key] = value
|
self._items[key] = value
|
||||||
self._prune()
|
self._prune()
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self._items.clear()
|
||||||
|
|
||||||
|
|
||||||
|
class RequestLocalCacheProxy(LocalCacheProxy):
|
||||||
|
""" this is a subclass without prune, to be cleared after every request """
|
||||||
|
def _prune(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_mapupdate(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
per_request_cache = RequestLocalCacheProxy(maxsize=settings.CACHE_SIZE_LOCATIONS)
|
|
@ -390,6 +390,7 @@ MIDDLEWARE = [
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
'c3nav.mapdata.middleware.RequestCacheMiddleware',
|
||||||
'c3nav.mapdata.middleware.UserDataMiddleware',
|
'c3nav.mapdata.middleware.UserDataMiddleware',
|
||||||
'c3nav.site.middleware.MobileclientMiddleware',
|
'c3nav.site.middleware.MobileclientMiddleware',
|
||||||
'c3nav.control.middleware.UserPermissionsMiddleware',
|
'c3nav.control.middleware.UserPermissionsMiddleware',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue