From 08ce22b59b180cc1a9ccb91fa41b1103b7feb41c Mon Sep 17 00:00:00 2001 From: Yulya Artyukhina Date: Thu, 11 Apr 2024 16:49:47 +0200 Subject: [PATCH] Optimize GET /users internal api endpoint (#4168) # What this PR does Speed up `GET /users` internal api endpoint by reducing number of calls to database ## Which issue(s) this PR closes Related to slow schedules page issue - https://github.com/grafana/oncall-private/issues/1552 ## Checklist - [ ] 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/api/serializers/user.py | 17 +++++++++++++++-- engine/apps/api/views/user.py | 24 +++++++++++++++++++++--- engine/apps/mobile_app/backend.py | 4 +--- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/engine/apps/api/serializers/user.py b/engine/apps/api/serializers/user.py index 8ae48492..b428aefb 100644 --- a/engine/apps/api/serializers/user.py +++ b/engine/apps/api/serializers/user.py @@ -88,7 +88,14 @@ class ListUserSerializer(DynamicFieldsModelSerializer, EagerLoadingMixin): cloud_connection_status = serializers.SerializerMethodField() working_hours = WorkingHoursSerializer(required=False) - SELECT_RELATED = ["telegram_verification_code", "telegram_connection", "organization", "slack_user_identity"] + SELECT_RELATED = [ + "telegram_verification_code", + "telegram_connection", + "organization", + "slack_user_identity", + "mobileappauthtoken", + "google_oauth2_user", + ] class Meta: model = User @@ -159,7 +166,13 @@ class ListUserSerializer(DynamicFieldsModelSerializer, EagerLoadingMixin): return {"default": " - ".join(default), "important": " - ".join(important)} def get_cloud_connection_status(self, obj: User) -> CloudSyncStatus | None: - if settings.IS_OPEN_SOURCE and live_settings.GRAFANA_CLOUD_NOTIFICATIONS_ENABLED: + is_open_source_with_cloud_notifications = self.context.get("is_open_source_with_cloud_notifications", None) + is_open_source_with_cloud_notifications = ( + is_open_source_with_cloud_notifications + if is_open_source_with_cloud_notifications is not None + else settings.IS_OPEN_SOURCE and live_settings.GRAFANA_CLOUD_NOTIFICATIONS_ENABLED + ) + if is_open_source_with_cloud_notifications: connector = self.context.get("connector", None) identities = self.context.get("cloud_identities", {}) identity = identities.get(obj.email, None) diff --git a/engine/apps/api/views/user.py b/engine/apps/api/views/user.py index ebc8827b..e0ee7414 100644 --- a/engine/apps/api/views/user.py +++ b/engine/apps/api/views/user.py @@ -137,7 +137,13 @@ class CurrentUserView(APIView, CachedSchedulesContextMixin): context = self.get_serializer_context() context.update({"request": self.request, "format": self.format_kwarg, "view": self}) - if settings.IS_OPEN_SOURCE and live_settings.GRAFANA_CLOUD_NOTIFICATIONS_ENABLED: + is_open_source_with_cloud_notifications = ( + settings.IS_OPEN_SOURCE and live_settings.GRAFANA_CLOUD_NOTIFICATIONS_ENABLED + ) + # set context to avoid additional requests to db + context["is_open_source_with_cloud_notifications"] = is_open_source_with_cloud_notifications + + if is_open_source_with_cloud_notifications: from apps.oss_installation.models import CloudConnector, CloudUserIdentity connector = CloudConnector.objects.first() @@ -368,7 +374,13 @@ class UserView( context = self.get_serializer_context() if paginate_results and (page := self.paginate_queryset(queryset)) is not None: - if settings.IS_OPEN_SOURCE and live_settings.GRAFANA_CLOUD_NOTIFICATIONS_ENABLED: + is_open_source_with_cloud_notifications = ( + settings.IS_OPEN_SOURCE and live_settings.GRAFANA_CLOUD_NOTIFICATIONS_ENABLED + ) + # set context to avoid additional requests to db + context["is_open_source_with_cloud_notifications"] = is_open_source_with_cloud_notifications + + if is_open_source_with_cloud_notifications: from apps.oss_installation.models import CloudConnector, CloudUserIdentity if (connector := CloudConnector.objects.first()) is not None: @@ -393,7 +405,13 @@ class UserView( except NotFound: return self.wrong_team_response() - if settings.IS_OPEN_SOURCE and live_settings.GRAFANA_CLOUD_NOTIFICATIONS_ENABLED: + is_open_source_with_cloud_notifications = ( + settings.IS_OPEN_SOURCE and live_settings.GRAFANA_CLOUD_NOTIFICATIONS_ENABLED + ) + # set context to avoid additional requests to db + context["is_open_source_with_cloud_notifications"] = is_open_source_with_cloud_notifications + + if is_open_source_with_cloud_notifications: from apps.oss_installation.models import CloudConnector, CloudUserIdentity connector = CloudConnector.objects.first() diff --git a/engine/apps/mobile_app/backend.py b/engine/apps/mobile_app/backend.py index 3082c340..ec2f5d91 100644 --- a/engine/apps/mobile_app/backend.py +++ b/engine/apps/mobile_app/backend.py @@ -42,9 +42,7 @@ class MobileAppBackend(BaseMessagingBackend): user_active_device.delete() def serialize_user(self, user): - from apps.mobile_app.models import MobileAppAuthToken - - return {"connected": MobileAppAuthToken.objects.filter(user=user).exists()} + return {"connected": getattr(user, "mobileappauthtoken", None) is not None} def notify_user(self, user, alert_group, notification_policy, critical=False): notify_user_about_new_alert_group.delay(