Skip to content

Commit

Permalink
fix: Performance degradation on Catalog Page
Browse files Browse the repository at this point in the history
  • Loading branch information
asadali145 committed Dec 19, 2022
1 parent 6680e87 commit 0ddb1d9
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 14 deletions.
Empty file added caching/__init__.py
Empty file.
61 changes: 61 additions & 0 deletions caching/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""
Interface for Django's caching API
"""
from dill import dumps, loads
from django.core.cache import cache
from django.db.models import Prefetch

from courses.models import Course
from ecommerce.models import Product


def get_course_key(course_id):
"""
Generates cache key for a course id.
Args:
course_id (int): id of a course.
Returns:
str: Cache for the given course id.
"""
return f"course_id:{course_id}"


def get_course_with_related_objects(course_id):
"""
Gets course with related objects from cache if it is available
else fetch the course with related objects and add it to the cache.
Args:
course_id (int): id of a course.
Returns:
Course or None: Course with related objects or None.
"""
cache_key = get_course_key(course_id)
course = cache.get(cache_key)
course = loads(course) if course else None
print("\n\n\nCOURSE FROM CACHE:", course)

if not course:
course = (
Course.objects.filter(id=course_id)
.select_related("program", "program__programpage")
.prefetch_related(
"courseruns",
Prefetch(
"courseruns__products", Product.objects.with_ordered_versions()
),
).first()
)
if course:
print("\n\n\nAdding Course Cache for:", course_id)
cache.set(cache_key, dumps(course), 24 * 60 * 60)

return course


def invalidate_course_cache(course_id):
cache_key = get_course_key(course_id)
cache.delete(cache_key)
13 changes: 2 additions & 11 deletions cms/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from wagtail.snippets.models import register_snippet
from wagtailmetadata.models import MetadataPageMixin

from caching.api import get_course_with_related_objects
from cms.api import filter_and_sort_catalog_pages
from cms.blocks import (
CourseRunCertificateOverrides,
Expand Down Expand Up @@ -976,17 +977,7 @@ def course_with_related_objects(self):
"""
Gets the course with related objects.
"""
return (
Course.objects.filter(id=self.course_id)
.select_related("program", "program__programpage")
.prefetch_related(
"courseruns",
Prefetch(
"courseruns__products", Product.objects.with_ordered_versions()
),
)
.first()
)
return get_course_with_related_objects(self.course_id)

def get_context(self, request, *args, **kwargs):
# Hits a circular import at the top of the module
Expand Down
35 changes: 34 additions & 1 deletion courses/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
from django.db.models.signals import post_save
from django.dispatch import receiver

from courses.models import CourseRunCertificate
from caching.api import invalidate_course_cache
from courses.models import CourseRunCertificate, Course, CourseRun, Program
from courses.utils import generate_program_certificate
from cms.models import ProgramPage
from ecommerce.models import Product, ProductVersion


@receiver(
Expand All @@ -25,3 +28,33 @@ def handle_create_course_run_certificate(
program = instance.course_run.course.program
if program:
transaction.on_commit(lambda: generate_program_certificate(user, program))


@receiver(post_save, sender=Course)
@receiver(post_save, sender=CourseRun)
@receiver(post_save, sender=Program)
@receiver(post_save, sender=ProgramPage)
@receiver(post_save, sender=Product)
def handle_course_cache_invalidation(sender, instance, created, **kwargs):
if sender == Course and created:
print("\n\n\nCreated a course object. No Cache to invalidate")
return

course_ids = []
if sender == Course:
course_ids = [instance.id]
elif sender == CourseRun:
course_ids = [instance.course_id]
elif sender == Program:
course_ids = instance.courses.all().values_list('id', flat=True)
elif sender == ProgramPage:
course_ids = instance.program.courses.all().values_list('id', flat=True)
elif sender == Product:
if instance.content_type.model == CourseRun.__name__.lower():
course_run = instance.run_queryset.first()
course_ids = [course_run.id]

if course_ids:
print("\n\n\nClear cache for Course IDs:", course_ids)
for course_id in course_ids:
invalidate_course_cache(course_id)
1 change: 1 addition & 0 deletions requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ wagtail==2.13.4
wagtail-metadata==3.3.0
zeep==3.4.0
flaky==3.7.0
dill==0.3.6
6 changes: 4 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#
# This file is autogenerated by pip-compile with python 3.9
# To update, run:
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
# pip-compile
#
Expand Down Expand Up @@ -88,6 +88,8 @@ defusedxml==0.7.1
# python3-openid
# social-auth-core
# zeep
dill==0.3.6
# via -r requirements.in
dj-database-url==0.5.0
# via -r requirements.in
django==3.2.15
Expand Down

0 comments on commit 0ddb1d9

Please sign in to comment.