From e49441491984054320630d817fb68370f5049b39 Mon Sep 17 00:00:00 2001 From: Anthony Johnson Date: Wed, 25 Sep 2024 12:19:59 -0700 Subject: [PATCH 1/3] Don't subquery for builds in project listing prefetch --- readthedocs/projects/querysets.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/readthedocs/projects/querysets.py b/readthedocs/projects/querysets.py index baa17db7e76..08dcea75062 100644 --- a/readthedocs/projects/querysets.py +++ b/readthedocs/projects/querysets.py @@ -1,6 +1,6 @@ """Project model QuerySet classes.""" from django.db import models -from django.db.models import Count, OuterRef, Prefetch, Q, Subquery +from django.db.models import Count, Max, Prefetch, Q from readthedocs.core.permissions import AdminPermission from readthedocs.core.querysets import NoReprQuerySet @@ -131,27 +131,30 @@ def prefetch_latest_build(self): """ from readthedocs.builds.models import Build - # Prefetch the latest build for each project. - subquery_build_latest = Subquery( - Build.internal.filter(project=OuterRef("project_id")) - .order_by("-date") - .values_list("id", flat=True)[:1] + # Get most recent and recent successful builds + builds_latest = ( + Build.objects.values("project") + .annotate(latest=Max("pk")) + .values_list("latest", flat=True) + ) + builds_success = ( + Build.objects.filter(success=True) + .values("project") + .annotate(latest=Max("pk")) + .values_list("latest", flat=True) ) + + # Prefetch the latest build for each project. prefetch_build_latest = Prefetch( "builds", - Build.internal.filter(pk__in=subquery_build_latest), + Build.internal.filter(pk__in=builds_latest), to_attr=self.model.LATEST_BUILD_CACHE, ) # Prefetch the latest successful build for each project. - subquery_build_successful = Subquery( - Build.internal.filter(project=OuterRef("project_id")) - .order_by("-date") - .values_list("id", flat=True)[:1] - ) prefetch_build_successful = Prefetch( "builds", - Build.internal.filter(pk__in=subquery_build_successful), + Build.internal.filter(pk__in=builds_success), to_attr=self.model.LATEST_SUCCESSFUL_BUILD_CACHE, ) From c89d84fa4c8e9f958bfce625af862c8cf0e05594 Mon Sep 17 00:00:00 2001 From: Anthony Johnson Date: Wed, 25 Sep 2024 12:37:04 -0700 Subject: [PATCH 2/3] Missing filter by projects --- readthedocs/projects/querysets.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/readthedocs/projects/querysets.py b/readthedocs/projects/querysets.py index 08dcea75062..9d383d84297 100644 --- a/readthedocs/projects/querysets.py +++ b/readthedocs/projects/querysets.py @@ -133,12 +133,13 @@ def prefetch_latest_build(self): # Get most recent and recent successful builds builds_latest = ( - Build.objects.values("project") + Build.objects.filter(project__in=self) + .values("project") .annotate(latest=Max("pk")) .values_list("latest", flat=True) ) builds_success = ( - Build.objects.filter(success=True) + Build.objects.filter(project__in=self, success=True) .values("project") .annotate(latest=Max("pk")) .values_list("latest", flat=True) From 87ca17e46050c622347b6380255ca3e2febaf5d2 Mon Sep 17 00:00:00 2001 From: Anthony Johnson Date: Wed, 25 Sep 2024 13:41:47 -0700 Subject: [PATCH 3/3] Use Build.internal queryset instead This seems like it's unneccessary, but the prefetch is not accurate using Build.objects first. --- readthedocs/projects/querysets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readthedocs/projects/querysets.py b/readthedocs/projects/querysets.py index 9d383d84297..4e519e50268 100644 --- a/readthedocs/projects/querysets.py +++ b/readthedocs/projects/querysets.py @@ -133,13 +133,13 @@ def prefetch_latest_build(self): # Get most recent and recent successful builds builds_latest = ( - Build.objects.filter(project__in=self) + Build.internal.filter(project__in=self) .values("project") .annotate(latest=Max("pk")) .values_list("latest", flat=True) ) builds_success = ( - Build.objects.filter(project__in=self, success=True) + Build.internal.filter(project__in=self, success=True) .values("project") .annotate(latest=Max("pk")) .values_list("latest", flat=True)