From 0df1bed21b4996f51796a4e697b201c0f1c0dfc5 Mon Sep 17 00:00:00 2001 From: Kelvin Muchiri Date: Fri, 8 Nov 2024 18:52:21 +0300 Subject: [PATCH] refactor to use safe cache operations --- onadata/libs/renderers/renderers.py | 22 +++++++++++++--------- onadata/libs/utils/cache_tools.py | 27 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/onadata/libs/renderers/renderers.py b/onadata/libs/renderers/renderers.py index f371783dd7..90bed1cbb2 100644 --- a/onadata/libs/renderers/renderers.py +++ b/onadata/libs/renderers/renderers.py @@ -2,19 +2,18 @@ """ Custom renderers for use with django rest_framework. """ + import decimal import json import math from io import BytesIO, StringIO from typing import Tuple -from django.core.cache import cache from django.utils import timezone from django.utils.dateparse import parse_datetime from django.utils.encoding import force_str, smart_str from django.utils.xmlutils import SimplerXMLGenerator - import six from rest_framework import negotiation from rest_framework.renderers import ( @@ -28,12 +27,15 @@ from six import iteritems from onadata.libs.utils.cache_tools import ( - XFORM_MANIFEST_CACHE_TTL, XFORM_MANIFEST_CACHE_LOCK_TTL, + XFORM_MANIFEST_CACHE_TTL, + safe_cache_add, + safe_cache_get, + safe_cache_set, + safe_delete, ) from onadata.libs.utils.osm import get_combined_osm - IGNORE_FIELDS = [ "formhub/uuid", "meta/contactID", @@ -395,18 +397,20 @@ def _get_current_buffer_data(self): if data and self.can_update_cache: data = data.strip() - cached_manifest: str | None = cache.get(self.cache_key) + cached_manifest: str | None = safe_cache_get(self.cache_key) if cached_manifest is not None: cached_manifest += data - cache.set(self.cache_key, cached_manifest, XFORM_MANIFEST_CACHE_TTL) + safe_cache_set( + self.cache_key, cached_manifest, XFORM_MANIFEST_CACHE_TTL + ) if data.endswith(""): # We are done, release the lock - cache.delete(self.cache_lock_key) + safe_delete(self.cache_lock_key) else: - cache.set(self.cache_key, data, XFORM_MANIFEST_CACHE_TTL) + safe_cache_set(self.cache_key, data, XFORM_MANIFEST_CACHE_TTL) return data @@ -415,7 +419,7 @@ def stream_data(self, data, serializer): # In the case of concurrent requests, we ensure only the first # request is updating the cache self.cache_lock_key = f"{self.cache_key}_lock" - self.can_update_cache = cache.add( + self.can_update_cache = safe_cache_add( self.cache_lock_key, "true", XFORM_MANIFEST_CACHE_LOCK_TTL ) diff --git a/onadata/libs/utils/cache_tools.py b/onadata/libs/utils/cache_tools.py index 8b9a67e046..6ed6a27201 100644 --- a/onadata/libs/utils/cache_tools.py +++ b/onadata/libs/utils/cache_tools.py @@ -165,6 +165,33 @@ def safe_cache_get(key, default=None): return default +def safe_cache_add(key, value, timeout=None): + """ + Safely add a value to the cache. + + If the cache is not reachable, the operation silently fails. + + Args: + key (str): The cache key to add. + value (Any): The value to store in the cache. + timeout (int, optional): The cache timeout in seconds. If None, + the default cache timeout will be used. + Returns: + bool: True if the value was added to the cache, False otherwise. + """ + try: + return cache.add(key, value, timeout) + except ConnectionError as exc: + # Handle cache connection error + logger.exception(exc) + return False + except socket.error as exc: + # Handle other potential connection errors, especially for + # older Python versions + logger.exception(exc) + return False + + class CacheLockError(Exception): """Custom exception raised when a cache lock cannot be acquired."""