From 7bf0073e87ace54473db6d385353f53c7b00ad4a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 28 Dec 2025 13:00:13 +0000 Subject: [PATCH 1/2] Fix N+1 query in course sitemap generation Replace inefficient per-course document query with database aggregation using Max() annotation. This reduces sitemap generation from N+1 queries to just 1 query, where N is the number of courses. Before: - items() prefetched document_set for each course - lastmod() called .order_by().first() for each course (N queries) - For 1000 courses: 1001 queries After: - items() annotates with Max('document__created') - lastmod() returns annotated field - For 1000 courses: 1 query This fix prevents database overload during sitemap generation and significantly improves response time for sitemap endpoints. --- catalog/sitemap.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/catalog/sitemap.py b/catalog/sitemap.py index 5182605b..10ba8c3e 100644 --- a/catalog/sitemap.py +++ b/catalog/sitemap.py @@ -1,4 +1,5 @@ from django.contrib.sitemaps import Sitemap +from django.db.models import Max from catalog.models import Course @@ -9,9 +10,7 @@ class CourseSitemap(Sitemap): protocol = "https" def items(self): - return Course.objects.prefetch_related("document_set") + return Course.objects.annotate(latest_doc_date=Max("document__created")) def lastmod(self, obj: Course): - lastdoc = obj.document_set.order_by("-created").first() - if lastdoc: - return lastdoc.created + return obj.latest_doc_date From cdc37e5690f3bc70cadaeae6c49a7c4206112e9d Mon Sep 17 00:00:00 2001 From: Nikita Marchant Date: Sat, 3 Jan 2026 21:32:30 +0100 Subject: [PATCH 2/2] Ignore mypy false positive --- catalog/sitemap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalog/sitemap.py b/catalog/sitemap.py index 10ba8c3e..94f003fb 100644 --- a/catalog/sitemap.py +++ b/catalog/sitemap.py @@ -13,4 +13,4 @@ def items(self): return Course.objects.annotate(latest_doc_date=Max("document__created")) def lastmod(self, obj: Course): - return obj.latest_doc_date + return obj.latest_doc_date # type: ignore[attr-defined]