test: ttl and maxsize

This commit is contained in:
Ankush Menat 2025-01-06 17:57:28 +05:30
parent 55ae5615d0
commit 53f085e0f4
3 changed files with 33 additions and 9 deletions

View file

@ -129,7 +129,7 @@ class IntegrationTestCase(UnitTestCase):
frappe.db.__class__.sql = orig_sql
@contextmanager
def assertRedisCallCounts(self, count: int) -> AbstractContextManager[None]:
def assertRedisCallCounts(self, count: int, *, exact=False) -> AbstractContextManager[None]:
from frappe.utils.redis_wrapper import RedisWrapper
commands = []
@ -146,9 +146,11 @@ class IntegrationTestCase(UnitTestCase):
orig_execute = RedisWrapper.execute_command
RedisWrapper.execute_command = execute_command_and_count
yield
self.assertLessEqual(
len(commands), count, msg="commands executed: \n" + "\n".join(str(c) for c in commands)
)
msg = "commands executed: \n" + "\n".join(str(c) for c in commands)
if exact:
self.assertEqual(len(commands), count, msg=msg)
else:
self.assertLessEqual(len(commands), count, msg=msg)
finally:
RedisWrapper.execute_command = orig_execute

View file

@ -2,6 +2,7 @@ import time
import frappe
from frappe.tests import IntegrationTestCase
from frappe.utils.redis_wrapper import _ClientCache
TEST_KEY = "42"
@ -35,5 +36,23 @@ class TestClientCache(IntegrationTestCase):
# current thread. So we wait.
time.sleep(0.1)
with self.assertRedisCallCounts(1):
with self.assertRedisCallCounts(1, exact=True):
self.assertEqual(frappe.client_cache.get_value(TEST_KEY), val)
def test_client_local_cache_ttl(self):
c = _ClientCache(ttl=1)
c.set_value(TEST_KEY, 42)
with self.assertRedisCallCounts(0):
c.get_value(TEST_KEY)
time.sleep(1)
with self.assertRedisCallCounts(1, exact=True):
c.get_value(TEST_KEY)
def test_client_cache_maxsize(self):
c = _ClientCache(maxsize=2)
c.set_value(TEST_KEY, 42)
c.set_value(frappe.generate_hash(), 42)
c.set_value(frappe.generate_hash(), 42)
self.assertEqual(len(c.local_cache), 2)

View file

@ -450,16 +450,14 @@ class _ClientCache:
if val is None:
return None
if len(self.local_cache) >= self.maxsize:
with suppress(RuntimeError):
self.local_cache.pop(next(iter(self.local_cache)), None)
self.ensure_max_size()
self.local_cache[key] = (val, time.monotonic() + self.local_ttl)
return val
def set_value(self, key, val):
key = self.redis.make_key(key)
self.ensure_max_size()
self.redis.set_value(key, val, shared=True)
self.local_cache[key] = (val, time.monotonic() + self.local_ttl)
# XXX: We need to tell redis that we indeed read this key we just wrote
@ -469,6 +467,11 @@ class _ClientCache:
# doesn't send invalidation.
_ = self.redis.get_value(key, shared=True, use_local_cache=False)
def ensure_max_size(self):
if len(self.local_cache) >= self.maxsize:
with suppress(RuntimeError):
self.local_cache.pop(next(iter(self.local_cache)), None)
def delete_value(self, key):
key = self.redis.make_key(key)
self.redis.delete_value(key, shared=True)