perf: reduce one redis call for redis_cache

This commit is contained in:
Ankush Menat 2024-12-17 16:00:20 +05:30
parent 30ec033747
commit b5218096e1
2 changed files with 32 additions and 2 deletions

View file

@ -32,6 +32,7 @@ from frappe.tests import IntegrationTestCase
from frappe.tests.test_api import FrappeAPITestCase
from frappe.tests.test_query_builder import run_only_if
from frappe.utils import cint
from frappe.utils.caching import redis_cache
from frappe.website.path_resolver import PathResolver
TEST_USER = "test@example.com"
@ -201,9 +202,26 @@ class TestPerformance(IntegrationTestCase):
def test_get_doc_cache_calls(self):
frappe.get_doc("User", "Administrator")
with self.assertRedisCallCounts(1):
with self.assertRedisCallCounts(0):
frappe.get_doc("User", "Administrator")
def test_local_caching(self):
frappe.get_cached_doc("User", "Administrator")
with self.assertRedisCallCounts(0):
frappe.get_cached_doc("User", "Administrator")
def test_redis_cache_calls(self):
redis_cached_func() # warmup
# Repeat call should use locally cached value
with self.assertRedisCallCounts(0):
redis_cached_func()
frappe.local.cache.clear()
# Without local cache - only one call required
with self.assertRedisCallCounts(1):
redis_cached_func()
def test_one_time_setup(self):
site = frappe.local.site
frappe.init(site, force=True)
@ -245,3 +263,8 @@ class TestOverheadCalls(FrappeAPITestCase):
self.get(self.resource("User", "Administrator"), {"sid": sid})
with self.assertRedisCallCounts(19), self.assertQueryCount(self.BASE_SQL_CALLS + 1 + tables):
self.get(self.resource("User", "Administrator"), {"sid": sid})
@redis_cache
def redis_cached_func():
return 42

View file

@ -164,8 +164,15 @@ def redis_cache(ttl: int | None = 3600, user: str | bool | None = None, shared:
@wraps(func)
def redis_cache_wrapper(*args, **kwargs):
func_call_key = func_key + "::" + str(__generate_request_cache_key(args, kwargs))
cached_val = frappe.cache.get_value(func_call_key, user=user, shared=shared)
if cached_val is not None:
return cached_val
# Edge Case: None can mean two things: cache miss or the result itself is `None`
# RedisWrapper doesn't give us any way to handle this cleanly.
if frappe.cache.exists(func_call_key, user=user, shared=shared):
return frappe.cache.get_value(func_call_key, user=user, shared=shared)
return None
val = func(*args, **kwargs)
ttl = getattr(func, "ttl", 3600)
frappe.cache.set_value(func_call_key, val, expires_in_sec=ttl, user=user, shared=shared)