Cache prefetch

This commit is contained in:
cuom1999 2024-02-23 17:07:34 -06:00
parent c2f6dba462
commit 2831a24b90
6 changed files with 70 additions and 34 deletions

View file

@ -25,26 +25,18 @@ class Room(models.Model):
class Meta: class Meta:
app_label = "chat_box" 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 @cached_property
def _cached_info(self): def _cached_info(self):
return self._info() return get_room_info(self.id)
def contain(self, profile): 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): def other_user(self, profile):
return self.user_one if profile == self.user_two else self.user_two return self.user_one if profile == self.user_two else self.user_two
def other_user_id(self, profile): 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 return sum(user_ids) - profile.id
def users(self): def users(self):
@ -53,6 +45,10 @@ class Room(models.Model):
def last_message_body(self): def last_message_body(self):
return self._cached_info["last_message"] 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): class Message(models.Model):
author = models.ForeignKey(Profile, verbose_name=_("user"), on_delete=CASCADE) author = models.ForeignKey(Profile, verbose_name=_("user"), on_delete=CASCADE)
@ -147,3 +143,11 @@ class Ignore(models.Model):
self.remove_ignore(current_user, friend) self.remove_ignore(current_user, friend)
else: else:
self.add_ignore(current_user, friend) 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,
}

View file

@ -34,7 +34,7 @@ from judge import event_poster as event
from judge.jinja2.gravatar import gravatar from judge.jinja2.gravatar import gravatar
from judge.models import Friend 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 from chat_box.utils import encrypt_url, decrypt_url, encrypt_channel, get_unread_boxes
@ -231,7 +231,7 @@ def post_message(request):
}, },
) )
else: else:
Room._info.dirty(room) get_room_info.dirty(room.id)
room.last_msg_time = new_message.time room.last_msg_time = new_message.time
room.save() room.save()
@ -363,6 +363,8 @@ def user_online_status_ajax(request):
def get_online_status(profile, other_profile_ids, rooms=None): def get_online_status(profile, other_profile_ids, rooms=None):
if not other_profile_ids: if not other_profile_ids:
return None return None
Profile.prefetch_profile_cache(other_profile_ids)
joined_ids = ",".join([str(id) for id in other_profile_ids]) joined_ids = ",".join([str(id) for id in other_profile_ids])
other_profiles = Profile.objects.raw( other_profiles = Profile.objects.raw(
f"SELECT * from judge_profile where id in ({joined_ids}) order by field(id,{joined_ids})" 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_profile_ids = [str(i["other_user"]) for i in recent_profile]
recent_rooms = [int(i["id"]) for i in recent_profile] recent_rooms = [int(i["id"]) for i in recent_profile]
Room.prefetch_room_cache(recent_rooms)
admin_list = ( admin_list = (
queryset.filter(display_rank="admin") queryset.filter(display_rank="admin")

View file

@ -74,7 +74,17 @@ def cache_wrapper(prefix, timeout=None):
if l0_cache: if l0_cache:
l0_cache.delete(cache_key) 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.dirty = dirty
wrapper.prefetch_multi = prefetch_multi
return wrapper return wrapper

View file

@ -253,24 +253,9 @@ class Profile(models.Model):
max_length=300, 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 @cached_property
def _cached_info(self): def _cached_info(self):
return self._get_basic_info() return _get_basic_info(self.id)
@cached_property @cached_property
def organization(self): def organization(self):
@ -412,6 +397,10 @@ class Profile(models.Model):
or self.user.is_superuser 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: class Meta:
indexes = [ indexes = [
models.Index(fields=["is_unlisted", "performance_points"]), models.Index(fields=["is_unlisted", "performance_points"]),
@ -540,7 +529,7 @@ class OrganizationProfile(models.Model):
def on_user_save(sender, instance, **kwargs): def on_user_save(sender, instance, **kwargs):
try: try:
profile = instance.profile profile = instance.profile
profile._get_basic_info.dirty(profile) _get_basic_info.dirty(profile.id)
except: except:
pass pass
@ -551,4 +540,34 @@ def on_profile_save(sender, instance, **kwargs):
return return
prev = sender.objects.get(id=instance.id) prev = sender.objects.get(id=instance.id)
if prev.mute != instance.mute or prev.profile_image != instance.profile_image: 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

View file

@ -470,6 +470,7 @@ class SubmissionsListBase(DiggPaginatorMixin, TitleMixin, ListView):
if context["in_hidden_subtasks_contest"]: if context["in_hidden_subtasks_contest"]:
for submission in context["submissions"]: for submission in context["submissions"]:
self.modify_attrs(submission) self.modify_attrs(submission)
return context return context
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):

View file

@ -457,14 +457,13 @@ class UserList(QueryStringSortMixin, InfinitePaginationMixin, TitleMixin, ListVi
queryset = ( queryset = (
Profile.objects.filter(is_unlisted=False) Profile.objects.filter(is_unlisted=False)
.order_by(self.order, "id") .order_by(self.order, "id")
.select_related("user")
.only( .only(
"display_rank", "display_rank",
"user__username",
"points", "points",
"rating", "rating",
"performance_points", "performance_points",
"problem_count", "problem_count",
"about",
) )
) )
if self.request.organization: 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: if (self.request.GET.get("friend") == "true") and self.request.profile:
queryset = self.filter_friend_queryset(queryset) queryset = self.filter_friend_queryset(queryset)
self.filter_friend = True self.filter_friend = True
return queryset return queryset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(UserList, self).get_context_data(**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"] = ranker(
context["users"], rank=self.paginate_by * (context["page_obj"].number - 1) context["users"], rank=self.paginate_by * (context["page_obj"].number - 1)
) )