fix(imdb): register async cache cleanup

This commit is contained in:
jxxghp
2026-08-23 15:29:42 +08:00
parent af5a330d0f
commit ba193cce08
5 changed files with 52 additions and 4 deletions
+11 -2
View File
@@ -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
View File
@@ -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",
+33
View File
@@ -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()