2023-04-05 17:49:23 +00:00
|
|
|
from inspect import signature
|
2023-10-12 01:33:48 +00:00
|
|
|
from django.core.cache import cache, caches
|
2023-04-05 17:49:23 +00:00
|
|
|
from django.db.models.query import QuerySet
|
2023-10-06 08:54:37 +00:00
|
|
|
from django.core.handlers.wsgi import WSGIRequest
|
2020-01-21 06:35:58 +00:00
|
|
|
|
2023-04-05 17:49:23 +00:00
|
|
|
import hashlib
|
2020-01-21 06:35:58 +00:00
|
|
|
|
2024-05-08 15:15:55 +00:00
|
|
|
from judge.logging import log_debug
|
|
|
|
|
2023-10-12 01:33:48 +00:00
|
|
|
MAX_NUM_CHAR = 50
|
2023-08-29 23:36:01 +00:00
|
|
|
NONE_RESULT = "__None__"
|
2023-04-05 17:49:23 +00:00
|
|
|
|
|
|
|
|
2023-10-12 01:33:48 +00:00
|
|
|
def arg_to_str(arg):
|
|
|
|
if hasattr(arg, "id"):
|
|
|
|
return str(arg.id)
|
|
|
|
if isinstance(arg, list) or isinstance(arg, QuerySet):
|
|
|
|
return hashlib.sha1(str(list(arg)).encode()).hexdigest()[:MAX_NUM_CHAR]
|
|
|
|
if len(str(arg)) > MAX_NUM_CHAR:
|
|
|
|
return str(arg)[:MAX_NUM_CHAR]
|
|
|
|
return str(arg)
|
|
|
|
|
|
|
|
|
|
|
|
def filter_args(args_list):
|
|
|
|
return [x for x in args_list if not isinstance(x, WSGIRequest)]
|
|
|
|
|
|
|
|
|
|
|
|
l0_cache = caches["l0"] if "l0" in caches else None
|
2023-10-06 08:54:37 +00:00
|
|
|
|
2023-10-12 01:33:48 +00:00
|
|
|
|
2024-05-08 15:15:55 +00:00
|
|
|
def cache_wrapper(prefix, timeout=None, expected_type=None):
|
2023-04-05 17:49:23 +00:00
|
|
|
def get_key(func, *args, **kwargs):
|
|
|
|
args_list = list(args)
|
|
|
|
signature_args = list(signature(func).parameters.keys())
|
|
|
|
args_list += [kwargs.get(k) for k in signature_args[len(args) :]]
|
2023-10-06 08:54:37 +00:00
|
|
|
args_list = filter_args(args_list)
|
2023-04-05 17:49:23 +00:00
|
|
|
args_list = [arg_to_str(i) for i in args_list]
|
|
|
|
key = prefix + ":" + ":".join(args_list)
|
|
|
|
key = key.replace(" ", "_")
|
|
|
|
return key
|
|
|
|
|
2023-10-12 01:33:48 +00:00
|
|
|
def _get(key):
|
|
|
|
if not l0_cache:
|
|
|
|
return cache.get(key)
|
2023-11-09 08:43:11 +00:00
|
|
|
result = l0_cache.get(key)
|
|
|
|
if result is None:
|
|
|
|
result = cache.get(key)
|
|
|
|
return result
|
2023-10-12 01:33:48 +00:00
|
|
|
|
|
|
|
def _set_l0(key, value):
|
|
|
|
if l0_cache:
|
|
|
|
l0_cache.set(key, value, 30)
|
|
|
|
|
|
|
|
def _set(key, value, timeout):
|
|
|
|
_set_l0(key, value)
|
|
|
|
cache.set(key, value, timeout)
|
|
|
|
|
2023-04-05 17:49:23 +00:00
|
|
|
def decorator(func):
|
2024-05-08 15:15:55 +00:00
|
|
|
def _validate_type(cache_key, result):
|
|
|
|
if expected_type and not isinstance(result, expected_type):
|
|
|
|
data = {
|
|
|
|
"function": f"{func.__module__}.{func.__qualname__}",
|
|
|
|
"result": str(result)[:30],
|
|
|
|
"expected_type": expected_type,
|
|
|
|
"type": type(result),
|
|
|
|
"key": cache_key,
|
|
|
|
}
|
|
|
|
log_debug("invalid_key", data)
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2023-04-05 17:49:23 +00:00
|
|
|
def wrapper(*args, **kwargs):
|
|
|
|
cache_key = get_key(func, *args, **kwargs)
|
2023-10-12 01:33:48 +00:00
|
|
|
result = _get(cache_key)
|
2024-05-08 15:15:55 +00:00
|
|
|
if result is not None and _validate_type(cache_key, result):
|
2023-10-12 01:33:48 +00:00
|
|
|
_set_l0(cache_key, result)
|
2023-11-09 08:43:11 +00:00
|
|
|
if type(result) == str and result == NONE_RESULT:
|
2023-08-29 23:36:01 +00:00
|
|
|
result = None
|
2023-04-05 17:49:23 +00:00
|
|
|
return result
|
2023-10-11 00:37:36 +00:00
|
|
|
result = func(*args, **kwargs)
|
2023-08-29 23:36:01 +00:00
|
|
|
if result is None:
|
|
|
|
result = NONE_RESULT
|
2023-10-12 01:33:48 +00:00
|
|
|
_set(cache_key, result, timeout)
|
2023-04-05 17:49:23 +00:00
|
|
|
return result
|
|
|
|
|
|
|
|
def dirty(*args, **kwargs):
|
|
|
|
cache_key = get_key(func, *args, **kwargs)
|
|
|
|
cache.delete(cache_key)
|
2023-10-12 01:33:48 +00:00
|
|
|
if l0_cache:
|
|
|
|
l0_cache.delete(cache_key)
|
2023-04-05 17:49:23 +00:00
|
|
|
|
2024-02-23 23:07:34 +00:00
|
|
|
def prefetch_multi(args_list):
|
|
|
|
keys = []
|
|
|
|
for args in args_list:
|
|
|
|
keys.append(get_key(func, *args))
|
|
|
|
results = cache.get_many(keys)
|
|
|
|
for key, result in results.items():
|
|
|
|
if result is not None:
|
|
|
|
_set_l0(key, result)
|
|
|
|
|
2024-05-06 03:43:41 +00:00
|
|
|
def dirty_multi(args_list):
|
|
|
|
keys = []
|
|
|
|
for args in args_list:
|
|
|
|
keys.append(get_key(func, *args))
|
|
|
|
cache.delete_many(keys)
|
|
|
|
if l0_cache:
|
|
|
|
l0_cache.delete_many(keys)
|
|
|
|
|
2023-04-05 17:49:23 +00:00
|
|
|
wrapper.dirty = dirty
|
2024-02-23 23:07:34 +00:00
|
|
|
wrapper.prefetch_multi = prefetch_multi
|
2024-05-06 03:43:41 +00:00
|
|
|
wrapper.dirty_multi = dirty_multi
|
2023-04-05 17:49:23 +00:00
|
|
|
|
|
|
|
return wrapper
|
|
|
|
|
|
|
|
return decorator
|