mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
fix(imdb): register async cache cleanup
This commit is contained in:
+11
-2
@@ -10,6 +10,7 @@ from pydantic import BaseModel, ConfigDict, Field, ValidationError
|
|||||||
from app.adapters.network.http import AsyncRequestUtils, RequestUtils
|
from app.adapters.network.http import AsyncRequestUtils, RequestUtils
|
||||||
from app.runtime.cache import cached
|
from app.runtime.cache import cached
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
|
from app.runtime.tasks import get_task_registry
|
||||||
|
|
||||||
settings = RuntimeSettingsCompat()
|
settings = RuntimeSettingsCompat()
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
@@ -339,11 +340,19 @@ class ImdbApi:
|
|||||||
self._cached_get_json.cache_clear()
|
self._cached_get_json.cache_clear()
|
||||||
self._cached_graphql.cache_clear()
|
self._cached_graphql.cache_clear()
|
||||||
try:
|
try:
|
||||||
loop = asyncio.get_running_loop()
|
asyncio.get_running_loop()
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
asyncio.run(self.async_clear_cache())
|
asyncio.run(self.async_clear_cache())
|
||||||
else:
|
else:
|
||||||
loop.create_task(self.async_clear_cache())
|
# 同步 ABI 不能改成 async;运行中的循环交给宿主登记器收口,避免清理任务悬挂。
|
||||||
|
try:
|
||||||
|
get_task_registry().create(
|
||||||
|
self.async_clear_cache(),
|
||||||
|
owner="module.imdb.cache_clear",
|
||||||
|
)
|
||||||
|
except RuntimeError:
|
||||||
|
# 兼容宿主已经进入关停阶段的同步调用:同步缓存已清理,异步缓存无需再启动新任务。
|
||||||
|
return
|
||||||
|
|
||||||
async def async_clear_cache(self) -> None:
|
async def async_clear_cache(self) -> None:
|
||||||
"""清理 IMDb 异步 GET 与 GraphQL 请求缓存区。"""
|
"""清理 IMDb 异步 GET 与 GraphQL 请求缓存区。"""
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ Event Contract Registry 是 53 个事件的逐项机器清单。下表按相同
|
|||||||
`api.anthropic.stream` 登记并在 lifespan shutdown 时取消;它们仍是 E0 请求交付,不提供跨重启恢复。
|
`api.anthropic.stream` 登记并在 lifespan shutdown 时取消;它们仍是 E0 请求交付,不提供跨重启恢复。
|
||||||
- stdio MCP 的 stderr reader 属于会话资源内部任务;会话退出时先取消并等待 reader 收口,再终止子进程,避免
|
- stdio MCP 的 stderr reader 属于会话资源内部任务;会话退出时先取消并等待 reader 收口,再终止子进程,避免
|
||||||
资源已释放而 reader 仍悬挂。
|
资源已释放而 reader 仍悬挂。
|
||||||
|
- IMDb 同步 `clear_cache()` ABI 在事件循环内触发的异步缓存清理登记为
|
||||||
|
`module.imdb.cache_clear`;同步调用方式和无运行事件循环时的立即清理行为保持不变,宿主关停后不再
|
||||||
|
接受新的清理任务。
|
||||||
|
|
||||||
### Transfer pending / 文件整理
|
### Transfer pending / 文件整理
|
||||||
|
|
||||||
|
|||||||
@@ -836,6 +836,8 @@ ADR 必须逐个映射当前 Event、BackgroundTasks、Scheduler job、Agent tas
|
|||||||
- OpenAI Chat Completions 与 Anthropic Messages 的流式 Agent 执行分别登记为
|
- OpenAI Chat Completions 与 Anthropic Messages 的流式 Agent 执行分别登记为
|
||||||
`api.openai.stream` 和 `api.anthropic.stream`。SSE payload、断线取消、临时会话清理和非流式入口保持
|
`api.openai.stream` 和 `api.anthropic.stream`。SSE payload、断线取消、临时会话清理和非流式入口保持
|
||||||
原语义;未启动完整 lifespan 的协议校验和旧直接调用通过兼容依赖回退到默认登记器。
|
原语义;未启动完整 lifespan 的协议校验和旧直接调用通过兼容依赖回退到默认登记器。
|
||||||
|
- IMDb 同步清缓存兼容入口在运行事件循环时改由 TaskRegistry 登记异步缓存清理任务,owner 为
|
||||||
|
`module.imdb.cache_clear`;同步签名、模块调用方式和无事件循环时的立即清理语义保持不变。
|
||||||
|
|
||||||
#### ARCH-251:用现有数据库做首个 durable side-effect pilot
|
#### ARCH-251:用现有数据库做首个 durable side-effect pilot
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -13,8 +13,8 @@
|
|||||||
"runtime_to_db": [],
|
"runtime_to_db": [],
|
||||||
"workflow_to_db": []
|
"workflow_to_db": []
|
||||||
},
|
},
|
||||||
"edge_count": 6477,
|
"edge_count": 6478,
|
||||||
"edge_sha256": "41e51256026afbd3abc9684dd1beb653026be927cab14384df6ff6d248806972",
|
"edge_sha256": "d1c26c72787d07b411f6e047863bfb135005be8d3cf2212f2f03e0d8e0326283",
|
||||||
"edges": [
|
"edges": [
|
||||||
"app -> app.runtime",
|
"app -> app.runtime",
|
||||||
"app -> app.runtime.compat",
|
"app -> app.runtime.compat",
|
||||||
@@ -4322,6 +4322,7 @@
|
|||||||
"app.modules.imdb.api -> app.runtime.cache",
|
"app.modules.imdb.api -> app.runtime.cache",
|
||||||
"app.modules.imdb.api -> app.runtime.log",
|
"app.modules.imdb.api -> app.runtime.log",
|
||||||
"app.modules.imdb.api -> app.runtime.settings",
|
"app.modules.imdb.api -> app.runtime.settings",
|
||||||
|
"app.modules.imdb.api -> app.runtime.tasks",
|
||||||
"app.modules.indexer -> app.application",
|
"app.modules.indexer -> app.application",
|
||||||
"app.modules.indexer -> app.application.site",
|
"app.modules.indexer -> app.application.site",
|
||||||
"app.modules.indexer -> app.application.site.health",
|
"app.modules.indexer -> app.application.site.health",
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ from app.modules.imdb.api import (
|
|||||||
ImdbTitle,
|
ImdbTitle,
|
||||||
)
|
)
|
||||||
from app.runtime.config import settings
|
from app.runtime.config import settings
|
||||||
|
from app.runtime.tasks import TaskRegistry
|
||||||
from app.schemas.types import MediaRecognizeType, MediaSource, MediaType
|
from app.schemas.types import MediaRecognizeType, MediaSource, MediaType
|
||||||
|
|
||||||
|
|
||||||
@@ -168,6 +169,38 @@ def test_imdb_graphql_error_is_not_cached() -> None:
|
|||||||
assert api._request.post_json.call_count == 2
|
assert api._request.post_json.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_imdb_clear_cache_registers_async_cleanup_in_running_loop() -> None:
|
||||||
|
"""同步清缓存入口在异步宿主中应登记任务,并保留原同步调用约定。"""
|
||||||
|
|
||||||
|
async def scenario() -> None:
|
||||||
|
"""验证异步缓存清理直到完成前都由宿主登记器持有。"""
|
||||||
|
api = ImdbApi()
|
||||||
|
registry = TaskRegistry()
|
||||||
|
release = asyncio.Event()
|
||||||
|
|
||||||
|
async def clear_async_cache() -> None:
|
||||||
|
"""等待测试释放,确保可以观察登记中的任务。"""
|
||||||
|
await release.wait()
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch("app.modules.imdb.api.get_task_registry", return_value=registry),
|
||||||
|
patch.object(api, "async_clear_cache", side_effect=clear_async_cache),
|
||||||
|
):
|
||||||
|
assert api.clear_cache() is None
|
||||||
|
assert [record.owner for record in registry.records] == [
|
||||||
|
"module.imdb.cache_clear"
|
||||||
|
]
|
||||||
|
|
||||||
|
release.set()
|
||||||
|
await registry.records[0].task
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
|
assert registry.records == ()
|
||||||
|
api.close()
|
||||||
|
|
||||||
|
asyncio.run(scenario())
|
||||||
|
|
||||||
|
|
||||||
def test_async_imdb_api_merges_paginated_episodes() -> None:
|
def test_async_imdb_api_merges_paginated_episodes() -> None:
|
||||||
"""IMDb 异步剧集查询应使用 GraphQL 游标合并并缓存所有分页。"""
|
"""IMDb 异步剧集查询应使用 GraphQL 游标合并并缓存所有分页。"""
|
||||||
api = ImdbApi()
|
api = ImdbApi()
|
||||||
|
|||||||
Reference in New Issue
Block a user