cache: Do not fail the request on cache-set failures.

Failure to update the cache should log an exception but continue.
This commit is contained in:
Alex Vandiver
2025-03-05 16:36:45 +00:00
committed by Tim Abbott
parent c4701fa8d5
commit 34f0e3b621
2 changed files with 40 additions and 2 deletions

View File

@@ -12,6 +12,7 @@ from functools import _lru_cache_wrapper, lru_cache, wraps
from itertools import islice, product
from typing import TYPE_CHECKING, Any, Generic, TypeVar
from bmemcached.exceptions import MemcachedException
from django.conf import settings
from django.core.cache import caches
from django.core.cache.backends.base import BaseCache
@@ -226,7 +227,10 @@ def cache_set(
remote_cache_stats_start()
cache_backend = get_cache_backend(cache_name)
cache_backend.set(final_key, (val,), timeout=timeout)
try:
cache_backend.set(final_key, (val,), timeout=timeout)
except MemcachedException as e:
logger.exception(e)
remote_cache_stats_finish()
@@ -278,7 +282,10 @@ def cache_set_many(
new_items[new_key] = item
items = new_items
remote_cache_stats_start()
get_cache_backend(cache_name).set_many(items, timeout=timeout)
try:
get_cache_backend(cache_name).set_many(items, timeout=timeout)
except MemcachedException as e:
logger.exception(e)
remote_cache_stats_finish()

View File

@@ -1,5 +1,6 @@
from unittest.mock import Mock, patch
from bmemcached.exceptions import MemcachedException
from django.conf import settings
from zerver.apps import flush_cache
@@ -158,6 +159,36 @@ class CacheWithKeyDecoratorTest(ZulipTestCase):
self.assertEqual(result_two, None)
class SetCacheExceptionTest(ZulipTestCase):
def test_set_cache_exception(self) -> None:
with (
patch("zerver.lib.cache.get_cache_backend") as mock_backend,
self.assertLogs("", level="INFO") as logs,
):
mock_backend.return_value.set.side_effect = MemcachedException(
b"Out of memory during read", 130
)
cache_set("test-key", 1)
mock_backend.assert_called_once()
mock_backend.return_value.set.assert_called_once()
self.assert_length(logs.output, 1)
self.assertIn("Out of memory during read", logs.output[0])
def test_set_many_cache_exception(self) -> None:
with (
patch("zerver.lib.cache.get_cache_backend") as mock_backend,
self.assertLogs("", level="INFO") as logs,
):
mock_backend.return_value.set_many.side_effect = MemcachedException(
b"Out of memory during read", 130
)
cache_set_many({"test-key": 1, "other-key": 2})
mock_backend.assert_called_once()
mock_backend.return_value.set_many.assert_called_once()
self.assert_length(logs.output, 1)
self.assertIn("Out of memory during read", logs.output[0])
class SafeCacheFunctionsTest(ZulipTestCase):
def test_safe_cache_functions_with_all_good_keys(self) -> None:
items = {