看股功能更新
This commit is contained in:
104
backend/app/cache.py
Normal file
104
backend/app/cache.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""Redis 读缓存(可选基础设施)。
|
||||
|
||||
- REDIS_URL 留空、连接失败或超时:所有操作静默退化为「无缓存」,接口照常直查数据库,
|
||||
且本进程内禁用重试(避免每个请求都陪跑一次连接超时)。
|
||||
- 失效策略:TTL 自然过期 + 版本号(INCR)作废。自选股增删等写操作只 INCR 版本 key,
|
||||
旧缓存 key 里带着旧版本号,无需 SCAN 批量删除。
|
||||
- 只缓存「读多写少、可容忍短暂陈旧」的聚合数据(股票列表、筛选项等);
|
||||
K线/回测等口径敏感数据不走这里。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import redis.asyncio as aioredis
|
||||
|
||||
from .config import settings
|
||||
|
||||
_pool: aioredis.ConnectionPool | None = None
|
||||
_disabled = False # 一次失败后本进程禁用(Redis 属加速件,坏了不能拖慢接口)
|
||||
|
||||
|
||||
def _client() -> aioredis.Redis | None:
|
||||
global _pool, _disabled
|
||||
if not settings.redis_url or _disabled:
|
||||
return None
|
||||
if _pool is None:
|
||||
_pool = aioredis.ConnectionPool.from_url(
|
||||
settings.redis_url,
|
||||
decode_responses=True,
|
||||
socket_connect_timeout=1.0,
|
||||
socket_timeout=1.0,
|
||||
health_check_interval=60,
|
||||
max_connections=32,
|
||||
)
|
||||
return aioredis.Redis(connection_pool=_pool)
|
||||
|
||||
|
||||
def _bail() -> None:
|
||||
global _disabled
|
||||
_disabled = True
|
||||
|
||||
|
||||
def digest(*parts: Any) -> str:
|
||||
"""参数指纹(拼接后 md5,仅用于拼缓存 key,非安全用途)"""
|
||||
raw = "\x1f".join(repr(p) for p in parts)
|
||||
return hashlib.md5(raw.encode()).hexdigest() # noqa: S324
|
||||
|
||||
|
||||
async def cache_get(key: str) -> Any | None:
|
||||
c = _client()
|
||||
if c is None:
|
||||
return None
|
||||
try:
|
||||
raw = await c.get(key)
|
||||
return json.loads(raw) if raw is not None else None
|
||||
except Exception: # noqa: BLE001 —— 缓存层任何故障都不影响主流程
|
||||
_bail()
|
||||
return None
|
||||
|
||||
|
||||
async def cache_set(key: str, value: Any, ttl: int) -> None:
|
||||
c = _client()
|
||||
if c is None:
|
||||
return
|
||||
try:
|
||||
await c.set(key, json.dumps(value, ensure_ascii=False), ex=max(1, ttl))
|
||||
except Exception: # noqa: BLE001
|
||||
_bail()
|
||||
|
||||
|
||||
async def get_version(name: str) -> int:
|
||||
"""读版本号(缺省 0)。版本号参与缓存 key:INCR 后旧 key 全部失效。"""
|
||||
c = _client()
|
||||
if c is None:
|
||||
return 0
|
||||
try:
|
||||
v = await c.get(f"ver:{name}")
|
||||
return int(v) if v is not None else 0
|
||||
except Exception: # noqa: BLE001
|
||||
_bail()
|
||||
return 0
|
||||
|
||||
|
||||
async def bump_version(name: str) -> None:
|
||||
c = _client()
|
||||
if c is None:
|
||||
return
|
||||
try:
|
||||
await c.incr(f"ver:{name}")
|
||||
except Exception: # noqa: BLE001
|
||||
_bail()
|
||||
|
||||
|
||||
async def aclose() -> None:
|
||||
"""进程退出时释放连接池(由 main.lifespan 调用)。"""
|
||||
global _pool
|
||||
if _pool is not None:
|
||||
try:
|
||||
await _pool.disconnect()
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
_pool = None
|
||||
Reference in New Issue
Block a user