mirror of
https://github.com/QingdaoU/OnlineJudge.git
synced 2024-12-28 16:12:13 +00:00
增加部分缓存
This commit is contained in:
parent
482a1a7d02
commit
7df98245e4
@ -1,13 +1,92 @@
|
|||||||
|
import functools
|
||||||
import os
|
import os
|
||||||
from django.core.cache import cache
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
from django.db import transaction, IntegrityError
|
from django.db import transaction, IntegrityError
|
||||||
|
|
||||||
from utils.constants import CacheKey
|
|
||||||
from utils.shortcuts import rand_str
|
from utils.shortcuts import rand_str
|
||||||
from judge.languages import languages
|
from judge.languages import languages
|
||||||
from .models import SysOptions as SysOptionsModel
|
from .models import SysOptions as SysOptionsModel
|
||||||
|
|
||||||
|
|
||||||
|
class my_property:
|
||||||
|
"""
|
||||||
|
在 metaclass 中使用,以实现:
|
||||||
|
1. ttl = None,不缓存
|
||||||
|
2. ttl is callable,条件缓存
|
||||||
|
3. 缓存 ttl 秒
|
||||||
|
"""
|
||||||
|
def __init__(self, func=None, fset=None, ttl=None):
|
||||||
|
self.fset = fset
|
||||||
|
self.local = threading.local()
|
||||||
|
self.ttl = ttl
|
||||||
|
self._check_ttl(ttl)
|
||||||
|
self.func = func
|
||||||
|
functools.update_wrapper(self, func)
|
||||||
|
|
||||||
|
def _check_ttl(self, value):
|
||||||
|
if value is None or callable(value):
|
||||||
|
return
|
||||||
|
return self._check_timeout(value)
|
||||||
|
|
||||||
|
def _check_timeout(self, value):
|
||||||
|
if not isinstance(value, int):
|
||||||
|
raise ValueError(f"Invalid timeout type: {type(value)}")
|
||||||
|
if value < 0:
|
||||||
|
raise ValueError("Invalid timeout value, it must >= 0")
|
||||||
|
|
||||||
|
def __get__(self, obj, cls):
|
||||||
|
if obj is None:
|
||||||
|
return self
|
||||||
|
|
||||||
|
now = time.time()
|
||||||
|
if self.ttl:
|
||||||
|
if hasattr(self.local, "value"):
|
||||||
|
value, expire_at = self.local.value
|
||||||
|
if now < expire_at:
|
||||||
|
return value
|
||||||
|
|
||||||
|
value = self.func(obj)
|
||||||
|
|
||||||
|
# 如果定义了条件缓存, ttl 是一个函数,返回要缓存多久;返回 0 代表不要缓存
|
||||||
|
if callable(self.ttl):
|
||||||
|
# 而且条件缓存说不要缓存,那就直接返回,不要设置 local
|
||||||
|
timeout = self.ttl(value)
|
||||||
|
self._check_timeout(timeout)
|
||||||
|
|
||||||
|
if timeout == 0:
|
||||||
|
return value
|
||||||
|
elif timeout > 0:
|
||||||
|
self.local.value = (value, now + timeout)
|
||||||
|
else:
|
||||||
|
# ttl 是一个数字
|
||||||
|
self.local.value = (value, now + self.ttl)
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return self.func(obj)
|
||||||
|
|
||||||
|
def __set__(self, obj, value):
|
||||||
|
if not self.fset:
|
||||||
|
raise AttributeError("can't set attribute")
|
||||||
|
self.fset(obj, value)
|
||||||
|
if hasattr(self.local, "value"):
|
||||||
|
del self.local.value
|
||||||
|
|
||||||
|
def setter(self, func):
|
||||||
|
self.fset = func
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __call__(self, func, *args, **kwargs) -> "my_property":
|
||||||
|
if self.func is None:
|
||||||
|
self.func = func
|
||||||
|
functools.update_wrapper(self, func)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_SHORT_TTL = 2
|
||||||
|
|
||||||
|
|
||||||
def default_token():
|
def default_token():
|
||||||
token = os.environ.get("JUDGE_SERVER_TOKEN")
|
token = os.environ.get("JUDGE_SERVER_TOKEN")
|
||||||
return token if token else rand_str()
|
return token if token else rand_str()
|
||||||
@ -41,23 +120,10 @@ class OptionDefaultValue:
|
|||||||
|
|
||||||
|
|
||||||
class _SysOptionsMeta(type):
|
class _SysOptionsMeta(type):
|
||||||
@classmethod
|
|
||||||
def _set_cache(mcs, option_key, option_value):
|
|
||||||
cache.set(f"{CacheKey.option}:{option_key}", option_value, timeout=60)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _del_cache(mcs, option_key):
|
|
||||||
cache.delete(f"{CacheKey.option}:{option_key}")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_keys(cls):
|
def _get_keys(cls):
|
||||||
return [key for key in OptionKeys.__dict__ if not key.startswith("__")]
|
return [key for key in OptionKeys.__dict__ if not key.startswith("__")]
|
||||||
|
|
||||||
def rebuild_cache(cls):
|
|
||||||
for key in cls._get_keys():
|
|
||||||
# get option 的时候会写 cache 的
|
|
||||||
cls._get_option(key, use_cache=False)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _init_option(mcs):
|
def _init_option(mcs):
|
||||||
for item in mcs._get_keys():
|
for item in mcs._get_keys():
|
||||||
@ -71,19 +137,14 @@ class _SysOptionsMeta(type):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_option(mcs, option_key, use_cache=True):
|
def _get_option(mcs, option_key):
|
||||||
try:
|
try:
|
||||||
if use_cache:
|
|
||||||
option = cache.get(f"{CacheKey.option}:{option_key}")
|
|
||||||
if option:
|
|
||||||
return option
|
|
||||||
option = SysOptionsModel.objects.get(key=option_key)
|
option = SysOptionsModel.objects.get(key=option_key)
|
||||||
value = option.value
|
value = option.value
|
||||||
mcs._set_cache(option_key, value)
|
|
||||||
return value
|
return value
|
||||||
except SysOptionsModel.DoesNotExist:
|
except SysOptionsModel.DoesNotExist:
|
||||||
mcs._init_option()
|
mcs._init_option()
|
||||||
return mcs._get_option(option_key, use_cache=use_cache)
|
return mcs._get_option(option_key)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _set_option(mcs, option_key: str, option_value):
|
def _set_option(mcs, option_key: str, option_value):
|
||||||
@ -92,7 +153,6 @@ class _SysOptionsMeta(type):
|
|||||||
option = SysOptionsModel.objects.select_for_update().get(key=option_key)
|
option = SysOptionsModel.objects.select_for_update().get(key=option_key)
|
||||||
option.value = option_value
|
option.value = option_value
|
||||||
option.save()
|
option.save()
|
||||||
mcs._del_cache(option_key)
|
|
||||||
except SysOptionsModel.DoesNotExist:
|
except SysOptionsModel.DoesNotExist:
|
||||||
mcs._init_option()
|
mcs._init_option()
|
||||||
mcs._set_option(option_key, option_value)
|
mcs._set_option(option_key, option_value)
|
||||||
@ -105,7 +165,6 @@ class _SysOptionsMeta(type):
|
|||||||
value = option.value + 1
|
value = option.value + 1
|
||||||
option.value = value
|
option.value = value
|
||||||
option.save()
|
option.save()
|
||||||
mcs._del_cache(option_key)
|
|
||||||
except SysOptionsModel.DoesNotExist:
|
except SysOptionsModel.DoesNotExist:
|
||||||
mcs._init_option()
|
mcs._init_option()
|
||||||
return mcs._increment(option_key)
|
return mcs._increment(option_key)
|
||||||
@ -122,7 +181,7 @@ class _SysOptionsMeta(type):
|
|||||||
result[key] = mcs._get_option(key)
|
result[key] = mcs._get_option(key)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@property
|
@my_property(ttl=DEFAULT_SHORT_TTL)
|
||||||
def website_base_url(cls):
|
def website_base_url(cls):
|
||||||
return cls._get_option(OptionKeys.website_base_url)
|
return cls._get_option(OptionKeys.website_base_url)
|
||||||
|
|
||||||
@ -130,7 +189,7 @@ class _SysOptionsMeta(type):
|
|||||||
def website_base_url(cls, value):
|
def website_base_url(cls, value):
|
||||||
cls._set_option(OptionKeys.website_base_url, value)
|
cls._set_option(OptionKeys.website_base_url, value)
|
||||||
|
|
||||||
@property
|
@my_property(ttl=DEFAULT_SHORT_TTL)
|
||||||
def website_name(cls):
|
def website_name(cls):
|
||||||
return cls._get_option(OptionKeys.website_name)
|
return cls._get_option(OptionKeys.website_name)
|
||||||
|
|
||||||
@ -138,7 +197,7 @@ class _SysOptionsMeta(type):
|
|||||||
def website_name(cls, value):
|
def website_name(cls, value):
|
||||||
cls._set_option(OptionKeys.website_name, value)
|
cls._set_option(OptionKeys.website_name, value)
|
||||||
|
|
||||||
@property
|
@my_property(ttl=DEFAULT_SHORT_TTL)
|
||||||
def website_name_shortcut(cls):
|
def website_name_shortcut(cls):
|
||||||
return cls._get_option(OptionKeys.website_name_shortcut)
|
return cls._get_option(OptionKeys.website_name_shortcut)
|
||||||
|
|
||||||
@ -146,7 +205,7 @@ class _SysOptionsMeta(type):
|
|||||||
def website_name_shortcut(cls, value):
|
def website_name_shortcut(cls, value):
|
||||||
cls._set_option(OptionKeys.website_name_shortcut, value)
|
cls._set_option(OptionKeys.website_name_shortcut, value)
|
||||||
|
|
||||||
@property
|
@my_property(ttl=DEFAULT_SHORT_TTL)
|
||||||
def website_footer(cls):
|
def website_footer(cls):
|
||||||
return cls._get_option(OptionKeys.website_footer)
|
return cls._get_option(OptionKeys.website_footer)
|
||||||
|
|
||||||
@ -154,7 +213,7 @@ class _SysOptionsMeta(type):
|
|||||||
def website_footer(cls, value):
|
def website_footer(cls, value):
|
||||||
cls._set_option(OptionKeys.website_footer, value)
|
cls._set_option(OptionKeys.website_footer, value)
|
||||||
|
|
||||||
@property
|
@my_property
|
||||||
def allow_register(cls):
|
def allow_register(cls):
|
||||||
return cls._get_option(OptionKeys.allow_register)
|
return cls._get_option(OptionKeys.allow_register)
|
||||||
|
|
||||||
@ -162,7 +221,7 @@ class _SysOptionsMeta(type):
|
|||||||
def allow_register(cls, value):
|
def allow_register(cls, value):
|
||||||
cls._set_option(OptionKeys.allow_register, value)
|
cls._set_option(OptionKeys.allow_register, value)
|
||||||
|
|
||||||
@property
|
@my_property(ttl=DEFAULT_SHORT_TTL)
|
||||||
def submission_list_show_all(cls):
|
def submission_list_show_all(cls):
|
||||||
return cls._get_option(OptionKeys.submission_list_show_all)
|
return cls._get_option(OptionKeys.submission_list_show_all)
|
||||||
|
|
||||||
@ -170,7 +229,7 @@ class _SysOptionsMeta(type):
|
|||||||
def submission_list_show_all(cls, value):
|
def submission_list_show_all(cls, value):
|
||||||
cls._set_option(OptionKeys.submission_list_show_all, value)
|
cls._set_option(OptionKeys.submission_list_show_all, value)
|
||||||
|
|
||||||
@property
|
@my_property
|
||||||
def smtp_config(cls):
|
def smtp_config(cls):
|
||||||
return cls._get_option(OptionKeys.smtp_config)
|
return cls._get_option(OptionKeys.smtp_config)
|
||||||
|
|
||||||
@ -178,7 +237,7 @@ class _SysOptionsMeta(type):
|
|||||||
def smtp_config(cls, value):
|
def smtp_config(cls, value):
|
||||||
cls._set_option(OptionKeys.smtp_config, value)
|
cls._set_option(OptionKeys.smtp_config, value)
|
||||||
|
|
||||||
@property
|
@my_property
|
||||||
def judge_server_token(cls):
|
def judge_server_token(cls):
|
||||||
return cls._get_option(OptionKeys.judge_server_token)
|
return cls._get_option(OptionKeys.judge_server_token)
|
||||||
|
|
||||||
@ -186,7 +245,7 @@ class _SysOptionsMeta(type):
|
|||||||
def judge_server_token(cls, value):
|
def judge_server_token(cls, value):
|
||||||
cls._set_option(OptionKeys.judge_server_token, value)
|
cls._set_option(OptionKeys.judge_server_token, value)
|
||||||
|
|
||||||
@property
|
@my_property
|
||||||
def throttling(cls):
|
def throttling(cls):
|
||||||
return cls._get_option(OptionKeys.throttling)
|
return cls._get_option(OptionKeys.throttling)
|
||||||
|
|
||||||
@ -194,7 +253,7 @@ class _SysOptionsMeta(type):
|
|||||||
def throttling(cls, value):
|
def throttling(cls, value):
|
||||||
cls._set_option(OptionKeys.throttling, value)
|
cls._set_option(OptionKeys.throttling, value)
|
||||||
|
|
||||||
@property
|
@my_property(ttl=DEFAULT_SHORT_TTL)
|
||||||
def languages(cls):
|
def languages(cls):
|
||||||
return cls._get_option(OptionKeys.languages)
|
return cls._get_option(OptionKeys.languages)
|
||||||
|
|
||||||
@ -202,15 +261,15 @@ class _SysOptionsMeta(type):
|
|||||||
def languages(cls, value):
|
def languages(cls, value):
|
||||||
cls._set_option(OptionKeys.languages, value)
|
cls._set_option(OptionKeys.languages, value)
|
||||||
|
|
||||||
@property
|
@my_property(ttl=DEFAULT_SHORT_TTL)
|
||||||
def spj_languages(cls):
|
def spj_languages(cls):
|
||||||
return [item for item in cls.languages if "spj" in item]
|
return [item for item in cls.languages if "spj" in item]
|
||||||
|
|
||||||
@property
|
@my_property(ttl=DEFAULT_SHORT_TTL)
|
||||||
def language_names(cls):
|
def language_names(cls):
|
||||||
return [item["name"] for item in languages]
|
return [item["name"] for item in languages]
|
||||||
|
|
||||||
@property
|
@my_property(ttl=DEFAULT_SHORT_TTL)
|
||||||
def spj_language_names(cls):
|
def spj_language_names(cls):
|
||||||
return [item["name"] for item in cls.languages if "spj" in item]
|
return [item["name"] for item in cls.languages if "spj" in item]
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import re
|
import re
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
|
||||||
TEMPLATE_BASE = """//PREPEND BEGIN
|
TEMPLATE_BASE = """//PREPEND BEGIN
|
||||||
{}
|
{}
|
||||||
@ -13,6 +15,7 @@ TEMPLATE_BASE = """//PREPEND BEGIN
|
|||||||
//APPEND END"""
|
//APPEND END"""
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=100)
|
||||||
def parse_problem_template(template_str):
|
def parse_problem_template(template_str):
|
||||||
prepend = re.findall(r"//PREPEND BEGIN\n([\s\S]+?)//PREPEND END", template_str)
|
prepend = re.findall(r"//PREPEND BEGIN\n([\s\S]+?)//PREPEND END", template_str)
|
||||||
template = re.findall(r"//TEMPLATE BEGIN\n([\s\S]+?)//TEMPLATE END", template_str)
|
template = re.findall(r"//TEMPLATE BEGIN\n([\s\S]+?)//TEMPLATE END", template_str)
|
||||||
@ -22,5 +25,6 @@ def parse_problem_template(template_str):
|
|||||||
"append": append[0] if append else ""}
|
"append": append[0] if append else ""}
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=100)
|
||||||
def build_problem_template(prepend, template, append):
|
def build_problem_template(prepend, template, append):
|
||||||
return TEMPLATE_BASE.format(prepend, template, append)
|
return TEMPLATE_BASE.format(prepend, template, append)
|
||||||
|
@ -25,7 +25,6 @@ class CacheKey:
|
|||||||
waiting_queue = "waiting_queue"
|
waiting_queue = "waiting_queue"
|
||||||
contest_rank_cache = "contest_rank_cache"
|
contest_rank_cache = "contest_rank_cache"
|
||||||
website_config = "website_config"
|
website_config = "website_config"
|
||||||
option = "option"
|
|
||||||
|
|
||||||
|
|
||||||
class Difficulty(Choices):
|
class Difficulty(Choices):
|
||||||
|
Loading…
Reference in New Issue
Block a user