Commit 4e22affb by Toby Lawrence

[PERF-344] Add versioning of cached course assets to allow graceful cache invalidation

When releasing the versioned assets work, we stumbled on a problem with old pickled
versions of the StaticContent objects residing in cache, which triggered a bug in the
code. Not wanting to blow away all cached items, we ended up having to revert and add
in some backwards-compatible helper code to ease the transition.

With this, we'll now utilize the version argument that Django's caching interface
allows, in conjunction with a constant value that can be modified when breaking changes
are being made, to let us gracefully ignore older cached course assets.
parent d328328f
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)
"""
Serves course assets to end users.
"""
CONTENTSERVER_VERSION = 1
"""
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 (
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment