Skip to content

Commit f0d956a

Browse files
authored
perf: added caching to XBlockSkillsViewSet list endpoint (#208)
* perf: added caching to XBlockSkillsViewSet list endpoint * test: add tests for XBlockSkillsViewSet caching behavior
1 parent a79beea commit f0d956a

File tree

5 files changed

+48
-1
lines changed

5 files changed

+48
-1
lines changed

CHANGELOG.rst

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ Change Log
1313
1414
Unreleased
1515

16+
[1.54.0] - 2024-10-02
17+
---------------------
18+
* perf: Added caching to `XBlockSkillsViewSet` list endpoint to improve performance and reduce redundant database queries
19+
1620
[1.53.0] - 2024-08-22
1721
---------------------
1822
* perf: Introduced db_index on the `created` and `is_blacklisted` fields in `XBlockSkillData` model

taxonomy/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
# 2. MINOR version when you add functionality in a backwards compatible manner, and
1616
# 3. PATCH version when you make backwards compatible bug fixes.
1717
# More details can be found at https://semver.org/
18-
__version__ = '1.53.0'
18+
__version__ = '1.54.0'
1919

2020
default_app_config = 'taxonomy.apps.TaxonomyConfig' # pylint: disable=invalid-name

taxonomy/api/v1/views.py

+15
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from collections import OrderedDict
55

66
from django_filters.rest_framework import DjangoFilterBackend
7+
from edx_django_utils.cache import TieredCache, get_cache_key
78
from rest_framework import permissions
89
from rest_framework.filters import OrderingFilter
910
from rest_framework.generics import ListAPIView, RetrieveAPIView
@@ -27,6 +28,7 @@
2728
SkillsQuizSerializer,
2829
XBlocksSkillsSerializer,
2930
)
31+
from taxonomy.constants import CACHE_TIMEOUT_XBLOCK_SKILLS_SECONDS
3032
from taxonomy.models import (
3133
CourseSkills,
3234
Job,
@@ -320,6 +322,19 @@ def get_queryset(self):
320322
),
321323
).only('id', 'skills', 'usage_key', 'requires_verification', 'auto_processed', 'hash_content')
322324

325+
def list(self, request, *args, **kwargs):
326+
cache_key = get_cache_key(domain='taxonomy', subdomain='xblock_skills', params=request.query_params)
327+
328+
cached_response = TieredCache.get_cached_response(cache_key)
329+
if cached_response.is_found:
330+
return Response(cached_response.value)
331+
332+
response = super().list(request, *args, **kwargs)
333+
334+
TieredCache.set_all_tiers(cache_key, response.data, CACHE_TIMEOUT_XBLOCK_SKILLS_SECONDS)
335+
336+
return response
337+
323338

324339
class JobPathAPIView(TaxonomyAPIViewSetMixin, RetrieveAPIView):
325340
"""

taxonomy/constants.py

+2
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,5 @@ def get_job_posting_query_filter(jobs=None):
146146
JOB_SOURCE_COURSE_SKILL = 'course_skill'
147147
JOB_SOURCE_INDUSTRY = 'industry'
148148
JOB_SKILLS_URL_NAME = 'job-skills'
149+
150+
CACHE_TIMEOUT_XBLOCK_SKILLS_SECONDS = 60 * 60

tests/test_views.py

+26
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from random import randint
77

88
import mock
9+
from edx_django_utils.cache import TieredCache
910
from mock import patch
1011
from pytest import mark
1112
from rest_framework import status
@@ -626,6 +627,31 @@ def test_xblocks_api_with_skill_validation_disabled(self):
626627
assert api_response.status_code == 200
627628
assert api_response.json() == []
628629

630+
def test_list_xblock_skills_cache_hit(self):
631+
cached_data = {'results': [{'id': str(self.xblock_skills[0].id)}]}
632+
with patch.object(
633+
TieredCache,
634+
'get_cached_response',
635+
wraps=TieredCache.get_cached_response
636+
) as mock_get_cached_response:
637+
mock_get_cached_response.return_value = mock.MagicMock(is_found=True, value=cached_data)
638+
response = self.client.get(self.view_url)
639+
self.assertEqual(response.status_code, status.HTTP_200_OK)
640+
self.assertEqual(response.data, cached_data)
641+
mock_get_cached_response.assert_called_once()
642+
643+
def test_list_xblock_skills_caching(self):
644+
with patch.object(TieredCache, 'set_all_tiers') as mock_set_all_tiers, \
645+
patch.object(TieredCache, 'get_cached_response', wraps=TieredCache.get_cached_response) \
646+
as mock_get_cached_response:
647+
648+
mock_get_cached_response.return_value = mock.MagicMock(is_found=False)
649+
response = self.client.get(self.view_url)
650+
651+
self.assertEqual(response.status_code, status.HTTP_200_OK)
652+
mock_get_cached_response.assert_called_once()
653+
mock_set_all_tiers.assert_called_once()
654+
629655

630656
@mark.django_db
631657
class TestJobPathAPIView(TestCase):

0 commit comments

Comments
 (0)