fix: stabilize rule config and cross-review backend
This commit is contained in:
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
from collections import defaultdict
|
||||
import re
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from fastapi_common.fastapi_common_sqlalchemy.database import GetAsyncSession
|
||||
@@ -20,17 +21,49 @@ from fastapi_modules.fastapi_leaudit.domian.vo.ruleConfigVo import (
|
||||
from fastapi_modules.fastapi_leaudit.leaudit_bridge.ruleValidator import RuleValidator
|
||||
from fastapi_modules.fastapi_leaudit.services import IOssService
|
||||
from fastapi_modules.fastapi_leaudit.services.impl.ossServiceImpl import OssServiceImpl
|
||||
from fastapi_modules.fastapi_leaudit.services.impl.ruleServiceImpl import RuleServiceImpl
|
||||
from fastapi_modules.fastapi_leaudit.services.impl.ruleServiceImpl import GetRuleServiceSingleton
|
||||
from fastapi_modules.fastapi_leaudit.services.ruleConfigService import IRuleConfigService
|
||||
|
||||
|
||||
class RuleConfigServiceImpl(IRuleConfigService):
|
||||
"""规则配置页聚合服务实现。"""
|
||||
|
||||
_GLOBAL_YAML_SUMMARY_CACHE: dict[int, tuple[float, dict[str, Any]]] = {}
|
||||
_GLOBAL_PACK_SUMMARY_CACHE: tuple[float, list[RuleConfigPackListVO]] | None = None
|
||||
_GLOBAL_WARM_LOCK: asyncio.Lock | None = None
|
||||
|
||||
def __init__(self, OssService: IOssService | None = None) -> None:
|
||||
self.OssService = OssService or OssServiceImpl()
|
||||
self.RuleService = RuleServiceImpl(self.OssService)
|
||||
self.RuleService = GetRuleServiceSingleton()
|
||||
self.Validator = RuleValidator()
|
||||
self._yaml_summary_cache = self.__class__._GLOBAL_YAML_SUMMARY_CACHE
|
||||
self._pack_summary_cache = self.__class__._GLOBAL_PACK_SUMMARY_CACHE
|
||||
|
||||
@classmethod
|
||||
def _set_pack_summary_cache(cls, value: tuple[float, list[RuleConfigPackListVO]] | None) -> None:
|
||||
cls._GLOBAL_PACK_SUMMARY_CACHE = value
|
||||
|
||||
@classmethod
|
||||
def _get_warm_lock(cls) -> asyncio.Lock:
|
||||
if cls._GLOBAL_WARM_LOCK is None:
|
||||
cls._GLOBAL_WARM_LOCK = asyncio.Lock()
|
||||
return cls._GLOBAL_WARM_LOCK
|
||||
|
||||
def InvalidateSummaryCaches(self, version_ids: list[int] | None = None) -> None:
|
||||
"""清理规则摘要缓存;version_ids 为空时清空全部。"""
|
||||
self.__class__._set_pack_summary_cache(None)
|
||||
if version_ids is None:
|
||||
self._yaml_summary_cache.clear()
|
||||
return
|
||||
for version_id in {int(item) for item in version_ids if item is not None}:
|
||||
self._yaml_summary_cache.pop(version_id, None)
|
||||
|
||||
async def WarmPackSummaries(self, force: bool = False) -> list[RuleConfigPackListVO]:
|
||||
"""预热规则列表摘要缓存。"""
|
||||
async with self.__class__._get_warm_lock():
|
||||
if force:
|
||||
self.InvalidateSummaryCaches()
|
||||
return await self.ListPackSummaries()
|
||||
|
||||
async def ListPacks(self) -> list[RuleConfigPackVO]:
|
||||
"""列出规则配置页所需的全部 pack。"""
|
||||
@@ -40,6 +73,11 @@ class RuleConfigServiceImpl(IRuleConfigService):
|
||||
|
||||
async def ListPackSummaries(self) -> list[RuleConfigPackListVO]:
|
||||
"""列出规则列表页所需的轻量 pack。"""
|
||||
cached = self.__class__._GLOBAL_PACK_SUMMARY_CACHE
|
||||
now = time.monotonic()
|
||||
if cached and now - cached[0] <= 60:
|
||||
return cached[1]
|
||||
|
||||
rows = await self._load_pack_rows()
|
||||
if not rows:
|
||||
return []
|
||||
@@ -121,6 +159,9 @@ class RuleConfigServiceImpl(IRuleConfigService):
|
||||
item["usableRuleCount"] = len(rules)
|
||||
packs.append(RuleConfigPackListVO(**item))
|
||||
|
||||
cache_value = (time.monotonic(), packs)
|
||||
self.__class__._set_pack_summary_cache(cache_value)
|
||||
self._pack_summary_cache = cache_value
|
||||
return packs
|
||||
|
||||
async def GetPack(self, PackId: int) -> RuleConfigPackVO:
|
||||
@@ -471,10 +512,16 @@ class RuleConfigServiceImpl(IRuleConfigService):
|
||||
async def _load_one(version_id: int, oss_url: str):
|
||||
if not oss_url:
|
||||
return version_id, {"loaded": False, "yaml_name": "", "rules": []}
|
||||
cached = self._yaml_summary_cache.get(version_id)
|
||||
now = time.monotonic()
|
||||
if cached and now - cached[0] <= 120:
|
||||
return version_id, cached[1]
|
||||
try:
|
||||
yaml_text = (await self.OssService.DownloadBytes(oss_url)).decode("utf-8")
|
||||
if not yaml_text.strip():
|
||||
return version_id, {"loaded": False, "yaml_name": "", "rules": []}
|
||||
summary = {"loaded": False, "yaml_name": "", "rules": []}
|
||||
self._yaml_summary_cache[version_id] = (time.monotonic(), summary)
|
||||
return version_id, summary
|
||||
rules_file = self.Validator.ParseValidated(yaml_text)
|
||||
groups: dict[str, str] = {}
|
||||
try:
|
||||
@@ -540,13 +587,17 @@ class RuleConfigServiceImpl(IRuleConfigService):
|
||||
seen_dependencies.add(normalized_dependency)
|
||||
merged_dependencies.append(normalized_dependency)
|
||||
item["dependencies"] = merged_dependencies
|
||||
return version_id, {
|
||||
summary = {
|
||||
"loaded": True,
|
||||
"yaml_name": str(getattr(getattr(rules_file, "metadata", None), "name", "") or ""),
|
||||
"rules": summaries,
|
||||
}
|
||||
self._yaml_summary_cache[version_id] = (time.monotonic(), summary)
|
||||
return version_id, summary
|
||||
except Exception:
|
||||
return version_id, {"loaded": False, "yaml_name": "", "rules": []}
|
||||
summary = {"loaded": False, "yaml_name": "", "rules": []}
|
||||
self._yaml_summary_cache[version_id] = (time.monotonic(), summary)
|
||||
return version_id, summary
|
||||
|
||||
results = await asyncio.gather(*[_load_one(version_id, oss_url) for version_id, oss_url in version_oss_map.items()])
|
||||
return dict(results)
|
||||
@@ -592,3 +643,14 @@ class RuleConfigServiceImpl(IRuleConfigService):
|
||||
if value is None:
|
||||
return None
|
||||
return int(value)
|
||||
|
||||
|
||||
_RULE_CONFIG_SERVICE_SINGLETON: RuleConfigServiceImpl | None = None
|
||||
|
||||
|
||||
def GetRuleConfigServiceSingleton() -> RuleConfigServiceImpl:
|
||||
"""返回共享的规则配置服务实例,供控制器和预热任务共用缓存。"""
|
||||
global _RULE_CONFIG_SERVICE_SINGLETON
|
||||
if _RULE_CONFIG_SERVICE_SINGLETON is None:
|
||||
_RULE_CONFIG_SERVICE_SINGLETON = RuleConfigServiceImpl()
|
||||
return _RULE_CONFIG_SERVICE_SINGLETON
|
||||
|
||||
Reference in New Issue
Block a user