Skip to content

Commit 4eeba7b

Browse files
8621-featured-items-celery-task-is-not-robust (#2973)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 14adcfd commit 4eeba7b

File tree

7 files changed

+45
-43
lines changed

7 files changed

+45
-43
lines changed

cms/api.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
from cms.constants import CERTIFICATE_INDEX_SLUG, INSTRUCTOR_INDEX_SLUG
2323
from cms.exceptions import WagtailSpecificPageError
2424
from cms.models import Page
25-
from courses.constants import HOMEPAGE_CACHE_AGE
2625
from courses.models import Course, Program
2726
from courses.utils import (
2827
get_enrollable_courseruns_qs,
@@ -364,7 +363,7 @@ def create_featured_items():
364363
)
365364

366365
if not valid_course_ids:
367-
redis_cache.set(cache_key, [], HOMEPAGE_CACHE_AGE)
366+
redis_cache.set(cache_key, [])
368367
return []
369368

370369
enrollable_courses_qs = Course.objects.select_related("page").filter(
@@ -377,7 +376,7 @@ def create_featured_items():
377376
)
378377

379378
if not enrollable_courseruns.exists():
380-
redis_cache.set(cache_key, [], HOMEPAGE_CACHE_AGE)
379+
redis_cache.set(cache_key, [])
381380
return []
382381

383382
self_paced_runs = []
@@ -410,21 +409,20 @@ def create_featured_items():
410409
all_course_ids = self_paced_course_ids + future_course_ids + started_course_ids
411410

412411
if not all_course_ids:
413-
redis_cache.set(cache_key, [], HOMEPAGE_CACHE_AGE)
412+
redis_cache.set(cache_key, [])
414413
return []
415414

416415
ordering = Case(
417416
*[When(id=cid, then=pos) for pos, cid in enumerate(all_course_ids)],
418417
output_field=IntegerField(),
419418
)
420419

421-
featured_courses = list(
420+
# Store only course IDs to avoid pickling issues and ensure fresh data on retrieval
421+
redis_cache.set(cache_key, all_course_ids)
422+
423+
return list(
422424
Course.objects.filter(id__in=all_course_ids)
423425
.select_related("page")
424426
.prefetch_related("courseruns")
425427
.order_by(ordering)
426428
)
427-
428-
redis_cache.set(cache_key, featured_courses, HOMEPAGE_CACHE_AGE)
429-
430-
return featured_courses

cms/api_test.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -360,18 +360,18 @@ def test_create_featured_items():
360360

361361
assert len(cache_value) == 4
362362
assert set(cache_value) == {
363-
enrollable_future_course,
364-
enrollable_other_future_course,
365-
enrollable_self_paced_course,
366-
in_progress_course,
363+
enrollable_future_course.id,
364+
enrollable_other_future_course.id,
365+
enrollable_self_paced_course.id,
366+
in_progress_course.id,
367367
}
368368

369-
assert cache_value[0] == enrollable_self_paced_course
370-
assert cache_value[1] == enrollable_future_course
371-
assert cache_value[2] == enrollable_other_future_course
372-
assert cache_value[3] == in_progress_course
369+
assert cache_value[0] == enrollable_self_paced_course.id
370+
assert cache_value[1] == enrollable_future_course.id
371+
assert cache_value[2] == enrollable_other_future_course.id
372+
assert cache_value[3] == in_progress_course.id
373373

374-
assert unenrollable_course not in cache_value
374+
assert unenrollable_course.id not in cache_value
375375

376376

377377
@pytest.mark.django_db
@@ -496,7 +496,7 @@ def test_create_featured_items_course_run_at_exact_time():
496496

497497
assert len(result) == 1
498498
assert result[0] == exact_time_course
499-
assert exact_time_course in cache_value
499+
assert exact_time_course.id in cache_value
500500

501501

502502
@pytest.mark.django_db

cms/models.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -792,13 +792,13 @@ def get_cached_featured_products(self):
792792
"""
793793
now = now_in_utc()
794794
redis_cache = caches["redis"]
795-
cached_featured_products = redis_cache.get("CMS_homepage_featured_courses")
796-
if cached_featured_products and len(cached_featured_products) > 0:
797-
featured_product_ids = [course.id for course in cached_featured_products]
795+
cached_featured_course_ids = redis_cache.get("CMS_homepage_featured_courses")
796+
if cached_featured_course_ids and len(cached_featured_course_ids) > 0:
797+
# Load fresh course data from database using cached IDs
798798
relevant_run_course_ids = (
799799
CourseRun.objects.filter(live=True)
800800
.filter(
801-
models.Q(course__id__in=featured_product_ids)
801+
models.Q(course__id__in=cached_featured_course_ids)
802802
& models.Q(enrollment_start__lte=now)
803803
& (
804804
models.Q(enrollment_end__gte=now)
@@ -807,12 +807,28 @@ def get_cached_featured_products(self):
807807
)
808808
.values_list("course__id", flat=True)
809809
)
810+
811+
# Create ordering to maintain the sequence from the cache
812+
ordering = models.Case(
813+
*[
814+
models.When(id=cid, then=pos)
815+
for pos, cid in enumerate(cached_featured_course_ids)
816+
],
817+
output_field=models.IntegerField(),
818+
)
819+
820+
valid_course_ids = set(cached_featured_course_ids) & set(
821+
relevant_run_course_ids
822+
)
823+
824+
featured_courses = (
825+
Course.objects.filter(id__in=valid_course_ids, page__live=True)
826+
.select_related("page")
827+
.order_by(ordering)
828+
)
829+
810830
featured_products = [
811-
course.page
812-
for course in cached_featured_products
813-
if course.page is not None
814-
and course.page.live
815-
and course.id in relevant_run_course_ids
831+
course.page for course in featured_courses if course.page is not None
816832
]
817833
featured_product_pages = []
818834
for page in featured_products:

cms/tasks.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22
from urllib.parse import urljoin, urlparse
33

44
import requests
5-
from django.core.cache import caches
65
from mitol.common.decorators import single_task
76

87
from cms.api import create_featured_items
9-
from cms.constants import ONE_MINUTE
108
from cms.models import Page
119
from main.celery import app
1210
from main.settings import (
@@ -109,12 +107,5 @@ def refresh_featured_homepage_items():
109107
"""
110108
logger = logging.getLogger("refresh_featured_homepage_items__task")
111109
logger.info("Refreshing featured homepage items...")
112-
# if the key is not found, the ttl will be 0 per their docs
113-
# https://github.com/jazzband/django-redis?tab=readme-ov-file#get-ttl-time-to-live-from-key
114-
redis_cache = caches["redis"]
115-
if redis_cache.ttl("CMS_homepage_featured_courses") > (10 * ONE_MINUTE):
116-
logger.info("Featured courses found in cache, moving on")
117-
return
118-
logger.info("No featured courses found in cache, refreshing")
119110
create_featured_items()
120-
logger.info("New featured items created")
111+
logger.info("Featured items refreshed")

courses/constants.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
zip(ALL_ENROLL_CHANGE_STATUSES, ALL_ENROLL_CHANGE_STATUSES)
3131
)
3232

33-
HOMEPAGE_CACHE_AGE = 86400 # 24 hours
34-
3533
SYNCED_COURSE_RUN_FIELD_MSG = "This value is synced automatically with edX studio."
3634

3735
AVAILABILITY_ANYTIME = "anytime"

main/celery_utils.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ def is_due(self, last_run_at):
2424
if self._apply_offset is not None and retval.is_due:
2525
self._apply_offset = False
2626
self.run_every = self._run_every
27-
retval = super().is_due(last_run_at)
2827
return retval
2928

3029
def __reduce__(self):

main/settings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,8 +1046,8 @@
10461046

10471047
REFRESH_FEATURED_HOMEPAGE_ITEMS_FREQ = get_int(
10481048
name="REFRESH_FEATURED_HOMEPAGE_ITEMS_FREQ",
1049-
default=60,
1050-
description="How many seconds between checking for featured items for the homepage in the local in memory cache",
1049+
default=86400,
1050+
description="How many seconds between refreshing featured items for the homepage cache",
10511051
)
10521052

10531053
REFRESH_FEATURED_HOMEPAGE_ITEMS_OFFSET = int(REFRESH_FEATURED_HOMEPAGE_ITEMS_FREQ / 2)

0 commit comments

Comments
 (0)