From f047f70525c04f43272b76256f4a2506078ea1b8 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Fri, 21 Jul 2023 12:39:13 +0530 Subject: [PATCH 1/2] fix: issue create update n+1 and issue activity get n+1 --- apiserver/plane/api/serializers/issue.py | 94 ++++++++++++++---------- apiserver/plane/api/views/issue.py | 39 ++++++---- 2 files changed, 80 insertions(+), 53 deletions(-) diff --git a/apiserver/plane/api/serializers/issue.py b/apiserver/plane/api/serializers/issue.py index 7aeee7d7074..4b9b776b6e6 100644 --- a/apiserver/plane/api/serializers/issue.py +++ b/apiserver/plane/api/serializers/issue.py @@ -101,8 +101,15 @@ def create(self, validated_data): labels = validated_data.pop("labels_list", None) blocks = validated_data.pop("blocks_list", None) - project = self.context["project"] - issue = Issue.objects.create(**validated_data, project=project) + project_id = self.context["project_id"] + workspace_id = self.context["workspace_id"] + default_assignee_id = self.context["default_assignee_id"] + + issue = Issue.objects.create(**validated_data, project_id=project_id) + + # Issue Audit Users + created_by_id = issue.created_by_id + updated_by_id = issue.updated_by_id if blockers is not None and len(blockers): IssueBlocker.objects.bulk_create( @@ -110,10 +117,10 @@ def create(self, validated_data): IssueBlocker( block=issue, blocked_by=blocker, - project=project, - workspace=project.workspace, - created_by=issue.created_by, - updated_by=issue.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for blocker in blockers ], @@ -126,10 +133,10 @@ def create(self, validated_data): IssueAssignee( assignee=user, issue=issue, - project=project, - workspace=project.workspace, - created_by=issue.created_by, - updated_by=issue.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for user in assignees ], @@ -137,14 +144,14 @@ def create(self, validated_data): ) else: # Then assign it to default assignee - if project.default_assignee is not None: + if default_assignee_id is not None: IssueAssignee.objects.create( - assignee=project.default_assignee, + assignee_id=default_assignee_id, issue=issue, - project=project, - workspace=project.workspace, - created_by=issue.created_by, - updated_by=issue.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) if labels is not None and len(labels): @@ -153,10 +160,10 @@ def create(self, validated_data): IssueLabel( label=label, issue=issue, - project=project, - workspace=project.workspace, - created_by=issue.created_by, - updated_by=issue.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for label in labels ], @@ -169,10 +176,10 @@ def create(self, validated_data): IssueBlocker( block=block, blocked_by=issue, - project=project, - workspace=project.workspace, - created_by=issue.created_by, - updated_by=issue.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for block in blocks ], @@ -187,6 +194,12 @@ def update(self, instance, validated_data): labels = validated_data.pop("labels_list", None) blocks = validated_data.pop("blocks_list", None) + # Related models + project_id = instance.project_id + workspace_id = instance.workspace_id + created_by_id = instance.created_by_id + updated_by_id = instance.updated_by_id + if blockers is not None: IssueBlocker.objects.filter(block=instance).delete() IssueBlocker.objects.bulk_create( @@ -194,10 +207,10 @@ def update(self, instance, validated_data): IssueBlocker( block=instance, blocked_by=blocker, - project=instance.project, - workspace=instance.project.workspace, - created_by=instance.created_by, - updated_by=instance.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for blocker in blockers ], @@ -211,10 +224,10 @@ def update(self, instance, validated_data): IssueAssignee( assignee=user, issue=instance, - project=instance.project, - workspace=instance.project.workspace, - created_by=instance.created_by, - updated_by=instance.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for user in assignees ], @@ -228,10 +241,10 @@ def update(self, instance, validated_data): IssueLabel( label=label, issue=instance, - project=instance.project, - workspace=instance.project.workspace, - created_by=instance.created_by, - updated_by=instance.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for label in labels ], @@ -245,16 +258,17 @@ def update(self, instance, validated_data): IssueBlocker( block=block, blocked_by=instance, - project=instance.project, - workspace=instance.project.workspace, - created_by=instance.created_by, - updated_by=instance.updated_by, + project_id=project_id, + workspace_id=workspace_id, + created_by_id=created_by_id, + updated_by_id=updated_by_id, ) for block in blocks ], batch_size=10, ) + # Time updation occues even when other related models are updated instance.updated_at = timezone.now() return super().update(instance, validated_data) diff --git a/apiserver/plane/api/views/issue.py b/apiserver/plane/api/views/issue.py index aab926fd2c7..e6379cfa494 100644 --- a/apiserver/plane/api/views/issue.py +++ b/apiserver/plane/api/views/issue.py @@ -270,9 +270,15 @@ def list(self, request, slug, project_id): def create(self, request, slug, project_id): try: - project = Project.objects.get(workspace__slug=slug, pk=project_id) + project = Project.objects.get(pk=project_id) + serializer = IssueCreateSerializer( - data=request.data, context={"project": project} + data=request.data, + context={ + "project_id": project_id, + "workspace_id": project.workspace_id, + "default_assignee_id": project.default_assignee_id, + }, ) if serializer.is_valid(): @@ -396,6 +402,7 @@ def get(self, request, slug, project_id, issue_id): IssueComment.objects.filter(issue_id=issue_id) .filter(project__project_projectmember__member=self.request.user) .order_by("created_at") + .select_related("actor", "issue", "project", "workspace") ) issue_activities = IssueActivitySerializer(issue_activities, many=True).data issue_comments = IssueCommentSerializer(issue_comments, many=True).data @@ -1096,7 +1103,8 @@ def unarchive(self, request, slug, project_id, pk=None): return Response(IssueSerializer(issue).data, status=status.HTTP_200_OK) except Issue.DoesNotExist: return Response( - {"error": "Issue Does not exist"}, status=status.HTTP_404_NOT_FOUND) + {"error": "Issue Does not exist"}, status=status.HTTP_404_NOT_FOUND + ) except Exception as e: capture_exception(e) return Response( @@ -1104,6 +1112,7 @@ def unarchive(self, request, slug, project_id, pk=None): status=status.HTTP_400_BAD_REQUEST, ) + class IssueSubscriberViewSet(BaseViewSet): serializer_class = IssueSubscriberSerializer model = IssueSubscriber @@ -1144,18 +1153,22 @@ def get_queryset(self): def list(self, request, slug, project_id, issue_id): try: - members = ProjectMember.objects.filter( - workspace__slug=slug, project_id=project_id - ).annotate( - is_subscribed=Exists( - IssueSubscriber.objects.filter( - workspace__slug=slug, - project_id=project_id, - issue_id=issue_id, - subscriber=OuterRef("member"), + members = ( + ProjectMember.objects.filter( + workspace__slug=slug, project_id=project_id + ) + .annotate( + is_subscribed=Exists( + IssueSubscriber.objects.filter( + workspace__slug=slug, + project_id=project_id, + issue_id=issue_id, + subscriber=OuterRef("member"), + ) ) ) - ).select_related("member") + .select_related("member") + ) serializer = ProjectMemberLiteSerializer(members, many=True) return Response(serializer.data, status=status.HTTP_200_OK) except Exception as e: From b566739d13a2afedd08c603a54334e466c341ad2 Mon Sep 17 00:00:00 2001 From: pablohashescobar Date: Sat, 22 Jul 2023 18:03:10 +0530 Subject: [PATCH 2/2] dev: notifications n+1 --- apiserver/plane/api/views/notification.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apiserver/plane/api/views/notification.py b/apiserver/plane/api/views/notification.py index b7f5bd33587..5222ce4f734 100644 --- a/apiserver/plane/api/views/notification.py +++ b/apiserver/plane/api/views/notification.py @@ -37,9 +37,13 @@ def list(self, request, slug): # Filter type type = request.GET.get("type", "all") - notifications = Notification.objects.filter( - workspace__slug=slug, receiver_id=request.user.id - ).order_by("snoozed_till", "-created_at") + notifications = ( + Notification.objects.filter( + workspace__slug=slug, receiver_id=request.user.id + ) + .select_related("workspace", "project," "triggered_by", "receiver") + .order_by("snoozed_till", "-created_at") + ) # Filter for snoozed notifications if snoozed == "false":