From eb9e2353b386cd0cf9997bcf89a11c0d71fe8355 Mon Sep 17 00:00:00 2001 From: Vadim Stepanov Date: Thu, 25 Jul 2024 18:26:05 +0100 Subject: [PATCH] Fix N+1 issue on alert group list page (#4738) # What this PR does Before: Screenshot 2024-07-25 at 17 35 28 After: Screenshot 2024-07-25 at 18 11 53 ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes. --- engine/apps/alerts/models/alert_group.py | 18 ++++++++++++++---- engine/apps/api/serializers/alert_group.py | 21 ++++++++++++++++++++- engine/settings/base.py | 1 + 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/engine/apps/alerts/models/alert_group.py b/engine/apps/alerts/models/alert_group.py index 59162a34..dbfd97ad 100644 --- a/engine/apps/alerts/models/alert_group.py +++ b/engine/apps/alerts/models/alert_group.py @@ -515,9 +515,15 @@ class AlertGroup(AlertGroupSlackRenderingMixin, EscalationSnapshotMixin, models. def telegram_permalink(self) -> typing.Optional[str]: from apps.telegram.models.message import TelegramMessage - main_telegram_message = self.telegram_messages.filter( - chat_id__startswith="-", message_type=TelegramMessage.ALERT_GROUP_MESSAGE - ).first() + try: + # prefetched_telegram_messages could be set in apps.api.serializers.alert_group.AlertGroupListSerializer + main_telegram_message = self.prefetched_telegram_messages[0] if self.prefetched_telegram_messages else None + except AttributeError: + main_telegram_message = ( + self.telegram_messages.filter(chat_id__startswith="-", message_type=TelegramMessage.ALERT_GROUP_MESSAGE) + .order_by("id") + .first() + ) return main_telegram_message.link if main_telegram_message else None @@ -1971,7 +1977,11 @@ class AlertGroup(AlertGroupSlackRenderingMixin, EscalationSnapshotMixin, models. @property def slack_message(self) -> typing.Optional["SlackMessage"]: - return self.slack_messages.order_by("created_at").first() + try: + # prefetched_slack_messages could be set in apps.api.serializers.alert_group.AlertGroupListSerializer + return self.prefetched_slack_messages[0] if self.prefetched_slack_messages else None + except AttributeError: + return self.slack_messages.order_by("created_at").first() @cached_property def last_stop_escalation_log(self): diff --git a/engine/apps/api/serializers/alert_group.py b/engine/apps/api/serializers/alert_group.py index 1c2a9e9f..27c0910e 100644 --- a/engine/apps/api/serializers/alert_group.py +++ b/engine/apps/api/serializers/alert_group.py @@ -2,7 +2,9 @@ import datetime import logging import typing +from django.conf import settings from django.core.cache import cache +from django.db.models import Prefetch from django.utils import timezone from drf_spectacular.utils import extend_schema_field from rest_framework import serializers @@ -10,6 +12,8 @@ from rest_framework import serializers from apps.alerts.incident_appearance.renderers.web_renderer import AlertGroupWebRenderer from apps.alerts.models import AlertGroup from apps.alerts.models.alert_group import PagedUser +from apps.slack.models import SlackMessage +from apps.telegram.models import TelegramMessage from common.api_helpers.custom_fields import TeamPrimaryKeyRelatedField from common.api_helpers.mixins import EagerLoadingMixin @@ -129,11 +133,26 @@ class AlertGroupListSerializer( labels = AlertGroupLabelSerializer(many=True, read_only=True) - PREFETCH_RELATED = [ + PREFETCH_RELATED: list[str | Prefetch] = [ "dependent_alert_groups", "log_records__author", "labels", ] + if settings.ALERT_GROUP_LIST_TRY_PREFETCH: + PREFETCH_RELATED += [ + Prefetch( + "slack_messages", + queryset=SlackMessage.objects.select_related("_slack_team_identity").order_by("created_at")[:1], + to_attr="prefetched_slack_messages", + ), + Prefetch( + "telegram_messages", + queryset=TelegramMessage.objects.filter( + chat_id__startswith="-", message_type=TelegramMessage.ALERT_GROUP_MESSAGE + ).order_by("id")[:1], + to_attr="prefetched_telegram_messages", + ), + ] SELECT_RELATED = [ "channel__organization", diff --git a/engine/settings/base.py b/engine/settings/base.py index 341d7cc0..a6973c62 100644 --- a/engine/settings/base.py +++ b/engine/settings/base.py @@ -194,6 +194,7 @@ DJANGO_MYSQL_REWRITE_QUERIES = True ALERT_GROUPS_DISABLE_PREFER_ORDERING_INDEX = DATABASE_TYPE == DatabaseTypes.MYSQL and getenv_boolean( "ALERT_GROUPS_DISABLE_PREFER_ORDERING_INDEX", default=False ) +ALERT_GROUP_LIST_TRY_PREFETCH = getenv_boolean("ALERT_GROUP_LIST_TRY_PREFETCH", default=False) # Redis REDIS_USERNAME = os.getenv("REDIS_USERNAME", "")