test: ttl and maxsize
This commit is contained in:
parent
55ae5615d0
commit
53f085e0f4
3 changed files with 33 additions and 9 deletions
|
|
@ -129,7 +129,7 @@ class IntegrationTestCase(UnitTestCase):
|
||||||
frappe.db.__class__.sql = orig_sql
|
frappe.db.__class__.sql = orig_sql
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def assertRedisCallCounts(self, count: int) -> AbstractContextManager[None]:
|
def assertRedisCallCounts(self, count: int, *, exact=False) -> AbstractContextManager[None]:
|
||||||
from frappe.utils.redis_wrapper import RedisWrapper
|
from frappe.utils.redis_wrapper import RedisWrapper
|
||||||
|
|
||||||
commands = []
|
commands = []
|
||||||
|
|
@ -146,9 +146,11 @@ class IntegrationTestCase(UnitTestCase):
|
||||||
orig_execute = RedisWrapper.execute_command
|
orig_execute = RedisWrapper.execute_command
|
||||||
RedisWrapper.execute_command = execute_command_and_count
|
RedisWrapper.execute_command = execute_command_and_count
|
||||||
yield
|
yield
|
||||||
self.assertLessEqual(
|
msg = "commands executed: \n" + "\n".join(str(c) for c in commands)
|
||||||
len(commands), count, 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:
|
finally:
|
||||||
RedisWrapper.execute_command = orig_execute
|
RedisWrapper.execute_command = orig_execute
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import time
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.tests import IntegrationTestCase
|
from frappe.tests import IntegrationTestCase
|
||||||
|
from frappe.utils.redis_wrapper import _ClientCache
|
||||||
|
|
||||||
TEST_KEY = "42"
|
TEST_KEY = "42"
|
||||||
|
|
||||||
|
|
@ -35,5 +36,23 @@ class TestClientCache(IntegrationTestCase):
|
||||||
# current thread. So we wait.
|
# current thread. So we wait.
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
with self.assertRedisCallCounts(1):
|
with self.assertRedisCallCounts(1, exact=True):
|
||||||
self.assertEqual(frappe.client_cache.get_value(TEST_KEY), val)
|
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)
|
||||||
|
|
|
||||||
|
|
@ -450,16 +450,14 @@ class _ClientCache:
|
||||||
if val is None:
|
if val is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if len(self.local_cache) >= self.maxsize:
|
self.ensure_max_size()
|
||||||
with suppress(RuntimeError):
|
|
||||||
self.local_cache.pop(next(iter(self.local_cache)), None)
|
|
||||||
|
|
||||||
self.local_cache[key] = (val, time.monotonic() + self.local_ttl)
|
self.local_cache[key] = (val, time.monotonic() + self.local_ttl)
|
||||||
|
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def set_value(self, key, val):
|
def set_value(self, key, val):
|
||||||
key = self.redis.make_key(key)
|
key = self.redis.make_key(key)
|
||||||
|
self.ensure_max_size()
|
||||||
self.redis.set_value(key, val, shared=True)
|
self.redis.set_value(key, val, shared=True)
|
||||||
self.local_cache[key] = (val, time.monotonic() + self.local_ttl)
|
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
|
# 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.
|
# doesn't send invalidation.
|
||||||
_ = self.redis.get_value(key, shared=True, use_local_cache=False)
|
_ = 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):
|
def delete_value(self, key):
|
||||||
key = self.redis.make_key(key)
|
key = self.redis.make_key(key)
|
||||||
self.redis.delete_value(key, shared=True)
|
self.redis.delete_value(key, shared=True)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue