From 2831a24b90f00779c2313d299928401b67dde87b Mon Sep 17 00:00:00 2001 From: cuom1999 Date: Fri, 23 Feb 2024 17:07:34 -0600 Subject: [PATCH] Cache prefetch --- chat_box/models.py | 26 ++++++++++-------- chat_box/views.py | 7 +++-- judge/caching.py | 10 +++++++ judge/models/profile.py | 55 ++++++++++++++++++++++++++------------- judge/views/submission.py | 1 + judge/views/user.py | 5 ++-- 6 files changed, 70 insertions(+), 34 deletions(-) diff --git a/chat_box/models.py b/chat_box/models.py index 00a76a2..476e861 100644 --- a/chat_box/models.py +++ b/chat_box/models.py @@ -25,26 +25,18 @@ class Room(models.Model): class Meta: app_label = "chat_box" - @cache_wrapper(prefix="Rinfo") - def _info(self): - last_msg = self.message_set.filter(hidden=False).first() - return { - "user_ids": [self.user_one.id, self.user_two.id], - "last_message": last_msg.body if last_msg else None, - } - @cached_property def _cached_info(self): - return self._info() + return get_room_info(self.id) def contain(self, profile): - return profile.id in self._cached_info["user_ids"] + return profile.id in [self.user_one_id, self.user_two_id] def other_user(self, profile): return self.user_one if profile == self.user_two else self.user_two def other_user_id(self, profile): - user_ids = self._cached_info["user_ids"] + user_ids = [self.user_one_id, self.user_two_id] return sum(user_ids) - profile.id def users(self): @@ -53,6 +45,10 @@ class Room(models.Model): def last_message_body(self): return self._cached_info["last_message"] + @classmethod + def prefetch_room_cache(self, room_ids): + get_room_info.prefetch_multi([(i,) for i in room_ids]) + class Message(models.Model): author = models.ForeignKey(Profile, verbose_name=_("user"), on_delete=CASCADE) @@ -147,3 +143,11 @@ class Ignore(models.Model): self.remove_ignore(current_user, friend) else: self.add_ignore(current_user, friend) + + +@cache_wrapper(prefix="Rinfo") +def get_room_info(room_id): + last_msg = Message.objects.filter(room_id=room_id).first() + return { + "last_message": last_msg.body if last_msg else None, + } diff --git a/chat_box/views.py b/chat_box/views.py index 34dcfe5..5bcabd1 100644 --- a/chat_box/views.py +++ b/chat_box/views.py @@ -34,7 +34,7 @@ from judge import event_poster as event from judge.jinja2.gravatar import gravatar from judge.models import Friend -from chat_box.models import Message, Profile, Room, UserRoom, Ignore +from chat_box.models import Message, Profile, Room, UserRoom, Ignore, get_room_info from chat_box.utils import encrypt_url, decrypt_url, encrypt_channel, get_unread_boxes @@ -231,7 +231,7 @@ def post_message(request): }, ) else: - Room._info.dirty(room) + get_room_info.dirty(room.id) room.last_msg_time = new_message.time room.save() @@ -363,6 +363,8 @@ def user_online_status_ajax(request): def get_online_status(profile, other_profile_ids, rooms=None): if not other_profile_ids: return None + Profile.prefetch_profile_cache(other_profile_ids) + joined_ids = ",".join([str(id) for id in other_profile_ids]) other_profiles = Profile.objects.raw( f"SELECT * from judge_profile where id in ({joined_ids}) order by field(id,{joined_ids})" @@ -429,6 +431,7 @@ def get_status_context(profile, include_ignored=False): recent_profile_ids = [str(i["other_user"]) for i in recent_profile] recent_rooms = [int(i["id"]) for i in recent_profile] + Room.prefetch_room_cache(recent_rooms) admin_list = ( queryset.filter(display_rank="admin") diff --git a/judge/caching.py b/judge/caching.py index 029cf08..8fd40df 100644 --- a/judge/caching.py +++ b/judge/caching.py @@ -74,7 +74,17 @@ def cache_wrapper(prefix, timeout=None): if l0_cache: l0_cache.delete(cache_key) + def prefetch_multi(args_list): + keys = [] + for args in args_list: + keys.append(get_key(func, *args)) + results = cache.get_many(keys) + for key, result in results.items(): + if result is not None: + _set_l0(key, result) + wrapper.dirty = dirty + wrapper.prefetch_multi = prefetch_multi return wrapper diff --git a/judge/models/profile.py b/judge/models/profile.py index 61c5d5f..bee95f6 100644 --- a/judge/models/profile.py +++ b/judge/models/profile.py @@ -253,24 +253,9 @@ class Profile(models.Model): max_length=300, ) - @cache_wrapper(prefix="Pgbi2") - def _get_basic_info(self): - res = { - "email": self.user.email, - "username": self.user.username, - "mute": self.mute, - } - if self.user.first_name: - res["first_name"] = self.user.first_name - if self.user.last_name: - res["last_name"] = self.user.last_name - if self.profile_image: - res["profile_image_url"] = self.profile_image.url - return res - @cached_property def _cached_info(self): - return self._get_basic_info() + return _get_basic_info(self.id) @cached_property def organization(self): @@ -412,6 +397,10 @@ class Profile(models.Model): or self.user.is_superuser ) + @classmethod + def prefetch_profile_cache(self, profile_ids): + _get_basic_info.prefetch_multi([(pid,) for pid in profile_ids]) + class Meta: indexes = [ models.Index(fields=["is_unlisted", "performance_points"]), @@ -540,7 +529,7 @@ class OrganizationProfile(models.Model): def on_user_save(sender, instance, **kwargs): try: profile = instance.profile - profile._get_basic_info.dirty(profile) + _get_basic_info.dirty(profile.id) except: pass @@ -551,4 +540,34 @@ def on_profile_save(sender, instance, **kwargs): return prev = sender.objects.get(id=instance.id) if prev.mute != instance.mute or prev.profile_image != instance.profile_image: - instance._get_basic_info.dirty(instance) + _get_basic_info.dirty(instance.id) + + +@cache_wrapper(prefix="Pgbi2") +def _get_basic_info(profile_id): + profile = ( + Profile.objects.select_related("user") + .only( + "id", + "mute", + "profile_image", + "user__username", + "user__email", + "user__first_name", + "user__last_name", + ) + .get(id=profile_id) + ) + user = profile.user + res = { + "email": user.email, + "username": user.username, + "mute": profile.mute, + "first_name": user.first_name or None, + "last_name": user.last_name or None, + "profile_image_url": profile.profile_image.url + if profile.profile_image + else None, + } + res = {k: v for k, v in res.items() if v is not None} + return res diff --git a/judge/views/submission.py b/judge/views/submission.py index 8baadac..a05f24f 100644 --- a/judge/views/submission.py +++ b/judge/views/submission.py @@ -470,6 +470,7 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView): if context["in_hidden_subtasks_contest"]: for submission in context["submissions"]: self.modify_attrs(submission) + return context def get(self, request, *args, **kwargs): diff --git a/judge/views/user.py b/judge/views/user.py index 356a8a7..bfbba08 100644 --- a/judge/views/user.py +++ b/judge/views/user.py @@ -457,14 +457,13 @@ class UserList(QueryStringSortMixin, InfinitePaginationMixin, TitleMixin, ListVi queryset = ( Profile.objects.filter(is_unlisted=False) .order_by(self.order, "id") - .select_related("user") .only( "display_rank", - "user__username", "points", "rating", "performance_points", "problem_count", + "about", ) ) if self.request.organization: @@ -472,11 +471,11 @@ class UserList(QueryStringSortMixin, InfinitePaginationMixin, TitleMixin, ListVi if (self.request.GET.get("friend") == "true") and self.request.profile: queryset = self.filter_friend_queryset(queryset) self.filter_friend = True - return queryset def get_context_data(self, **kwargs): context = super(UserList, self).get_context_data(**kwargs) + Profile.prefetch_profile_cache([u.id for u in context["users"]]) context["users"] = ranker( context["users"], rank=self.paginate_by * (context["page_obj"].number - 1) )