|
5 | 5 | from django.db.models import Count |
6 | 6 | from django.db.models import Exists |
7 | 7 | from django.db.models import OuterRef |
8 | | -from django.db.models import Prefetch |
9 | 8 | from django.db.models import Q |
10 | 9 |
|
11 | 10 | from readthedocs.core.permissions import AdminPermission |
@@ -134,25 +133,26 @@ def max_concurrent_builds(self, project): |
134 | 133 |
|
135 | 134 | def prefetch_latest_build(self): |
136 | 135 | """ |
137 | | - Prefetch "latest build" for each project. |
| 136 | + Prefetch and annotate to avoid N+1 queries. |
138 | 137 |
|
139 | 138 | .. note:: |
140 | 139 |
|
141 | 140 | This should come after any filtering. |
142 | 141 | """ |
143 | 142 | from readthedocs.builds.models import Build |
144 | 143 |
|
145 | | - # Prefetch the latest build for each project. |
146 | | - latest_build = Prefetch( |
147 | | - "builds", |
148 | | - Build.internal.select_related("version").order_by("-date")[:1], |
149 | | - to_attr=self.model.LATEST_BUILD_CACHE, |
150 | | - ) |
151 | | - query = self.prefetch_related(latest_build) |
| 144 | + # NOTE: prefetching the latest build will perform worse than just |
| 145 | + # accessing the latest build for each project. |
| 146 | + # While prefetching reduces the number of queries, |
| 147 | + # the query used to fetch the latest build can be quite expensive, |
| 148 | + # specially in projects with lots of builds. |
| 149 | + # Not prefetching here is fine, as this query is paginated by 15 |
| 150 | + # items per page, so it will generate at most 15 queries. |
152 | 151 |
|
| 152 | + # This annotation performs fine in all cases. |
153 | 153 | # Annotate whether the project has a successful build or not, |
154 | 154 | # to avoid N+1 queries when showing the build status. |
155 | | - return query.annotate( |
| 155 | + return self.annotate( |
156 | 156 | _has_good_build=Exists(Build.internal.filter(project=OuterRef("pk"), success=True)) |
157 | 157 | ) |
158 | 158 |
|
|
0 commit comments