From 6b7e2bbabb31837abf4d56a1189394589e86c080 Mon Sep 17 00:00:00 2001 From: Simon Oliver Tveit Date: Thu, 26 Jan 2023 10:04:37 +0100 Subject: [PATCH 1/6] Access cache by function If cache with that name doesnt exist, then everything will crash when the module is imported if its defined directly --- python/nav/web/sortedstats/views.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/python/nav/web/sortedstats/views.py b/python/nav/web/sortedstats/views.py index 9f33c7fbc3..557b788178 100644 --- a/python/nav/web/sortedstats/views.py +++ b/python/nav/web/sortedstats/views.py @@ -33,7 +33,10 @@ GRAPHITE_TIME_FORMAT = "%H:%M_%Y%m%d" _logger = logging.getLogger(__name__) -cache = caches['sortedstats'] + + +def get_cache(): + return caches['sortedstats'] def index(request): @@ -78,7 +81,7 @@ def process_form(form): rows = form.cleaned_data['rows'] cache_key = get_cache_key(view, timeframe, rows) if form.cleaned_data['use_cache']: - result = cache.get(cache_key) + result = get_cache().get(cache_key) if result and not result.data: result = None if not result: @@ -106,7 +109,7 @@ def collect_result(view, timeframe, rows): cache_key = get_cache_key(view, timeframe, rows) result = get_result(view, start, end, rows) result.collect() - cache.set(cache_key, result, timeout=timeout) + get_cache().set(cache_key, result, timeout=timeout) return result From dcbe583cef9488c152c5cbd54b96e129f3165321 Mon Sep 17 00:00:00 2001 From: Simon Oliver Tveit Date: Thu, 26 Jan 2023 10:12:18 +0100 Subject: [PATCH 2/6] Do not set cache if cache cannot be accessed --- python/nav/web/sortedstats/views.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/python/nav/web/sortedstats/views.py b/python/nav/web/sortedstats/views.py index 557b788178..49bce4241c 100644 --- a/python/nav/web/sortedstats/views.py +++ b/python/nav/web/sortedstats/views.py @@ -26,6 +26,7 @@ from django.shortcuts import render from django.core.cache import caches from django.conf import settings +from django.core.cache.backends.base import InvalidCacheBackendError from .forms import ViewForm from . import CLASSMAP, TIMEFRAMES @@ -109,7 +110,11 @@ def collect_result(view, timeframe, rows): cache_key = get_cache_key(view, timeframe, rows) result = get_result(view, start, end, rows) result.collect() - get_cache().set(cache_key, result, timeout=timeout) + try: + cache = get_cache() + cache.set(cache_key, result, timeout=timeout) + except InvalidCacheBackendError as e: + _logger.error("Error accessing cache for ranked statistics: %s".format(e)) return result From 22cd9a57fc63b96c488fde50e4f4c87e9e1ff474 Mon Sep 17 00:00:00 2001 From: Simon Oliver Tveit Date: Thu, 26 Jan 2023 10:26:08 +0100 Subject: [PATCH 3/6] Collect results if cache cannot be accessed --- python/nav/web/sortedstats/views.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/python/nav/web/sortedstats/views.py b/python/nav/web/sortedstats/views.py index 49bce4241c..51a85f71a1 100644 --- a/python/nav/web/sortedstats/views.py +++ b/python/nav/web/sortedstats/views.py @@ -82,8 +82,13 @@ def process_form(form): rows = form.cleaned_data['rows'] cache_key = get_cache_key(view, timeframe, rows) if form.cleaned_data['use_cache']: - result = get_cache().get(cache_key) - if result and not result.data: + try: + cache = get_cache() + result = cache.get(cache_key) + if result and not result.data: + result = None + except InvalidCacheBackendError as e: + _logger.error("Error accessing cache for ranked statistics: %s".format(e)) result = None if not result: result = collect_result(view, timeframe, rows) From 6f861e87ac05fc7fa21719505240a6f3409156e7 Mon Sep 17 00:00:00 2001 From: Simon Oliver Tveit Date: Thu, 26 Jan 2023 11:16:19 +0100 Subject: [PATCH 4/6] Show error if ranked stats cache is misconfigured --- python/nav/web/sortedstats/views.py | 10 ++++++++++ python/nav/web/templates/sortedstats/sortedstats.html | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/python/nav/web/sortedstats/views.py b/python/nav/web/sortedstats/views.py index 51a85f71a1..4777e0c08c 100644 --- a/python/nav/web/sortedstats/views.py +++ b/python/nav/web/sortedstats/views.py @@ -40,6 +40,15 @@ def get_cache(): return caches['sortedstats'] +def cache_is_misconfigured(): + try: + get_cache() + except InvalidCacheBackendError: + return True + else: + return False + + def index(request): """Sorted stats search & result view""" result = None @@ -68,6 +77,7 @@ def index(request): 'graphite_unreachable': graphite_unreachable, 'from_cache': from_cache, 'duration': duration, + 'cache_misconfigured': cache_is_misconfigured(), } return render(request, 'sortedstats/sortedstats.html', context) diff --git a/python/nav/web/templates/sortedstats/sortedstats.html b/python/nav/web/templates/sortedstats/sortedstats.html index b769accf7e..eec944e04e 100644 --- a/python/nav/web/templates/sortedstats/sortedstats.html +++ b/python/nav/web/templates/sortedstats/sortedstats.html @@ -17,6 +17,11 @@ {% include 'nav_header.html' %} {% endwith %} + {% if cache_misconfigured %} +
+ The cache for Ranked Statistics is not configured correctly. +
+ {% endif %} {% crispy form %} From 3e80061fdb44ff65eb8a0d722133b1b21a81cde6 Mon Sep 17 00:00:00 2001 From: Simon Oliver Tveit Date: Thu, 26 Jan 2023 21:07:59 +0100 Subject: [PATCH 5/6] Update tests --- tests/unittests/web/sortedstats/sortedstats_test.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unittests/web/sortedstats/sortedstats_test.py b/tests/unittests/web/sortedstats/sortedstats_test.py index d74b68bc04..f3feef6379 100644 --- a/tests/unittests/web/sortedstats/sortedstats_test.py +++ b/tests/unittests/web/sortedstats/sortedstats_test.py @@ -26,10 +26,10 @@ def test_cache_key_is_correct(self): cache_key = views.get_cache_key(view, timeframe, rows) self.assertEqual(cache_key, expected_cache_key) - @patch('nav.web.sortedstats.views.cache') + @patch('nav.web.sortedstats.views.get_cache') def test_process_form_returns_cache_value_if_cache_exists(self, cache_mock): data = "cached" - cache_mock.get.return_value.data = data + cache_mock.return_value.get.return_value.data = data fake_form = MagicMock() fake_form.cleaned_data = { 'view': 'uptime', @@ -42,12 +42,12 @@ def test_process_form_returns_cache_value_if_cache_exists(self, cache_mock): self.assertEqual(result.data, data) @patch('nav.web.sortedstats.views.collect_result') - @patch('nav.web.sortedstats.views.cache') + @patch('nav.web.sortedstats.views.get_cache') def test_cache_not_used_if_empty_and_use_cache_is_on( self, cache_mock, collect_mock ): data = "new" - cache_mock.get.return_value.data = "" + cache_mock.return_value.get.return_value.data = "" collect_mock.return_value.data = data fake_form = MagicMock() fake_form.cleaned_data = { @@ -61,12 +61,12 @@ def test_cache_not_used_if_empty_and_use_cache_is_on( self.assertEqual(result.data, data) @patch('nav.web.sortedstats.views.collect_result') - @patch('nav.web.sortedstats.views.cache') + @patch('nav.web.sortedstats.views.get_cache') def test_cache_not_used_if_empty_and_use_cache_is_off( self, cache_mock, collect_mock ): data = "new" - cache_mock.get.return_value.data = "" + cache_mock.return_value.get.return_value.data = "" collect_mock.return_value.data = data fake_form = MagicMock() fake_form.cleaned_data = { From 199cb16dd8f2632915c0ae17072bfecf194b97f9 Mon Sep 17 00:00:00 2001 From: Simon Oliver Tveit Date: Wed, 22 Feb 2023 10:05:13 +0100 Subject: [PATCH 6/6] Use loggers own method for formatting log message --- python/nav/web/sortedstats/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/nav/web/sortedstats/views.py b/python/nav/web/sortedstats/views.py index 4777e0c08c..7dc0d91283 100644 --- a/python/nav/web/sortedstats/views.py +++ b/python/nav/web/sortedstats/views.py @@ -98,7 +98,7 @@ def process_form(form): if result and not result.data: result = None except InvalidCacheBackendError as e: - _logger.error("Error accessing cache for ranked statistics: %s".format(e)) + _logger.error("Error accessing cache for ranked statistics: %s", e) result = None if not result: result = collect_result(view, timeframe, rows) @@ -129,7 +129,7 @@ def collect_result(view, timeframe, rows): cache = get_cache() cache.set(cache_key, result, timeout=timeout) except InvalidCacheBackendError as e: - _logger.error("Error accessing cache for ranked statistics: %s".format(e)) + _logger.error("Error accessing cache for ranked statistics: %s", e) return result