fix(storage): inject Redis/MySQL clients into StorageManager singleton

refactor3.0
Lxy 2 weeks ago
parent 7cea089d48
commit 177a10fbd1

@ -39,16 +39,23 @@ async def lifespan(app: FastAPI):
# 初始化 Redis 和 MySQL
from app.redis_client import init_redis
from app.mysql_database import init_mysql
from app.storage_manager import get_storage_manager
redis_client = None
mysql_engine = None
try:
redis_ok = init_redis() is not None
redis_client = init_redis()
except Exception:
redis_ok = False
redis_client = None
try:
mysql_ok = init_mysql() is not None
mysql_engine = init_mysql()
except Exception:
mysql_ok = False
mysql_engine = None
redis_ok = redis_client is not None
mysql_ok = mysql_engine is not None
if redis_ok and mysql_ok:
logger.info("存储模式: Redis + MySQL")
@ -57,6 +64,10 @@ async def lifespan(app: FastAPI):
else:
logger.error("存储模式: SQLite (Redis 和 MySQL 均不可用)")
# 将初始化后的客户端注入 StorageManager 全局单例
storage_manager = get_storage_manager()
storage_manager.initialize(redis_client, mysql_engine)
if mysql_ok:
from app.mysql_database import mysql_engine
from app.models import Base as MarketBase, MarketData, SymbolTimestamp

@ -85,8 +85,17 @@ class StorageManager:
"""判断 Redis 或 MySQL 至少一个可用。"""
return self.check_redis() or self.check_mysql()
def initialize(self):
"""启动时调用,检测各后端可用性并输出日志。"""
def initialize(self, redis_client=None, mysql_engine=None):
"""启动时调用,可选注入 Redis/MySQL 客户端并检测各后端可用性。"""
if redis_client is not None:
self.redis_client = redis_client
if mysql_engine is not None:
self.mysql_engine = mysql_engine
# 重置检测缓存,确保注入新客户端后重新探测后端可用性
self.last_redis_check = 0
self.last_mysql_check = 0
redis_ok = self.check_redis()
mysql_ok = self.check_mysql()

@ -40,6 +40,11 @@ def patch_lifespan_deps():
for p in patches:
p.stop()
# 清理 StorageManager 全局单例,避免测试间状态泄漏
import app.storage_manager as _sm_module
_sm_module._storage_manager = None
@pytest.mark.asyncio
async def test_lifespan_calls_redis_and_mysql_init(patch_lifespan_deps, caplog):
@ -145,3 +150,36 @@ async def test_lifespan_storage_init_failure_does_not_raise(patch_lifespan_deps,
pass
assert "存储模式: SQLite (Redis 和 MySQL 均不可用)" in caplog.text
def test_storage_manager_initialize_accepts_clients_and_updates_references():
"""initialize 应可选接收 redis_client 和 mysql_engine 并更新内部引用。"""
from app.storage_manager import StorageManager
manager = StorageManager(redis_client=None, mysql_engine=None)
fake_redis = MagicMock()
fake_mysql = MagicMock()
manager.initialize(redis_client=fake_redis, mysql_engine=fake_mysql)
assert manager.redis_client is fake_redis
assert manager.mysql_engine is fake_mysql
@pytest.mark.asyncio
async def test_lifespan_injects_redis_and_mysql_into_storage_manager(patch_lifespan_deps):
"""lifespan 初始化后应将 Redis/MySQL 客户端注入 StorageManager 单例。"""
from app.main import lifespan, app
from app.storage_manager import StorageManager
fake_redis = MagicMock()
fake_mysql = MagicMock()
patch_lifespan_deps["init_redis"].return_value = fake_redis
patch_lifespan_deps["init_mysql"].return_value = fake_mysql
mock_manager = MagicMock(spec=StorageManager)
with patch("app.storage_manager.get_storage_manager", return_value=mock_manager):
async with lifespan(app):
pass
mock_manager.initialize.assert_called_once_with(fake_redis, fake_mysql)

Loading…
Cancel
Save