积分冻结释放
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from typing import Iterable
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.models.system_config import SystemConfig
|
||||
from app.utils.redis import get_redis
|
||||
|
||||
_CACHE_VERSION_KEY = "system_config_cache_version"
|
||||
_DEFAULT_TTL_SECONDS = 60
|
||||
|
||||
|
||||
@dataclass
|
||||
class _CacheState:
|
||||
values: dict[str, str | None]
|
||||
expires_at: float
|
||||
version: str | None
|
||||
|
||||
|
||||
_cache = _CacheState(values={}, expires_at=0.0, version=None)
|
||||
|
||||
|
||||
async def _get_remote_version() -> str | None:
|
||||
redis = get_redis()
|
||||
if not redis:
|
||||
return None
|
||||
try:
|
||||
value = await redis.get(_CACHE_VERSION_KEY)
|
||||
return str(value or "0")
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
async def invalidate_system_config_cache(keys: Iterable[str] | None = None) -> None:
|
||||
"""Invalidate local cache and notify other workers through a Redis version bump when available."""
|
||||
key_set = set(keys or [])
|
||||
if key_set:
|
||||
for key in key_set:
|
||||
_cache.values.pop(key, None)
|
||||
else:
|
||||
_cache.values.clear()
|
||||
_cache.expires_at = 0.0
|
||||
|
||||
redis = get_redis()
|
||||
if redis:
|
||||
try:
|
||||
await redis.incr(_CACHE_VERSION_KEY)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
async def get_system_config_values(
|
||||
db: AsyncSession,
|
||||
keys: Iterable[str],
|
||||
*,
|
||||
ttl_seconds: int = _DEFAULT_TTL_SECONDS,
|
||||
) -> dict[str, str | None]:
|
||||
key_list = [str(key) for key in keys if str(key)]
|
||||
if not key_list:
|
||||
return {}
|
||||
|
||||
now = time.monotonic()
|
||||
remote_version = await _get_remote_version()
|
||||
if remote_version is not None and remote_version != _cache.version:
|
||||
_cache.values.clear()
|
||||
_cache.expires_at = 0.0
|
||||
_cache.version = remote_version
|
||||
|
||||
missing = [key for key in key_list if key not in _cache.values]
|
||||
if now >= _cache.expires_at:
|
||||
missing = key_list
|
||||
|
||||
if missing:
|
||||
result = await db.execute(select(SystemConfig).where(SystemConfig.key.in_(missing)))
|
||||
rows = {row.key: row.value for row in result.scalars().all()}
|
||||
for key in missing:
|
||||
_cache.values[key] = rows.get(key)
|
||||
_cache.expires_at = now + max(1, int(ttl_seconds or _DEFAULT_TTL_SECONDS))
|
||||
if remote_version is not None:
|
||||
_cache.version = remote_version
|
||||
|
||||
return {key: _cache.values.get(key) for key in key_list}
|
||||
|
||||
|
||||
async def get_system_config_value(db: AsyncSession, key: str, *, ttl_seconds: int = _DEFAULT_TTL_SECONDS) -> str | None:
|
||||
return (await get_system_config_values(db, [key], ttl_seconds=ttl_seconds)).get(key)
|
||||
Reference in New Issue
Block a user