Use cache for get_all_entries (#21)

* Use cache for get_all_entries

* Use prefetch_related in get_all_entries

* Run formatter

* More type annotations
This commit is contained in:
Luke Street 2022-11-21 12:22:28 -05:00 committed by GitHub
parent b3a907e91c
commit 8eaf1784f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 5 deletions

45
frog_api/cache.py Normal file
View File

@ -0,0 +1,45 @@
from typing import Optional
from django.core.cache import cache
from rest_framework.utils.serializer_helpers import ReturnDict
ENTRIES_CACHE_TIMEOUT = 7200 # 2 hours
def _entries_cache_key(project_slug: str, version_slug: str, category_slug: str) -> str:
return f"entries_{project_slug}_{version_slug}_{category_slug}"
def get_entries_cache(
project_slug: str, version_slug: str, category_slug: str
) -> Optional[ReturnDict]:
"""
Fetches cached entries data.
"""
return cache.get(_entries_cache_key(project_slug, version_slug, category_slug))
def set_entries_cache(
project_slug: str, version_slug: str, category_slug: str, data: ReturnDict
) -> None:
"""
Updates cached entries data.
"""
cache.set(
_entries_cache_key(project_slug, version_slug, category_slug),
data,
ENTRIES_CACHE_TIMEOUT,
)
def invalidate_entries_cache(
project_slug: str, version_slug: str, data: ReturnDict
) -> None:
"""
Invalidates all affected entries caches.
"""
all_categories = set()
for entry in data["entries"]:
for category in entry["categories"]:
all_categories.add(category)
for category_slug in all_categories:
cache.delete(_entries_cache_key(project_slug, version_slug, category_slug))

View File

@ -3,6 +3,11 @@ from typing import Any
from django.db import models
from django.template.defaultfilters import title
from frog_api.cache import (
invalidate_entries_cache,
set_entries_cache,
get_entries_cache,
)
from frog_api.exceptions import (
InvalidDataException,
NoEntriesException,
@ -43,13 +48,19 @@ def get_latest_entry(
def get_all_entries(
project_slug: str, version_slug: str, category_slug: str
) -> list[dict[str, Any]]:
data = get_entries_cache(project_slug, version_slug, category_slug)
if data:
return data # type: ignore
project = get_project(project_slug)
version = get_version(version_slug, project)
category = get_category(category_slug, version)
entries = Entry.objects.filter(category=category)
entries = Entry.objects.filter(category=category).prefetch_related("measures")
return EntrySerializer(entries, many=True).data # type: ignore
data = EntrySerializer(entries, many=True).data
set_entries_cache(project_slug, version_slug, category_slug, data)
return data # type: ignore
def get_versions_digest_for_project(project: Project) -> dict[Any, Any]:
@ -191,6 +202,8 @@ class VersionDataView(APIView):
for s in to_save:
s.save()
invalidate_entries_cache(project_slug, version_slug, data)
return len(to_save)
def get(self, request: Request, project_slug: str, version_slug: str) -> Response:

View File

@ -42,6 +42,7 @@ class ProjectStructureView(APIView):
Create a new project.
"""
request_ser = CreateProjectSerializer(data=request.data)
request_ser.is_valid(raise_exception=True)
validate_ultimate_api_key(request_ser.data["api_key"])
@ -58,12 +59,10 @@ class VersionStructureView(APIView):
def post(self, request: Request, project_slug: str, version_slug: str) -> Response:
request_ser = CreateVersionSerializer(data=request.data)
request_ser.is_valid(raise_exception=True)
project = get_project(project_slug)
if not request_ser.is_valid():
return Response(request_ser.errors, status=status.HTTP_400_BAD_REQUEST)
validate_api_key(request_ser.data["api_key"], project)
if Version.objects.filter(slug=version_slug, project=project).exists():