From 3242a58f128e3b8f6bae29f82a0346109fccb091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=86=D0=B2=D0=B0=D0=BD=20=D0=9D=D1=94=D0=B4=D1=94=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D1=96=D1=86=D0=B5=D0=B2?= Date: Thu, 18 Apr 2024 10:09:35 +0300 Subject: [PATCH] fix: [AXIMST-806] returned cache clearing when publishing a course --- .../contentstore/signals/handlers.py | 2 ++ cms/djangoapps/contentstore/utils.py | 36 ++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/cms/djangoapps/contentstore/signals/handlers.py b/cms/djangoapps/contentstore/signals/handlers.py index e0bc9fcc9558..84a76fd77312 100644 --- a/cms/djangoapps/contentstore/signals/handlers.py +++ b/cms/djangoapps/contentstore/signals/handlers.py @@ -21,6 +21,7 @@ CoursewareSearchIndexer, LibrarySearchIndexer, ) +from cms.djangoapps.contentstore.utils import drop_course_sidebar_blocks_cache from common.djangoapps.track.event_transaction_utils import get_event_transaction_id, get_event_transaction_type from common.djangoapps.util.block_utils import yield_dynamic_block_descendants from lms.djangoapps.grades.api import task_compute_all_grades_for_course @@ -141,6 +142,7 @@ def listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable= # register special exams asynchronously after the data is ready course_key_str = str(course_key) transaction.on_commit(lambda: update_special_exams_and_publish.delay(course_key_str)) + drop_course_sidebar_blocks_cache(course_key_str) if key_supports_outlines(course_key): # Push the course outline to learning_sequences asynchronously. diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index d14944436a7e..880902c5db4b 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -8,10 +8,11 @@ from collections import defaultdict from contextlib import contextmanager from datetime import datetime, timezone -from urllib.parse import quote_plus +from urllib.parse import quote_plus, unquote from uuid import uuid4 from django.conf import settings +from django.core.cache import cache from django.core.exceptions import ValidationError from django.urls import reverse from django.utils import translation @@ -2287,3 +2288,36 @@ def get_xblock_render_context(request, block): return str(exc) return "" + + +def drop_course_sidebar_blocks_cache(course_id: str): + """ + Drop the course sidebar blocks cache for the given course. + """ + cache_key_prefix = f"course_sidebar_blocks_{course_id}" + cache_keys = get_cache_keys(cache_key_prefix) + + cache.delete_many(cache_keys) + + +def get_cache_keys(cache_key_prefix): + """ + Get all cache keys for the given cache key prefix. + LocMemCache does not have a keys method, so we need to iterate over the cache + and manually filter out the keys that match the given prefix. + """ + cache_backend = settings.CACHES['default']['BACKEND'] + if cache_backend == 'django_redis.cache.RedisCache': + yield cache.iter_keys(f"{cache_key_prefix}*") + elif cache_backend == 'django.core.cache.backends.locmem.LocMemCache': + for key in cache._cache.keys(): # pylint: disable=protected-access + try: + decoded_key = unquote(key.split(':', 2)[-1], encoding='utf-8') + except IndexError: + continue + + if decoded_key.startswith(cache_key_prefix): + yield decoded_key + else: + log.error(f"Unsupported cache backend: {cache_backend}") + yield