caching for overlay api?

This commit is contained in:
Laura Klünder 2024-12-26 23:06:46 +01:00
parent 148517cc5f
commit 616d74fba9
2 changed files with 34 additions and 5 deletions

View file

@ -1,7 +1,10 @@
import json
import time
from functools import wraps
from typing import Optional
from django.conf import settings
from django.core.cache import cache
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Prefetch
from django.utils.cache import get_conditional_response
@ -19,7 +22,8 @@ from c3nav.mapdata.utils.cache.stats import increment_cache_key
request_cache = LocalCacheProxy(maxsize=settings.CACHE_SIZE_API)
def api_etag(permissions=True, quests=False, etag_func=AccessPermission.etag_func, base_mapdata=False):
def api_etag(permissions=True, quests=False, etag_func=AccessPermission.etag_func, base_mapdata=False,
etag_add_key: Optional[tuple[str, str]] = None):
def outer_wrapper(func):
@wraps(func)
@ -63,9 +67,20 @@ def api_etag(permissions=True, quests=False, etag_func=AccessPermission.etag_fun
else:
value = model_dump()
data[name] = value
cache_key = 'mapdata:api:%s:%s:%s' % (
etag_add = ''
if etag_add_key:
etag_add_cache_key = (
f'mapdata:etag_add:{etag_add_key[1]}:{getattr(kwargs[etag_add_key[0]], etag_add_key[1])}'
)
etag_add = cache.get(etag_add_cache_key, None)
if etag_add is None:
etag_add = int(time.time())
cache.set(etag_add_cache_key, etag_add, 300)
cache_key = 'mapdata:api:%s:%s:%s:%s' % (
request.resolver_match.route.replace('/', '-').strip('-'),
raw_etag,
etag_add,
json.dumps(data, separators=(',', ':'), sort_keys=True, cls=DjangoJSONEncoder),
)

View file

@ -1,6 +1,7 @@
from dataclasses import dataclass
from typing import Optional, Sequence, Type, Callable, Any
from django.core.cache import cache
from django.db import transaction
from django.db.models import Model, F
from django.shortcuts import get_object_or_404
@ -79,6 +80,7 @@ class MapdataEndpoint:
schema: Type[BaseSchema]
filters: Type[FilterSchema] | None = None
no_cache: bool = False
etag_add_key: Optional[str] = None
name: Optional[str] = None
@property
@ -143,7 +145,9 @@ class MapdataAPIBuilder:
list_func.__name__ = f"{endpoint.model_name}_list"
if not endpoint.no_cache:
list_func = api_etag()(list_func)
list_func = api_etag(
etag_add_key=(("filters", endpoint.etag_add_key) if endpoint.etag_add_key else None)
)(list_func)
self.router.get(f"/{endpoint.endpoint_name}/", summary=f"{endpoint.model_name} list",
tags=[f"mapdata-{tag}"], description=schema_description(endpoint.schema),
@ -233,13 +237,13 @@ mapdata_endpoints: dict[str, list[MapdataEndpoint]] = {
model=DataOverlayFeature,
schema=DataOverlayFeatureSchema,
filters=ByOverlayFilter,
no_cache=True,
etag_add_key="overlay",
),
MapdataEndpoint(
model=DataOverlayFeature,
schema=DataOverlayFeatureGeometrySchema,
filters=ByOverlayFilter,
no_cache=True,
etag_add_key="overlay",
name='dataoverlayfeaturegeometries'
),
MapdataEndpoint(
@ -341,6 +345,8 @@ def update_data_overlay_feature(request, id: int, parameters: DataOverlayFeature
feature.save()
cache.delete(f'mapdata:etag_add:overlay:{feature.overlay_id}')
return 204, None
@ -356,6 +362,7 @@ def update_data_overlay_features_bulk(request, parameters: DataOverlayFeatureBul
forbidden_object_ids = []
overlay_ids = set()
with transaction.atomic():
features = DataOverlayFeature.objects.filter(id__in=updates.keys()).annotate(
edit_access_restriction_id=F('overlay__edit_access_restriction_id'))
@ -371,8 +378,15 @@ def update_data_overlay_features_bulk(request, parameters: DataOverlayFeatureBul
if key == 'id':
continue
setattr(feature, key, value)
overlay_ids.add(feature.overlay_id)
feature.save()
for pk in overlay_ids:
transaction.on_commit(
lambda: cache.delete(f'mapdata:etag_add:overlay:{pk}')
)
if len(forbidden_object_ids) > 0:
raise APIPermissionDenied('You are not allowed to edit the objects with the following ids: %s.'
% ", ".join([str(x) for x in forbidden_object_ids]))