Files
zhenxun_bot/zhenxun/builtin_plugins/hooks/auth_runtime_config.py
T

156 lines
5.3 KiB
Python

from __future__ import annotations
from dataclasses import dataclass, fields
import os
from urllib.parse import urlparse
@dataclass(frozen=True, slots=True)
class AuthDispatchRuntimeConfig:
hooks_concurrency_limit: int = 5
db_concurrency_limit: int = 6
command_exact_limit: int = 96
command_shortcut_limit: int = 32
command_regex_limit: int = 8
system_limit: int = 64
passive_light_limit: int = 12
passive_db_limit: int = 4
passive_http_limit: int = 4
passive_ai_limit: int = 2
passive_render_limit: int = 2
overload_selected_threshold: int = 48
overload_lane_wait_ms: float = 200.0
timeout_seconds: float = 5.0
circuit_reset_time: int = 300
matcher_route_prefilter_ttl: int = 2
prefilter_stats_log_interval: float = 10.0
cache_sweep_interval: float = 45.0
dispatch_stats_log_interval: float = 10.0
@dataclass(frozen=True, slots=True)
class AuthObservabilityRuntimeConfig:
buffer_max_retain: int = 20_000
flush_trigger_size: int = 256
flush_batch_size: int = 500
flush_interval_seconds: float = 30.0
drop_log_interval_seconds: float = 10.0
allow_sample_rate: float = 0.005
overloaded_allow_sample_rate: float = 0.02
non_allow_sample_rate: float = 1.0
backpressure_sample_rate: float = 0.2
backpressure_severe_active_threshold: int = 5
_WARNED_ENV_KEYS: set[str] = set()
_ENV_ALIASES: dict[str, tuple[str, ...]] = {
"hooks_concurrency_limit": ("ZX_AUTH_HOOKS_CONCURRENCY_LIMIT",),
"db_concurrency_limit": ("ZX_AUTH_DB_CONCURRENCY_LIMIT",),
"command_exact_limit": ("ZX_AUTH_DISPATCH_COMMAND_EXACT_LIMIT",),
"command_shortcut_limit": ("ZX_AUTH_DISPATCH_COMMAND_SHORTCUT_LIMIT",),
"command_regex_limit": ("ZX_AUTH_DISPATCH_COMMAND_REGEX_LIMIT",),
"system_limit": ("ZX_AUTH_DISPATCH_SYSTEM_LIMIT",),
"passive_light_limit": ("ZX_AUTH_DISPATCH_PASSIVE_LIGHT_LIMIT",),
"passive_db_limit": ("ZX_AUTH_DISPATCH_PASSIVE_DB_LIMIT",),
"passive_http_limit": ("ZX_AUTH_DISPATCH_PASSIVE_HTTP_LIMIT",),
"passive_ai_limit": ("ZX_AUTH_DISPATCH_PASSIVE_AI_LIMIT",),
"passive_render_limit": ("ZX_AUTH_DISPATCH_PASSIVE_RENDER_LIMIT",),
"overload_selected_threshold": ("ZX_AUTH_OVERLOAD_SELECTED_THRESHOLD",),
"overload_lane_wait_ms": ("ZX_AUTH_OVERLOAD_LANE_WAIT_MS",),
"timeout_seconds": ("ZX_AUTH_TIMEOUT_SECONDS",),
"circuit_reset_time": ("ZX_AUTH_CIRCUIT_RESET_TIME",),
"matcher_route_prefilter_ttl": ("ZX_AUTH_MATCHER_ROUTE_PREFILTER_TTL",),
"prefilter_stats_log_interval": ("ZX_AUTH_PREFILTER_STATS_LOG_INTERVAL",),
"cache_sweep_interval": ("ZX_AUTH_CACHE_SWEEP_INTERVAL",),
"dispatch_stats_log_interval": ("ZX_AUTH_DISPATCH_STATS_LOG_INTERVAL",),
}
def _env_name(prefix: str, field_name: str) -> str:
return f"{prefix}_{field_name.upper()}"
def _env_names(prefix: str, field_name: str) -> tuple[str, ...]:
generated = _env_name(prefix, field_name)
aliases = _ENV_ALIASES.get(field_name, ())
return (*aliases, generated)
def _coerce_env_value(raw: str, default: object) -> object:
if isinstance(default, bool):
return raw.strip().lower() in {"1", "true", "yes", "on"}
if isinstance(default, int) and not isinstance(default, bool):
return int(raw)
if isinstance(default, float):
return float(raw)
return raw
def _warn_invalid_env(env_name: str, raw: str, exc: Exception) -> None:
if env_name in _WARNED_ENV_KEYS:
return
_WARNED_ENV_KEYS.add(env_name)
try:
from zhenxun.services.log import logger
logger.warning(
f"{env_name}={raw!r} 解析失败,使用默认值: {exc}",
"AuthRuntimeConfig",
)
except Exception:
# Config is imported early on the auth hot path; logging must be optional.
return
def _default_passive_db_limit() -> int:
try:
from zhenxun.configs.config import BotConfig
scheme = urlparse(BotConfig.db_url or "").scheme.lower()
except Exception:
scheme = ""
if scheme == "sqlite":
return 1
if scheme.startswith("postgres"):
return 6
if scheme == "mysql":
return 4
return 2
def _load_config(cls: type, prefix: str):
values = {}
default_obj = cls()
for item in fields(default_obj):
default = getattr(default_obj, item.name)
env_name = ""
raw = None
for candidate in _env_names(prefix, item.name):
candidate_value = os.getenv(candidate)
if candidate_value is not None and candidate_value.strip():
env_name = candidate
raw = candidate_value
break
if raw is None or not raw.strip():
if cls is AuthDispatchRuntimeConfig and item.name == "passive_db_limit":
values[item.name] = _default_passive_db_limit()
continue
values[item.name] = default
continue
try:
values[item.name] = _coerce_env_value(raw, default)
except Exception as exc:
_warn_invalid_env(env_name, raw, exc)
values[item.name] = default
return cls(**values)
AUTH_DISPATCH_RUNTIME_CONFIG = _load_config(
AuthDispatchRuntimeConfig,
"ZX_AUTH",
)
AUTH_OBSERVABILITY_RUNTIME_CONFIG = _load_config(
AuthObservabilityRuntimeConfig,
"ZX_AUTH_OBSERVABILITY",
)