From 8c3254fd7e86c84450aa9865fc21d6b16ce63267 Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Tue, 13 Feb 2024 11:00:16 -0800 Subject: [PATCH 1/2] Ensure that Kolibri can start regardless of whether redis and redis cache is installed. --- kolibri/core/utils/cache.py | 28 +++++++++++++++++----------- kolibri/utils/options.py | 27 +++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/kolibri/core/utils/cache.py b/kolibri/core/utils/cache.py index e0c9b8bd2f3..ce719c50a64 100644 --- a/kolibri/core/utils/cache.py +++ b/kolibri/core/utils/cache.py @@ -3,7 +3,6 @@ from django.core.cache import caches from django.core.cache import InvalidCacheBackendError from django.utils.functional import SimpleLazyObject -from redis_cache import RedisCache as BaseRedisCache logger = logging.getLogger(__name__) @@ -64,13 +63,20 @@ def save(self): self.changed = False -class RedisCache(BaseRedisCache): - def set(self, *args, **kwargs): - """ - Overwrite the set method to not return a value, in line with the Django cache interface - This causes particular issues for Django's caching middleware, which expects the set method to return None - as it invokes it directly in a lambda in the response.add_post_render_callback method - We use a similar pattern in our own caching decorator in kolibri/core/content/api.py and saw errors - due to the fact if the lambda returns a value, it is interpreted as a replacement for the response object. - """ - super(RedisCache, self).set(*args, **kwargs) +try: + from redis_cache import RedisCache as BaseRedisCache + + class RedisCache(BaseRedisCache): + def set(self, *args, **kwargs): + """ + Overwrite the set method to not return a value, in line with the Django cache interface + This causes particular issues for Django's caching middleware, which expects the set method to return None + as it invokes it directly in a lambda in the response.add_post_render_callback method + We use a similar pattern in our own caching decorator in kolibri/core/content/api.py and saw errors + due to the fact if the lambda returns a value, it is interpreted as a replacement for the response object. + """ + super(RedisCache, self).set(*args, **kwargs) + + +except ImportError: + pass diff --git a/kolibri/utils/options.py b/kolibri/utils/options.py index b990691955d..8f3f620e9db 100644 --- a/kolibri/utils/options.py +++ b/kolibri/utils/options.py @@ -19,6 +19,7 @@ from six.moves.urllib.parse import urlparse from six.moves.urllib.parse import urlunparse from validate import is_boolean +from validate import is_option from validate import Validator from validate import VdtTypeError from validate import VdtValueError @@ -271,6 +272,28 @@ def multiprocess_bool(value): return False +def cache_option(value): + """ + Validate the cache options. + Do this by checking it's an allowed option, and also that redis cache + can be imported properly on this platform. + """ + value = is_option(value, "memory", "redis") + try: + if value == "redis": + # Check that we can properly import our RedisCache + # implementation, to ensure that we can use it. + # Also ensure that the redis package is installed. + from kolibri.core.utils.cache import RedisCache # noqa + import redis # noqa + return value + except ImportError: + logger.error( + "Redis cache backend is not available, are Redis packages installed?" + ) + raise VdtValueError(value) + + class LazyImportFunction(object): """ A function wrapper that will import a module when called. @@ -339,8 +362,7 @@ def lazy_import_callback_list(value): base_option_spec = { "Cache": { "CACHE_BACKEND": { - "type": "option", - "options": ("memory", "redis"), + "type": "cache_option", "default": "memory", "description": """ Which backend to use for the main cache - if 'memory' is selected, then for most cache operations, @@ -716,6 +738,7 @@ def _get_validator(): "url_prefix": url_prefix, "bytes": validate_bytes, "multiprocess_bool": multiprocess_bool, + "cache_option": cache_option, "lazy_import_callback_list": lazy_import_callback_list, } ) From 840b41600ef50a83039bf7474e4974670e0625cf Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Tue, 13 Feb 2024 11:32:46 -0800 Subject: [PATCH 2/2] Catch error raised by Django when redis is not installed. --- kolibri/core/utils/cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kolibri/core/utils/cache.py b/kolibri/core/utils/cache.py index ce719c50a64..8595f7fef4c 100644 --- a/kolibri/core/utils/cache.py +++ b/kolibri/core/utils/cache.py @@ -78,5 +78,5 @@ def set(self, *args, **kwargs): super(RedisCache, self).set(*args, **kwargs) -except ImportError: +except (ImportError, InvalidCacheBackendError): pass