[FEAT]: Add mem caching to the API to create and retrieve IntegritySignature (#27922)

With this change, whenever a IntegritySignature is created or retrieved, the result will be cached into mem cache for future use. This will save round trip to database significantly for honor code check
This commit is contained in:
Simon Chen
2021-06-10 14:45:06 -04:00
committed by GitHub
parent 14ec30e018
commit 6d380fc5a2
3 changed files with 60 additions and 7 deletions

View File

@@ -5,9 +5,11 @@ Agreements API
import logging
from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.agreements.cache import get_integrity_signature_cache_key
from openedx.core.djangoapps.agreements.models import IntegritySignature
log = logging.getLogger(__name__)
@@ -33,6 +35,9 @@ def create_integrity_signature(username, course_id):
'Integrity signature already exists for user_id={user_id} and '
'course_id={course_id}'.format(user_id=user.id, course_id=course_id)
)
cache_key = get_integrity_signature_cache_key(username, course_id)
# Write into the cache for future retrieval
cache.set(cache_key, signature)
return signature
@@ -48,10 +53,17 @@ def get_integrity_signature(username, course_id):
* An IntegritySignature object, or None if one does not exist for the
user + course combination.
"""
cache_key = get_integrity_signature_cache_key(username, course_id)
cached_integrity_signature = cache.get(cache_key)
if cached_integrity_signature:
return cached_integrity_signature
user = User.objects.get(username=username)
course_key = CourseKey.from_string(course_id)
try:
return IntegritySignature.objects.get(user=user, course_key=course_key)
signature = IntegritySignature.objects.get(user=user, course_key=course_key)
cache.set(cache_key, signature)
return signature
except ObjectDoesNotExist:
return None
@@ -66,5 +78,12 @@ def get_integrity_signatures_for_course(course_id):
Returns:
* QuerySet of IntegritySignature objects (can be empty).
"""
course_key = CourseKey.from_string(course_id)
return IntegritySignature.objects.filter(course_key=course_key)
course_integrity_signature = IntegritySignature.objects.filter(
course_key=course_key
).select_related('user')
for signature in course_integrity_signature:
cache_key = get_integrity_signature_cache_key(signature.user.username, course_id)
cache.set(cache_key, signature)
return course_integrity_signature

View File

@@ -0,0 +1,13 @@
# lint-amnesty, pylint: disable=missing-module-docstring
# Template used to create cache keys for Integrity Signatures
INTEGRITY_SIGNATURE_CACHE_KEY_TPL = 'integrity-signature-{course_id}-{username}'
def get_integrity_signature_cache_key(username, course_id):
"""
Util function to help form the cache key for integrity signature
"""
return INTEGRITY_SIGNATURE_CACHE_KEY_TPL.format(
username=username,
course_id=course_id,
)

View File

@@ -3,13 +3,14 @@ Tests for the Agreements API
"""
import logging
from django.core.cache import cache
from testfixtures import LogCapture
from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangoapps.agreements.api import (
create_integrity_signature,
get_integrity_signature,
get_integrity_signatures_for_course,
get_integrity_signatures_for_course
)
from openedx.core.djangolib.testing.utils import skip_unless_lms
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
@@ -62,15 +63,17 @@ class TestIntegritySignatureApi(SharedModuleStoreTestCase):
Test to get an integrity signature
"""
create_integrity_signature(self.user.username, self.course_id)
signature = get_integrity_signature(self.user.username, self.course_id)
self._assert_integrity_signature(signature)
with self.assertNumQueries(0):
signature = get_integrity_signature(self.user.username, self.course_id)
self._assert_integrity_signature(signature)
def test_get_nonexistent_integrity_signature(self):
"""
Test that None is returned if an integrity signature does not exist
"""
signature = get_integrity_signature(self.user.username, self.course_id)
self.assertIsNone(signature)
with self.assertNumQueries(2):
signature = get_integrity_signature(self.user.username, self.course_id)
self.assertIsNone(signature)
def test_get_integrity_signatures_for_course(self):
"""
@@ -98,3 +101,21 @@ class TestIntegritySignatureApi(SharedModuleStoreTestCase):
"""
self.assertEqual(signature.user, self.user)
self.assertEqual(signature.course_key, self.course.id)
def test_get_integrity_signatures_for_course_cached(self):
"""
Test to ensure the integrity_signatures retrieved by course is also set into cache
"""
create_integrity_signature(self.user.username, self.course_id)
second_user = UserFactory()
create_integrity_signature(second_user.username, self.course_id)
cache.clear()
with self.assertNumQueries(1):
get_integrity_signatures_for_course(self.course_id)
with self.assertNumQueries(0):
signature = get_integrity_signature(self.user.username, self.course_id)
self._assert_integrity_signature(signature)
with self.assertNumQueries(0):
get_integrity_signature(second_user.username, self.course_id)