优化系统健康评分与缓存监测

This commit is contained in:
Cheng Zhou
2026-06-30 10:43:24 +08:00
parent b25055775e
commit 668e719b6d
10 changed files with 412 additions and 42 deletions
+15
View File
@@ -44,8 +44,12 @@ class PermissionCache:
if cache_key in self._member_role_cache:
cached_role, timestamp = self._member_role_cache[cache_key]
if not self._is_expired(timestamp, ttl):
from app.core.permission_monitor import get_permission_monitor
get_permission_monitor().record_cache_hit("member_roles")
return cached_role
from app.core.permission_monitor import get_permission_monitor
get_permission_monitor().record_cache_miss("member_roles")
membership = await member_crud.get_member(db, study_id, user_id)
role = membership.role_in_study if membership and membership.is_active else None
@@ -102,6 +106,17 @@ class PermissionCache:
self._member_role_cache.clear()
def get_cache_stats(self) -> dict[str, Any]:
now = time.time()
self._project_permissions_cache = {
key: value
for key, value in self._project_permissions_cache.items()
if now - value[1] <= self.default_ttl
}
self._member_role_cache = {
key: value
for key, value in self._member_role_cache.items()
if now - value[1] <= self.default_ttl
}
return {
"project_permissions_count": len(self._project_permissions_cache),
"member_role_count": len(self._member_role_cache),
+64 -10
View File
@@ -8,13 +8,15 @@ permission_access_logs 表中,通过 /metrics 端点实时聚合查询。
from __future__ import annotations
import time
from collections import deque
from dataclasses import dataclass, field
from typing import Any
from app.core.permission_cache import get_permission_cache
CACHE_HIT_RATE_HEALTH_MIN_ACCESSES = 10
CACHE_HIT_RATE_HEALTH_MIN_ACCESSES = 50
CACHE_METRICS_WINDOW_SECONDS = 3600
@dataclass
@@ -25,6 +27,11 @@ class CacheMetrics:
cache_hits: int = 0
cache_misses: int = 0
cache_invalidations: int = 0
_access_events: deque[tuple[float, str, bool]] = field(
default_factory=deque,
repr=False,
)
_invalidation_events: deque[float] = field(default_factory=deque, repr=False)
@property
def hit_rate(self) -> float:
@@ -48,6 +55,53 @@ class CacheMetrics:
"miss_rate": round(self.miss_rate, 2),
}
def record_access(self, cache_name: str, *, hit: bool) -> None:
self.total_accesses += 1
if hit:
self.cache_hits += 1
else:
self.cache_misses += 1
self._access_events.append((time.time(), cache_name, hit))
def record_invalidation(self) -> None:
self.cache_invalidations += 1
self._invalidation_events.append(time.time())
def window_stats(self, seconds: int = CACHE_METRICS_WINDOW_SECONDS) -> dict[str, Any]:
cutoff = time.time() - seconds
while self._access_events and self._access_events[0][0] < cutoff:
self._access_events.popleft()
while self._invalidation_events and self._invalidation_events[0] < cutoff:
self._invalidation_events.popleft()
def summarize(cache_name: str | None = None) -> dict[str, Any]:
events = [
event for event in self._access_events
if cache_name is None or event[1] == cache_name
]
hits = sum(event[2] for event in events)
total = len(events)
misses = total - hits
return {
"total_accesses": total,
"cache_hits": hits,
"cache_misses": misses,
"cache_invalidations": len(self._invalidation_events),
"hit_rate": round(hits / total * 100, 2) if total else 0.0,
"miss_rate": round(misses / total * 100, 2) if total else 0.0,
}
stats = summarize()
stats.update({
"window_seconds": seconds,
"scope": "sliding_window",
"by_cache": {
"project_permissions": summarize("project_permissions"),
"member_roles": summarize("member_roles"),
},
})
return stats
@dataclass
class PermissionSystemMetrics:
@@ -70,16 +124,14 @@ class PermissionMonitor:
self.metrics = PermissionSystemMetrics()
self._alerts: list[dict[str, Any]] = []
def record_cache_hit(self) -> None:
self.metrics.cache_metrics.total_accesses += 1
self.metrics.cache_metrics.cache_hits += 1
def record_cache_hit(self, cache_name: str = "project_permissions") -> None:
self.metrics.cache_metrics.record_access(cache_name, hit=True)
def record_cache_miss(self) -> None:
self.metrics.cache_metrics.total_accesses += 1
self.metrics.cache_metrics.cache_misses += 1
def record_cache_miss(self, cache_name: str = "project_permissions") -> None:
self.metrics.cache_metrics.record_access(cache_name, hit=False)
def record_cache_invalidation(self) -> None:
self.metrics.cache_metrics.cache_invalidations += 1
self.metrics.cache_metrics.record_invalidation()
def record_error_alert(self, error: Exception) -> None:
self._add_alert(
@@ -119,12 +171,14 @@ class PermissionMonitor:
cache = get_permission_cache()
return {
"cache_items": cache.get_cache_stats(),
"cache_metrics": self.metrics.cache_metrics.to_dict(),
"cache_metrics": self.metrics.cache_metrics.window_stats(),
"cache_metrics_lifetime": self.metrics.cache_metrics.to_dict(),
}
def get_metrics(self) -> dict[str, Any]:
return {
"cache_metrics": self.metrics.cache_metrics.to_dict(),
"cache_metrics": self.metrics.cache_metrics.window_stats(),
"cache_metrics_lifetime": self.metrics.cache_metrics.to_dict(),
"uptime_seconds": self.metrics.uptime_seconds,
}