mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-29 03:56:43 +08:00
refactor: own fanart cache cleanup
This commit is contained in:
@@ -8,6 +8,7 @@ from app.runtime.settings import RuntimeSettingsCompat
|
||||
|
||||
settings = RuntimeSettingsCompat()
|
||||
from app.runtime.log import logger
|
||||
from app.runtime.tasks import get_task_registry
|
||||
from app.modules import _ModuleBase
|
||||
from app.schemas.types import MediaType, ModuleType, OtherModulesType
|
||||
from app.adapters.network.http import RequestUtils, AsyncRequestUtils
|
||||
@@ -626,16 +627,22 @@ class FanartModule(_ModuleBase):
|
||||
return cls._movie_url % queryid
|
||||
return cls._tv_url % queryid
|
||||
|
||||
def clear_cache(self):
|
||||
"""
|
||||
清除缓存
|
||||
"""
|
||||
def clear_cache(self) -> None:
|
||||
"""清理同步缓存,并由宿主登记运行中事件循环的异步清理。"""
|
||||
logger.info(f"开始清除{self.get_name()}缓存 ...")
|
||||
self.__request_fanart.cache_clear()
|
||||
async_cache_clear = self.__async_request_fanart.cache_clear()
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
loop.create_task(async_cache_clear)
|
||||
asyncio.get_running_loop()
|
||||
except RuntimeError:
|
||||
asyncio.run(async_cache_clear)
|
||||
else:
|
||||
try:
|
||||
get_task_registry().create(
|
||||
async_cache_clear,
|
||||
owner="module.fanart.cache_clear",
|
||||
)
|
||||
except RuntimeError:
|
||||
# 关停阶段拒绝新 owner 时,同步缓存已清理且登记器会关闭 coroutine。
|
||||
return
|
||||
logger.info(f"{self.get_name()}缓存清除完成")
|
||||
|
||||
@@ -85,6 +85,8 @@
|
||||
Agent 活动摘要已完成一个深层 owner 切片:中间件仍保留自己的完成回调与非阻塞语义,但任务创建统一
|
||||
经 lifespan `TaskRegistry` 登记为 `agent.activity_log.record`,宿主关停会取消并有限等待,不再形成
|
||||
绕过全局关停预算的第二套后台任务集合。该摘要属于可丢弃 E1 观测数据,不宣称 durable。
|
||||
模块同步清缓存的异步桥接也统一复用同一模式:Fanart 不再保留裸 `loop.create_task`,与 IMDb 一样登记
|
||||
稳定 owner,并继续保留无事件循环时 `asyncio.run` 和原同步 ABI。
|
||||
2. **动态模块契约仍以 legacy 聚合语义为主。** 当前登记 `212` 个模块方法,其中 `194` 个仍使用 `legacy` aggregation,只有 `14` 个 `first_non_empty`、`4` 个 `ordered_list_merge`。`app/runtime/extensions/module/contracts.py:422-455` 已能登记 family、输入/结果标签和基础签名诊断,但 `193` 个方法没有 required parameters,调度器 `app/runtime/extensions/module/dispatcher.py:109-260` 仍主要依赖运行时反射、返回值形状和短路规则。未知第三方方法保留 legacy fallback 是兼容要求,不应删除;宿主高频能力则应逐族补齐可执行的输入校验、结果校验、超时和错误语义。
|
||||
3. **Model/Base 的数据库装饰器和隐式会话 ABI 已全部清零。** 查询、写事务和 `legacy_*` 装饰器均为 `0`;所有 Model `db` 参数要求显式 Session,Base CRUD 仅在调用方事务内查询或 stage。可无会话构造的入口统一留在 Oper,经组合根事务执行器运行;插件 SDK 不再导出宿主 Model。后续重点转为减少 ORM 对象跨层流转,并保持 Model 隐式事务零回退。
|
||||
|
||||
|
||||
+3
-2
@@ -13,8 +13,8 @@
|
||||
"runtime_to_db": [],
|
||||
"workflow_to_db": []
|
||||
},
|
||||
"edge_count": 6500,
|
||||
"edge_sha256": "6fa263f6f10aa51b4395aa41cf32a0c24254c2131d7882341936412400a10e4a",
|
||||
"edge_count": 6501,
|
||||
"edge_sha256": "3c01f9aeb7703065ff447672284ddcc88d6120ac3236b9659200df9b6f921bdd",
|
||||
"edges": [
|
||||
"app -> app.runtime",
|
||||
"app -> app.runtime.compat",
|
||||
@@ -4115,6 +4115,7 @@
|
||||
"app.modules.fanart -> app.runtime.cache",
|
||||
"app.modules.fanart -> app.runtime.log",
|
||||
"app.modules.fanart -> app.runtime.settings",
|
||||
"app.modules.fanart -> app.runtime.tasks",
|
||||
"app.modules.fanart -> app.schemas",
|
||||
"app.modules.fanart -> app.schemas.types",
|
||||
"app.modules.feishu -> app.application",
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
"""Fanart 模块缓存清理生命周期测试。"""
|
||||
|
||||
import asyncio
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from app.modules.fanart import FanartModule
|
||||
from app.runtime.tasks import TaskRegistry
|
||||
|
||||
|
||||
def test_fanart_clear_cache_registers_async_cleanup_in_running_loop() -> None:
|
||||
"""同步清缓存入口应复用宿主 owner,并保持立即返回的兼容约定。"""
|
||||
|
||||
async def scenario() -> None:
|
||||
"""验证异步缓存清理完成前始终由宿主登记器持有。"""
|
||||
registry = TaskRegistry()
|
||||
release = asyncio.Event()
|
||||
|
||||
async def clear_async_cache() -> None:
|
||||
"""等待测试释放,以便观察登记中的清理任务。"""
|
||||
await release.wait()
|
||||
|
||||
sync_cache = SimpleNamespace(cache_clear=Mock())
|
||||
async_cache = SimpleNamespace(
|
||||
cache_clear=Mock(side_effect=lambda: clear_async_cache())
|
||||
)
|
||||
module = FanartModule()
|
||||
with (
|
||||
patch.object(
|
||||
FanartModule,
|
||||
"_FanartModule__request_fanart",
|
||||
sync_cache,
|
||||
),
|
||||
patch.object(
|
||||
FanartModule,
|
||||
"_FanartModule__async_request_fanart",
|
||||
async_cache,
|
||||
),
|
||||
patch("app.modules.fanart.get_task_registry", return_value=registry),
|
||||
):
|
||||
assert module.clear_cache() is None
|
||||
assert [record.owner for record in registry.records] == [
|
||||
"module.fanart.cache_clear"
|
||||
]
|
||||
|
||||
release.set()
|
||||
await registry.records[0].task
|
||||
await asyncio.sleep(0)
|
||||
|
||||
sync_cache.cache_clear.assert_called_once_with()
|
||||
async_cache.cache_clear.assert_called_once_with()
|
||||
assert registry.records == ()
|
||||
|
||||
asyncio.run(scenario())
|
||||
Reference in New Issue
Block a user