diff --git a/judge/caching.py b/judge/caching.py index 66ea686..d86cc30 100644 --- a/judge/caching.py +++ b/judge/caching.py @@ -5,6 +5,8 @@ from django.core.handlers.wsgi import WSGIRequest import hashlib +from judge.logging import log_debug + MAX_NUM_CHAR = 50 NONE_RESULT = "__None__" @@ -26,7 +28,7 @@ def filter_args(args_list): l0_cache = caches["l0"] if "l0" in caches else None -def cache_wrapper(prefix, timeout=None): +def cache_wrapper(prefix, timeout=None, expected_type=None): def get_key(func, *args, **kwargs): args_list = list(args) signature_args = list(signature(func).parameters.keys()) @@ -54,10 +56,23 @@ def cache_wrapper(prefix, timeout=None): cache.set(key, value, timeout) def decorator(func): + def _validate_type(cache_key, result): + if expected_type and not isinstance(result, expected_type): + data = { + "function": f"{func.__module__}.{func.__qualname__}", + "result": str(result)[:30], + "expected_type": expected_type, + "type": type(result), + "key": cache_key, + } + log_debug("invalid_key", data) + return False + return True + def wrapper(*args, **kwargs): cache_key = get_key(func, *args, **kwargs) result = _get(cache_key) - if result is not None: + if result is not None and _validate_type(cache_key, result): _set_l0(cache_key, result) if type(result) == str and result == NONE_RESULT: result = None diff --git a/judge/logging.py b/judge/logging.py index 8d9f154..a1a25be 100644 --- a/judge/logging.py +++ b/judge/logging.py @@ -1,7 +1,12 @@ import logging error_log = logging.getLogger("judge.errors") +debug_log = logging.getLogger("judge.debug") def log_exception(msg): error_log.exception(msg) + + +def log_debug(category, data): + debug_log.info(f"{category}: {data}") diff --git a/judge/models/bookmark.py b/judge/models/bookmark.py index 6a25411..06be327 100644 --- a/judge/models/bookmark.py +++ b/judge/models/bookmark.py @@ -23,7 +23,7 @@ class BookMark(models.Model): linked_object = GenericForeignKey("content_type", "object_id") @cache_wrapper(prefix="BMgb") - def get_bookmark(self, user): + def is_bookmarked_by(self, user): return MakeBookMark.objects.filter(bookmark=self, user=user).exists() class Meta: @@ -53,7 +53,7 @@ class MakeBookMark(models.Model): verbose_name_plural = _("make bookmarks") -@cache_wrapper(prefix="gocb") +@cache_wrapper(prefix="gocb", expected_type=BookMark) def _get_or_create_bookmark(content_type, object_id): bookmark, created = BookMark.objects.get_or_create( content_type=content_type, @@ -70,5 +70,5 @@ class Bookmarkable: def dirty_bookmark(bookmark, profile): - bookmark.get_bookmark.dirty(bookmark, profile) + bookmark.is_bookmarked_by.dirty(bookmark, profile) _get_or_create_bookmark.dirty(bookmark.content_type, bookmark.object_id) diff --git a/judge/models/interface.py b/judge/models/interface.py index 8ff3fff..d9c6e2c 100644 --- a/judge/models/interface.py +++ b/judge/models/interface.py @@ -133,7 +133,7 @@ class BlogPost(models.Model, PageVotable, Bookmarkable): and self.authors.filter(id=user.profile.id).exists() ) - @cache_wrapper(prefix="BPga") + @cache_wrapper(prefix="BPga", expected_type=models.query.QuerySet) def get_authors(self): return self.authors.only("id") diff --git a/judge/models/pagevote.py b/judge/models/pagevote.py index 9186197..bc0224e 100644 --- a/judge/models/pagevote.py +++ b/judge/models/pagevote.py @@ -49,7 +49,7 @@ class PageVoteVoter(models.Model): verbose_name_plural = _("pagevote votes") -@cache_wrapper(prefix="gocp") +@cache_wrapper(prefix="gocp", expected_type=PageVote) def _get_or_create_pagevote(content_type, object_id): pagevote, created = PageVote.objects.get_or_create( content_type=content_type, diff --git a/judge/models/problem.py b/judge/models/problem.py index fd5f3e8..c19eaf7 100644 --- a/judge/models/problem.py +++ b/judge/models/problem.py @@ -440,7 +440,7 @@ class Problem(models.Model, PageVotable, Bookmarkable): "profile_id", flat=True ) - @cache_wrapper(prefix="Pga") + @cache_wrapper(prefix="Pga", expected_type=models.query.QuerySet) def get_authors(self): return self.authors.only("id") diff --git a/judge/models/profile.py b/judge/models/profile.py index faac4bb..14fc5f7 100644 --- a/judge/models/profile.py +++ b/judge/models/profile.py @@ -581,7 +581,7 @@ def on_user_save(sender, instance, **kwargs): pass -@cache_wrapper(prefix="Pgbi3") +@cache_wrapper(prefix="Pgbi3", expected_type=dict) def _get_basic_info(profile_id): profile = ( Profile.objects.select_related("user") diff --git a/judge/template_context.py b/judge/template_context.py index 59b29bf..337d32a 100644 --- a/judge/template_context.py +++ b/judge/template_context.py @@ -7,6 +7,8 @@ from django.contrib.sites.shortcuts import get_current_site from django.core.cache import cache from django.utils.functional import SimpleLazyObject, new_method_proxy +from mptt.querysets import TreeQuerySet + from .models import MiscConfig, NavigationBar, Profile from judge.caching import cache_wrapper @@ -52,7 +54,7 @@ def comet_location(request): return {"EVENT_DAEMON_LOCATION": websocket, "EVENT_DAEMON_POLL_LOCATION": poll} -@cache_wrapper(prefix="nb") +@cache_wrapper(prefix="nb", expected_type=TreeQuerySet) def _nav_bar(): return NavigationBar.objects.all() diff --git a/judge/utils/problems.py b/judge/utils/problems.py index 0b69267..67c4228 100644 --- a/judge/utils/problems.py +++ b/judge/utils/problems.py @@ -167,7 +167,7 @@ def editable_problems(user, profile=None): return subquery -@cache_wrapper(prefix="hp", timeout=900) +@cache_wrapper(prefix="hp", timeout=14400) def hot_problems(duration, limit): qs = Problem.get_public_problems().filter( submission__date__gt=timezone.now() - duration @@ -224,7 +224,7 @@ def hot_problems(duration, limit): return qs -@cache_wrapper(prefix="grp", timeout=26400) +@cache_wrapper(prefix="grp", timeout=14400) def get_related_problems(profile, problem, limit=8): if not profile or not settings.ML_OUTPUT_PATH: return None diff --git a/templates/actionbar/list.html b/templates/actionbar/list.html index 4a38aaf..7c313b7 100644 --- a/templates/actionbar/list.html +++ b/templates/actionbar/list.html @@ -37,7 +37,7 @@ {% endif %} {{_("Bookmark")}}