introducing LocalCacheProxy to skip pickling stuff

This commit is contained in:
Laura Klünder 2018-12-27 17:24:53 +01:00
parent 957ce0c0cd
commit 3eeda53ca8
3 changed files with 69 additions and 19 deletions

View file

@ -27,6 +27,7 @@ from c3nav.mapdata.models.geometry.space import (POI, Area, Column, CrossDescrip
from c3nav.mapdata.models.level import Level from c3nav.mapdata.models.level import Level
from c3nav.mapdata.models.locations import (Location, LocationGroupCategory, LocationRedirect, LocationSlug, from c3nav.mapdata.models.locations import (Location, LocationGroupCategory, LocationRedirect, LocationSlug,
SpecificLocation) SpecificLocation)
from c3nav.mapdata.utils.cache.local import LocalCacheProxy
from c3nav.mapdata.utils.cache.stats import increment_cache_key from c3nav.mapdata.utils.cache.stats import increment_cache_key
from c3nav.mapdata.utils.locations import (get_location_by_id_for_request, get_location_by_slug_for_request, from c3nav.mapdata.utils.locations import (get_location_by_id_for_request, get_location_by_slug_for_request,
searchable_locations_for_request, visible_locations_for_request) searchable_locations_for_request, visible_locations_for_request)
@ -34,6 +35,8 @@ from c3nav.mapdata.utils.models import get_submodels
from c3nav.mapdata.utils.user import can_access_editor, get_user_data from c3nav.mapdata.utils.user import can_access_editor, get_user_data
from c3nav.mapdata.views import set_tile_access_cookie from c3nav.mapdata.views import set_tile_access_cookie
request_cache = LocalCacheProxy(maxsize=64)
def optimize_query(qs): def optimize_query(qs):
if issubclass(qs.model, SpecificLocation): if issubclass(qs.model, SpecificLocation):
@ -85,15 +88,18 @@ def api_etag(permissions=True, etag_func=AccessPermission.etag_func, cache_param
for param, type_ in cache_parameters.items(): for param, type_ in cache_parameters.items():
value = int(param in request.GET) if type_ == bool else type_(request.GET.get(param)) value = int(param in request.GET) if type_ == bool else type_(request.GET.get(param))
cache_key += ':'+urlsafe_base64_encode(str(value).encode()).decode() cache_key += ':'+urlsafe_base64_encode(str(value).encode()).decode()
data = cache.get(cache_key) print(cache_key)
if data is not None: data = request_cache.get(cache_key)
response = Response(data) print(data)
if data is not None:
print('HA CACHE')
response = Response(data)
if response is None: if response is None:
with GeometryMixin.dont_keep_originals(): with GeometryMixin.dont_keep_originals():
response = func(self, request, *args, **kwargs) response = func(self, request, *args, **kwargs)
if cache_parameters is not None and response.status_code == 200: if cache_parameters is not None and response.status_code == 200:
cache.set(cache_key, response.data, 900) request_cache.set(cache_key, response.data, 900)
if response.status_code == 200: if response.status_code == 200:
response['ETag'] = etag response['ETag'] = etag

42
src/c3nav/mapdata/utils/cache/local.py vendored Normal file
View file

@ -0,0 +1,42 @@
from collections import OrderedDict
from django.core.cache import cache
class NoneFromCache:
pass
class LocalCacheProxy:
# django cache, buffered using a LRU cache
# only usable for stuff that never changes, obviously
def __init__(self, maxsize=128):
self._maxsize = maxsize
self._items = OrderedDict()
def get(self, key, default=None):
print('get')
try:
# first check out cache
result = self._items[key]
except KeyError:
# not in our cache
result = cache.get(key, default=NoneFromCache)
if result is not NoneFromCache:
self._items[key] = result
self._prune()
else:
result = default
else:
self._items.move_to_end(key, last=True)
return result
def _prune(self):
# remove old items
while len(self._items) > self._maxsize:
self._items.pop(next(iter(self._items.keys())))
def set(self, key, value, expire):
cache.set(key, value, expire)
self._items[key] = value
self._prune()

View file

@ -7,7 +7,6 @@ from itertools import chain
from typing import List, Mapping, Optional from typing import List, Mapping, Optional
from django.apps import apps from django.apps import apps
from django.core.cache import cache
from django.db.models import Prefetch, Q from django.db.models import Prefetch, Q
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -20,12 +19,15 @@ from c3nav.mapdata.models.geometry.base import GeometryMixin
from c3nav.mapdata.models.geometry.level import LevelGeometryMixin, Space from c3nav.mapdata.models.geometry.level import LevelGeometryMixin, Space
from c3nav.mapdata.models.geometry.space import SpaceGeometryMixin from c3nav.mapdata.models.geometry.space import SpaceGeometryMixin
from c3nav.mapdata.models.locations import LocationRedirect, LocationSlug, SpecificLocation from c3nav.mapdata.models.locations import LocationRedirect, LocationSlug, SpecificLocation
from c3nav.mapdata.utils.cache.local import LocalCacheProxy
from c3nav.mapdata.utils.models import get_submodels from c3nav.mapdata.utils.models import get_submodels
proxied_cache = LocalCacheProxy(maxsize=128)
def locations_for_request(request) -> Mapping[int, LocationSlug]: def locations_for_request(request) -> Mapping[int, LocationSlug]:
cache_key = 'mapdata:locations:%s' % AccessPermission.cache_key_for_request(request) cache_key = 'mapdata:locations:%s' % AccessPermission.cache_key_for_request(request)
locations = cache.get(cache_key, None) locations = proxied_cache.get(cache_key, None)
if locations is not None: if locations is not None:
return locations return locations
@ -110,7 +112,7 @@ def locations_for_request(request) -> Mapping[int, LocationSlug]:
# noinspection PyStatementEffect # noinspection PyStatementEffect
obj.point obj.point
cache.set(cache_key, locations, 1800) proxied_cache.set(cache_key, locations, 1800)
return locations return locations
@ -118,7 +120,7 @@ def locations_for_request(request) -> Mapping[int, LocationSlug]:
def get_better_space_geometries(): def get_better_space_geometries():
# change space geometries for better representative points # change space geometries for better representative points
cache_key = 'mapdata:better_space_geometries:%s' % MapUpdate.current_cache_key() cache_key = 'mapdata:better_space_geometries:%s' % MapUpdate.current_cache_key()
result = cache.get(cache_key, None) result = proxied_cache.get(cache_key, None)
if result is not None: if result is not None:
return result return result
@ -130,28 +132,28 @@ def get_better_space_geometries():
if not geometry.is_empty: if not geometry.is_empty:
result[space.pk] = geometry result[space.pk] = geometry
cache.set(cache_key, result, 1800) proxied_cache.set(cache_key, result, 1800)
return result return result
def visible_locations_for_request(request) -> Mapping[int, Location]: def visible_locations_for_request(request) -> Mapping[int, Location]:
cache_key = 'mapdata:locations:real:%s' % AccessPermission.cache_key_for_request(request) cache_key = 'mapdata:locations:real:%s' % AccessPermission.cache_key_for_request(request)
locations = cache.get(cache_key, None) locations = proxied_cache.get(cache_key, None)
if locations is not None: if locations is not None:
return locations return locations
locations = {pk: location for pk, location in locations_for_request(request).items() locations = {pk: location for pk, location in locations_for_request(request).items()
if not isinstance(location, LocationRedirect) and (location.can_search or location.can_describe)} if not isinstance(location, LocationRedirect) and (location.can_search or location.can_describe)}
cache.set(cache_key, locations, 1800) proxied_cache.set(cache_key, locations, 1800)
return locations return locations
def searchable_locations_for_request(request) -> List[Location]: def searchable_locations_for_request(request) -> List[Location]:
cache_key = 'mapdata:locations:searchable:%s' % AccessPermission.cache_key_for_request(request) cache_key = 'mapdata:locations:searchable:%s' % AccessPermission.cache_key_for_request(request)
locations = cache.get(cache_key, None) locations = proxied_cache.get(cache_key, None)
if locations is not None: if locations is not None:
return locations return locations
@ -160,27 +162,27 @@ def searchable_locations_for_request(request) -> List[Location]:
locations = sorted(locations, key=operator.attrgetter('order'), reverse=True) locations = sorted(locations, key=operator.attrgetter('order'), reverse=True)
cache.set(cache_key, locations, 1800) proxied_cache.set(cache_key, locations, 1800)
return locations return locations
def locations_by_slug_for_request(request) -> Mapping[str, LocationSlug]: def locations_by_slug_for_request(request) -> Mapping[str, LocationSlug]:
cache_key = 'mapdata:locations:by_slug:%s' % AccessPermission.cache_key_for_request(request) cache_key = 'mapdata:locations:by_slug:%s' % AccessPermission.cache_key_for_request(request)
locations = cache.get(cache_key, None) locations = proxied_cache.get(cache_key, None)
if locations is not None: if locations is not None:
return locations return locations
locations = {location.slug: location for location in locations_for_request(request).values() if location.slug} locations = {location.slug: location for location in locations_for_request(request).values() if location.slug}
cache.set(cache_key, locations, 1800) proxied_cache.set(cache_key, locations, 1800)
return locations return locations
def levels_by_short_label_for_request(request) -> Mapping[str, Level]: def levels_by_short_label_for_request(request) -> Mapping[str, Level]:
cache_key = 'mapdata:levels:by_short_label:%s' % AccessPermission.cache_key_for_request(request) cache_key = 'mapdata:levels:by_short_label:%s' % AccessPermission.cache_key_for_request(request)
levels = cache.get(cache_key, None) levels = proxied_cache.get(cache_key, None)
if levels is not None: if levels is not None:
return levels return levels
@ -189,7 +191,7 @@ def levels_by_short_label_for_request(request) -> Mapping[str, Level]:
for level in Level.qs_for_request(request).filter(on_top_of_id__isnull=True).order_by('base_altitude') for level in Level.qs_for_request(request).filter(on_top_of_id__isnull=True).order_by('base_altitude')
) )
cache.set(cache_key, levels, 1800) proxied_cache.set(cache_key, levels, 1800)
return levels return levels
@ -205,7 +207,7 @@ def get_location_by_id_for_request(pk, request):
def get_location_by_slug_for_request(slug: str, request) -> Optional[LocationSlug]: def get_location_by_slug_for_request(slug: str, request) -> Optional[LocationSlug]:
cache_key = 'mapdata:location:by_slug:%s:%s' % (AccessPermission.cache_key_for_request(request), slug) cache_key = 'mapdata:location:by_slug:%s:%s' % (AccessPermission.cache_key_for_request(request), slug)
location = cache.get(cache_key, None) location = proxied_cache.get(cache_key, None)
if location is not None: if location is not None:
return location return location
@ -230,7 +232,7 @@ def get_location_by_slug_for_request(slug: str, request) -> Optional[LocationSlu
else: else:
location = locations_by_slug_for_request(request).get(slug, None) location = locations_by_slug_for_request(request).get(slug, None)
cache.set(cache_key, location, 1800) proxied_cache.set(cache_key, location, 1800)
return location return location