Optimise for cache hits

Don't select/prefetch for listing, because we cache the entire listing item. This makes the main query more efficient, thus page loads faster.
This commit is contained in:
Jake Howard 2022-10-04 20:49:29 +01:00
parent fa85c8ba80
commit b515b6368c
Signed by: jake
GPG key ID: 57AFB45680EDD477
6 changed files with 24 additions and 41 deletions

View file

@ -9,7 +9,7 @@ from wagtail.admin.panels import FieldPanel
from wagtailautocomplete.edit_handlers import AutocompletePanel from wagtailautocomplete.edit_handlers import AutocompletePanel
from website.common.models import BaseContentPage, BaseListingPage from website.common.models import BaseContentPage, BaseListingPage
from website.common.utils import TocEntry, prefetch_for_listing from website.common.utils import TocEntry
from website.common.views import ContentPageFeed from website.common.views import ContentPageFeed
from website.contrib.singleton_page.utils import SingletonPageCache from website.contrib.singleton_page.utils import SingletonPageCache
@ -43,12 +43,11 @@ class BlogPostListPage(BaseListingPage):
] ]
def get_listing_pages(self) -> models.QuerySet: def get_listing_pages(self) -> models.QuerySet:
return prefetch_for_listing( return (
BlogPostPage.objects.descendant_of(self) BlogPostPage.objects.descendant_of(self)
.live() .live()
.public() .public()
.order_by("-date", "title") .order_by("-date", "title")
.prefetch_related("tags")
) )
@property @property
@ -127,11 +126,10 @@ class BlogPostCollectionPage(BaseListingPage):
subpage_types = [BlogPostPage] subpage_types = [BlogPostPage]
def get_listing_pages(self) -> models.QuerySet: def get_listing_pages(self) -> models.QuerySet:
return prefetch_for_listing( return (
BlogPostPage.objects.child_of(self) BlogPostPage.objects.child_of(self)
.live() .live()
.public() .public()
.prefetch_related("tags")
.order_by("-date", "title") .order_by("-date", "title")
) )

View file

@ -33,7 +33,6 @@ from .utils import (
count_words, count_words,
extract_text, extract_text,
get_table_of_contents, get_table_of_contents,
prefetch_for_listing,
truncate_string, truncate_string,
) )
@ -190,9 +189,7 @@ class BaseListingPage(RoutablePageMixin, BaseContentPage):
abstract = True abstract = True
def get_listing_pages(self) -> models.QuerySet: def get_listing_pages(self) -> models.QuerySet:
return prefetch_for_listing( return self.get_children().live().public().specific().order_by("title")
self.get_children().live().public().specific().order_by("title")
)
def get_paginator_page(self) -> PaginatorPage: def get_paginator_page(self) -> PaginatorPage:
paginator = Paginator(self.get_listing_pages(), per_page=self.PAGE_SIZE) paginator = Paginator(self.get_listing_pages(), per_page=self.PAGE_SIZE)

View file

@ -1,6 +1,7 @@
{% load wagtailcore_tags cache util_tags %} {% load wagtailcore_tags cache util_tags %}
<article class="media listing-item"> {% cache FRAGMENT_CACHE_TTL|jitter:FRAGMENT_CACHE_TTL_JITTER "listing-item" page.id request.is_preview %}
<article class="media listing-item">
<div class="columns"> <div class="columns">
<figure class="media-left column is-3 image-column"> <figure class="media-left column is-3 image-column">
{% if page.list_image_url %} {% if page.list_image_url %}
@ -13,10 +14,9 @@
<div> <div>
<h2 class="title is-3"><a href="{% pageurl page %}">{{ page.title }}</a></h2> <h2 class="title is-3"><a href="{% pageurl page %}">{{ page.title }}</a></h2>
{% include "common/content-details.html" %} {% include "common/content-details.html" %}
{% cache FRAGMENT_CACHE_TTL|jitter:FRAGMENT_CACHE_TTL_JITTER "summary" page.id request.is_preview %}
<p>{{ page.summary }}</p> <p>{{ page.summary }}</p>
{% endcache %}
</div> </div>
</div> </div>
</div> </div>
</article> </article>
{% endcache %}

View file

@ -87,16 +87,6 @@ def truncate_string(text: str, words: int) -> str:
return " ".join(islice(smart_split(text), words)) return " ".join(islice(smart_split(text), words))
def prefetch_for_listing(queryset: PageQuerySet) -> PageQuerySet:
"""
Prefetch a queryset ready for listing.
This should be a queryset method, but dealing with lots of
different page models is a pain.
"""
return queryset.select_related("hero_image", "hero_unsplash_photo")
def heading_id(heading: str) -> str: def heading_id(heading: str) -> str:
""" """
Convert a heading into an identifier which is valid for a HTML id attribute Convert a heading into an identifier which is valid for a HTML id attribute

View file

@ -6,7 +6,7 @@ from wagtail.models import Page
from website.common.models import BasePage from website.common.models import BasePage
FRAGMENT_CACHES = {"summary", "content-details"} FRAGMENT_CACHES = {"listing-item", "content-details"}
@hooks.register("after_edit_page") @hooks.register("after_edit_page")

View file

@ -10,7 +10,7 @@ from wagtail.models import Page
from wagtail.search.utils import parse_query_string from wagtail.search.utils import parse_query_string
from website.common.models import BaseContentPage from website.common.models import BaseContentPage
from website.common.utils import TocEntry, prefetch_for_listing from website.common.utils import TocEntry
from website.home.models import HomePage from website.home.models import HomePage
from .serializers import MIN_SEARCH_LENGTH, SearchParamsSerializer from .serializers import MIN_SEARCH_LENGTH, SearchParamsSerializer
@ -76,9 +76,7 @@ class SearchPage(RoutablePageMixin, BaseContentPage):
results = paginator.page(page_num) results = paginator.page(page_num)
# HACK: Search results aren't a queryset, so we can't call `.specific` on it. This forces it to one as efficiently as possible # HACK: Search results aren't a queryset, so we can't call `.specific` on it. This forces it to one as efficiently as possible
results.object_list = prefetch_for_listing( results.object_list = results.object_list.get_queryset().specific()
results.object_list.get_queryset().specific()
)
except EmptyPage: except EmptyPage:
raise Http404 raise Http404