Merge pull request #12911 from edx/PERF-344
[PERF-344] Add versioning of cached course assets to allow graceful cache invalidation
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
from cache_toolbox.core import get_cached_content, set_cached_content, del_cached_content
|
||||
"""
|
||||
Tests core caching facilities.
|
||||
"""
|
||||
from contentserver.caching import get_cached_content, set_cached_content, del_cached_content
|
||||
from opaque_keys.edx.locations import Location
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from django.views.decorators.http import require_http_methods, require_POST
|
||||
from django.conf import settings
|
||||
|
||||
from edxmako.shortcuts import render_to_response
|
||||
from cache_toolbox.core import del_cached_content
|
||||
from contentserver.caching import del_cached_content
|
||||
|
||||
from contentstore.utils import reverse_course_url
|
||||
from xmodule.contentstore.django import contentstore
|
||||
|
||||
@@ -13,7 +13,7 @@ from django.test.utils import override_settings
|
||||
from django.conf import settings
|
||||
|
||||
from contentstore.tests.utils import CourseTestCase, mock_requests_get
|
||||
from cache_toolbox.core import del_cached_content
|
||||
from contentserver.caching import del_cached_content
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.contentstore.django import contentstore
|
||||
from xmodule.contentstore.content import StaticContent
|
||||
|
||||
@@ -107,30 +107,3 @@ def instance_key(model, instance_or_pk):
|
||||
model._meta.model_name,
|
||||
getattr(instance_or_pk, 'pk', instance_or_pk),
|
||||
)
|
||||
|
||||
|
||||
def set_cached_content(content):
|
||||
cache.set(unicode(content.location).encode("utf-8"), content)
|
||||
|
||||
|
||||
def get_cached_content(location):
|
||||
return cache.get(unicode(location).encode("utf-8"))
|
||||
|
||||
|
||||
def del_cached_content(location):
|
||||
"""
|
||||
delete content for the given location, as well as for content with run=None.
|
||||
it's possible that the content could have been cached without knowing the
|
||||
course_key - and so without having the run.
|
||||
"""
|
||||
def location_str(loc):
|
||||
return unicode(loc).encode("utf-8")
|
||||
|
||||
locations = [location_str(location)]
|
||||
try:
|
||||
locations.append(location_str(location.replace(run=None)))
|
||||
except InvalidKeyError:
|
||||
# although deprecated keys allowed run=None, new keys don't if there is no version.
|
||||
pass
|
||||
|
||||
cache.delete_many(locations)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
"""
|
||||
Serves course assets to end users.
|
||||
"""
|
||||
|
||||
CONTENTSERVER_VERSION = 1
|
||||
|
||||
49
common/djangoapps/contentserver/caching.py
Normal file
49
common/djangoapps/contentserver/caching.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""
|
||||
Helper functions for caching course assets.
|
||||
"""
|
||||
from django.core.cache import caches
|
||||
from django.core.cache.backends.base import InvalidCacheBackendError
|
||||
from opaque_keys import InvalidKeyError
|
||||
from . import CONTENTSERVER_VERSION
|
||||
|
||||
# See if there's a "course_assets" cache configured, and if not, fallback to the default cache.
|
||||
CONTENT_CACHE = caches['default']
|
||||
try:
|
||||
CONTENT_CACHE = caches['course_assets']
|
||||
except InvalidCacheBackendError:
|
||||
pass
|
||||
|
||||
|
||||
def set_cached_content(content):
|
||||
"""
|
||||
Stores the given piece of content in the cache, using its location as the key.
|
||||
"""
|
||||
CONTENT_CACHE.set(unicode(content.location).encode("utf-8"), content, version=CONTENTSERVER_VERSION)
|
||||
|
||||
|
||||
def get_cached_content(location):
|
||||
"""
|
||||
Retrieves the given piece of content by its location if cached.
|
||||
"""
|
||||
return CONTENT_CACHE.get(unicode(location).encode("utf-8"), version=CONTENTSERVER_VERSION)
|
||||
|
||||
|
||||
def del_cached_content(location):
|
||||
"""
|
||||
Delete content for the given location, as well versions of the content without a run.
|
||||
|
||||
It's possible that the content could have been cached without knowing the course_key,
|
||||
and so without having the run.
|
||||
"""
|
||||
def location_str(loc):
|
||||
"""Force the location to a Unicode string."""
|
||||
return unicode(loc).encode("utf-8")
|
||||
|
||||
locations = [location_str(location)]
|
||||
try:
|
||||
locations.append(location_str(location.replace(run=None)))
|
||||
except InvalidKeyError:
|
||||
# although deprecated keys allowed run=None, new keys don't if there is no version.
|
||||
pass
|
||||
|
||||
CONTENT_CACHE.delete_many(locations, version=CONTENTSERVER_VERSION)
|
||||
@@ -17,7 +17,7 @@ from xmodule.contentstore.content import StaticContent, XASSET_LOCATION_TAG
|
||||
from xmodule.modulestore import InvalidLocationError
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.locator import AssetLocator
|
||||
from cache_toolbox.core import get_cached_content, set_cached_content
|
||||
from .caching import get_cached_content, set_cached_content
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from xmodule.exceptions import NotFoundError
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.x_module import STUDENT_VIEW
|
||||
from . import BaseTestXmodule
|
||||
from .test_video_xml import SOURCE_XML
|
||||
from cache_toolbox.core import del_cached_content
|
||||
from contentserver.caching import del_cached_content
|
||||
from xmodule.exceptions import NotFoundError
|
||||
|
||||
from xmodule.video_module.transcripts_utils import (
|
||||
|
||||
Reference in New Issue
Block a user