refactor: unify host threadpool boundary

This commit is contained in:
jxxghp
2026-08-24 03:28:10 +08:00
parent 22848eeda2
commit 371383f0a8
15 changed files with 59 additions and 21 deletions
+1 -2
View File
@@ -3,8 +3,7 @@ import re
import threading import threading
from typing import Any, Optional, Tuple from typing import Any, Optional, Tuple
from fastapi.concurrency import run_in_threadpool from app.runtime.execution import run_in_threadpool
from app.agent.policy.sanitizer import sanitize_for_host from app.agent.policy.sanitizer import sanitize_for_host
from app.chain import ChainBase from app.chain import ChainBase
from app.runtime.log import logger from app.runtime.log import logger
+1 -1
View File
@@ -11,7 +11,6 @@ from dataclasses import dataclass
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Any, Awaitable, Callable, Dict, List, Optional from typing import Any, Awaitable, Callable, Dict, List, Optional
from fastapi.concurrency import run_in_threadpool
from langchain.agents import create_agent from langchain.agents import create_agent
from langchain_core.messages import ( # noqa: F401 from langchain_core.messages import ( # noqa: F401
HumanMessage, HumanMessage,
@@ -21,6 +20,7 @@ from langchain_core.messages import ( # noqa: F401
from langgraph.checkpoint.memory import InMemorySaver from langgraph.checkpoint.memory import InMemorySaver
from app.runtime.execution import run_in_threadpool
from app.agent.callback import StreamingHandler from app.agent.callback import StreamingHandler
from app.agent.contracts import ReplyMode, build_display_message from app.agent.contracts import ReplyMode, build_display_message
from app.agent.llm.helper import LLMHelper from app.agent.llm.helper import LLMHelper
+1 -1
View File
@@ -14,9 +14,9 @@ from typing import Any, AsyncIterator, Awaitable, Callable, Optional, Union
import aiofiles import aiofiles
from fastapi import Depends, File, Form, HTTPException, Request, UploadFile, status from fastapi import Depends, File, Form, HTTPException, Request, UploadFile, status
from fastapi.concurrency import run_in_threadpool
from fastapi.responses import FileResponse, StreamingResponse from fastapi.responses import FileResponse, StreamingResponse
from app.runtime.execution import run_in_threadpool
from app.schemas.agent import AgentChatDisplaySaveRequest as _SchemaAgentChatDisplaySaveRequest from app.schemas.agent import AgentChatDisplaySaveRequest as _SchemaAgentChatDisplaySaveRequest
from app.schemas.agent import AgentChatSessionDetail as _SchemaAgentChatSessionDetail from app.schemas.agent import AgentChatSessionDetail as _SchemaAgentChatSessionDetail
from app.schemas.agent import AgentChatSessionSummary as _SchemaAgentChatSessionSummary from app.schemas.agent import AgentChatSessionSummary as _SchemaAgentChatSessionSummary
+1 -1
View File
@@ -2,8 +2,8 @@ from pathlib import Path
from typing import Any, List, Optional, Annotated from typing import Any, List, Optional, Annotated
from fastapi import Depends from fastapi import Depends
from fastapi.concurrency import run_in_threadpool
from app.runtime.execution import run_in_threadpool
from app.schemas.dashboard import DashboardMemoryInfo as _SchemaDashboardMemoryInfo from app.schemas.dashboard import DashboardMemoryInfo as _SchemaDashboardMemoryInfo
from app.schemas.dashboard import DashboardSystemInfo as _SchemaDashboardSystemInfo from app.schemas.dashboard import DashboardSystemInfo as _SchemaDashboardSystemInfo
from app.schemas.dashboard import DownloaderInfo as _SchemaDownloaderInfo from app.schemas.dashboard import DownloaderInfo as _SchemaDownloaderInfo
+1 -1
View File
@@ -6,10 +6,10 @@ from typing import Annotated, Any, Dict, List, Optional
import aiofiles import aiofiles
from anyio import Path as AsyncPath from anyio import Path as AsyncPath
from fastapi import Depends, Header, HTTPException, Security from fastapi import Depends, Header, HTTPException, Security
from fastapi.concurrency import run_in_threadpool
from starlette import status from starlette import status
from starlette.responses import StreamingResponse from starlette.responses import StreamingResponse
from app.runtime.execution import run_in_threadpool
from app.schemas.common import JsonObject as _SchemaJsonObject from app.schemas.common import JsonObject as _SchemaJsonObject
from app.schemas.plugin import Plugin as _SchemaPlugin from app.schemas.plugin import Plugin as _SchemaPlugin
from app.schemas.plugin import PluginDashboard as _SchemaPluginDashboard from app.schemas.plugin import PluginDashboard as _SchemaPluginDashboard
+1 -2
View File
@@ -7,8 +7,7 @@
import copy import copy
from typing import Optional from typing import Optional
from fastapi.concurrency import run_in_threadpool from app.runtime.execution import run_in_threadpool
from app.adapters.external.server import MoviePilotServerHelper from app.adapters.external.server import MoviePilotServerHelper
from app.application.configuration import get_configured_system_config from app.application.configuration import get_configured_system_config
from app.domain.context import MediaInfo, MusicInfo from app.domain.context import MediaInfo, MusicInfo
+1 -2
View File
@@ -3,8 +3,7 @@ from pathlib import Path
from threading import Lock from threading import Lock
from typing import Any, Iterable, List, Optional, Tuple, Union from typing import Any, Iterable, List, Optional, Tuple, Union
from fastapi.concurrency import run_in_threadpool from app.runtime.execution import run_in_threadpool
from app.schemas.event import MediaRecognizeConvertEventData as _SchemaMediaRecognizeConvertEventData from app.schemas.event import MediaRecognizeConvertEventData as _SchemaMediaRecognizeConvertEventData
from app.chain import ChainBase from app.chain import ChainBase
from app.chain.acoustid import AcoustIdChain from app.chain.acoustid import AcoustIdChain
+1 -2
View File
@@ -10,8 +10,7 @@ from typing import AsyncIterator, Any, Dict, Iterable, Tuple
from typing import List, Optional from typing import List, Optional
from unicodedata import normalize from unicodedata import normalize
from fastapi.concurrency import run_in_threadpool from app.runtime.execution import run_in_threadpool
from app.chain import ChainBase from app.chain import ChainBase
from app.chain.media import MediaChain from app.chain.media import MediaChain
from app.runtime.config import global_vars from app.runtime.config import global_vars
+1 -2
View File
@@ -9,8 +9,7 @@ from pathlib import Path
from typing import Any, Optional, Tuple, Union from typing import Any, Optional, Tuple, Union
from uuid import UUID from uuid import UUID
from fastapi.concurrency import run_in_threadpool from app.runtime.execution import run_in_threadpool
from app.runtime.settings import RuntimeSettingsCompat from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat() settings = RuntimeSettingsCompat()
+1 -1
View File
@@ -6,8 +6,8 @@ from typing import Optional, List, Dict, Any, Tuple, Union
import discord import discord
from discord import app_commands from discord import app_commands
from fastapi.concurrency import run_in_threadpool
from app.runtime.execution import run_in_threadpool
from app.runtime.settings import RuntimeSettingsCompat from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat() settings = RuntimeSettingsCompat()
+1 -1
View File
@@ -5,10 +5,10 @@ from typing import Any, Optional
from typing import List from typing import List
from urllib.parse import quote, urlparse, parse_qs from urllib.parse import quote, urlparse, parse_qs
from fastapi.concurrency import run_in_threadpool
from jinja2 import Template from jinja2 import Template
from pyquery import PyQuery from pyquery import PyQuery
from app.runtime.execution import run_in_threadpool
from app.runtime.settings import RuntimeSettingsCompat from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat() settings = RuntimeSettingsCompat()
@@ -104,6 +104,8 @@ Event Contract Registry 是 53 个事件的逐项机器清单。下表按相同
的同步/异步调用合同。 的同步/异步调用合同。
- 插件安装快照、取消补偿和临时约束文件清理统一复用 `runtime.execution.await_task_to_terminal`,连续取消 - 插件安装快照、取消补偿和临时约束文件清理统一复用 `runtime.execution.await_task_to_terminal`,连续取消
不再由 Application 与 Adapter 各自维护近似循环;数据库 worker 的可中断队列等待仍保留独立职责。 不再由 Application 与 Adapter 各自维护近似循环;数据库 worker 的可中断队列等待仍保留独立职责。
- canonical 宿主的同步函数异步桥接统一从 `runtime.execution.run_in_threadpool` 进入 AnyIO 线程池并传播
contextFastAPI/Starlette 同名 helper 不再作为第二个导入入口,插件和精确兼容目录不受此门禁约束。
### Transfer pending / 文件整理 ### Transfer pending / 文件整理
@@ -6,7 +6,7 @@
> 审计范围:宿主后端;排除 `app/plugins/**` 运行时插件副本 > 审计范围:宿主后端;排除 `app/plugins/**` 运行时插件副本
> 规范优先级:`AGENTS.md``docs/rules/` 高于本文 > 规范优先级:`AGENTS.md``docs/rules/` 高于本文
> 相关文档:`docs/architecture-overview.md``docs/refactor/backend-architecture-governance.md``docs/refactor/backend-module-refactor-compatibility.md` > 相关文档:`docs/architecture-overview.md``docs/refactor/backend-architecture-governance.md``docs/refactor/backend-module-refactor-compatibility.md`
> 实施进度:阶段 0~6 的宿主架构能力已完成收口;API/Application 公共复杂度基线已清零,启动组合根的 SystemConfigOper 构造点已由 14 降至 1;API 进程内后台任务已完成首批统一登记,插件仓适配和 Outbox 外围扩展仍按风险切片推进。Model/Base 查询与写装饰器、legacy 隐式会话外壳均已清零,插件 SDK 也不再导出宿主 Model。2026-08-23 的长期整改阶段 0 已恢复宿主、启动性能、官方插件和 SDK 契约门禁的可信基线;阶段 1a 已补齐 TaskRegistry owner 零债务门禁和诚实的关停超时语义;阶段 1b1 已收口整理 worker、pending 回放、失败通知、进程内 AI 重试、插件监控与事件投递的生命周期所有权;2026-08-24 的阶段 2 已将 212 个已观察宿主模块方法的 legacy aggregation 清零,并补齐可执行 fanout 与下载器文件 DTO 边界;阶段 3 已将消息交互和远程命令的订阅删除统一到 Application/UoW/outbox,宿主不再调用裸线程统计入口;阶段 4 已统一七种消息渠道的宿主回环与后台执行边界;阶段 5 已补齐事件窗口聚合任务的生命周期所有权;阶段 6 已统一插件文件操作的取消完成语义;阶段 7 已统一插件协程补偿的终态等待。 > 实施进度:阶段 0~6 的宿主架构能力已完成收口;API/Application 公共复杂度基线已清零,启动组合根的 SystemConfigOper 构造点已由 14 降至 1;API 进程内后台任务已完成首批统一登记,插件仓适配和 Outbox 外围扩展仍按风险切片推进。Model/Base 查询与写装饰器、legacy 隐式会话外壳均已清零,插件 SDK 也不再导出宿主 Model。2026-08-23 的长期整改阶段 0 已恢复宿主、启动性能、官方插件和 SDK 契约门禁的可信基线;阶段 1a 已补齐 TaskRegistry owner 零债务门禁和诚实的关停超时语义;阶段 1b1 已收口整理 worker、pending 回放、失败通知、进程内 AI 重试、插件监控与事件投递的生命周期所有权;2026-08-24 的阶段 2 已将 212 个已观察宿主模块方法的 legacy aggregation 清零,并补齐可执行 fanout 与下载器文件 DTO 边界;阶段 3 已将消息交互和远程命令的订阅删除统一到 Application/UoW/outbox,宿主不再调用裸线程统计入口;阶段 4 已统一七种消息渠道的宿主回环与后台执行边界;阶段 5 已补齐事件窗口聚合任务的生命周期所有权;阶段 6 已统一插件文件操作的取消完成语义;阶段 7 已统一插件协程补偿的终态等待;阶段 8 已统一宿主同步函数的异步线程池入口
## 当前复核结论(2026-08-24 ## 当前复核结论(2026-08-24
@@ -15,7 +15,7 @@
### 长期整改阶段 0:治理门禁恢复(2026-08-23 ### 长期整改阶段 0:治理门禁恢复(2026-08-23
- 宿主依赖基线已审查 TaskRegistry、有界后台 owner 与插件变更准入接入后的语义差异:当前为 `806` 个模块、`6532` 条内部导入边,12 组重点禁止边继续全部为 `0`,唯一非平凡 SCC 仍是隔离的 TMDB 移植包。 - 宿主依赖基线已审查 TaskRegistry、有界后台 owner 与插件变更准入接入后的语义差异:当前为 `806` 个模块、`6544` 条内部导入边,12 组重点禁止边继续全部为 `0`,唯一非平凡 SCC 仍是隔离的 TMDB 移植包。
- 启动性能探针会在隔离生命周期中真实创建并释放 TaskRegistrynormal/safe 组件数分别为 `23`/`11`,CI 只读检查使用稳定的宿主模块集合和生命周期组件顺序,不再把 Python/平台模块数量当作硬合同。 - 启动性能探针会在隔离生命周期中真实创建并释放 TaskRegistrynormal/safe 组件数分别为 `23`/`11`,CI 只读检查使用稳定的宿主模块集合和生命周期组件顺序,不再把 Python/平台模块数量当作硬合同。
- 官方插件快照覆盖 `plugins.v3``plugins.v2` 以及 V3 实际会从 `package.json` 回退加载的 31 个默认实现;`app/plugins/**` 仍只是宿主运行副本,不进入扫描。 - 官方插件快照覆盖 `plugins.v3``plugins.v2` 以及 V3 实际会从 `package.json` 回退加载的 31 个默认实现;`app/plugins/**` 仍只是宿主运行副本,不进入扫描。
- SDK 快照以各模块显式 `__all__` 为公开合同,能够记录赋值别名;`typing``__future__` 等实现期导入不再被误冻结,既有数据库备份门面已补精确导出清单。 - SDK 快照以各模块显式 `__all__` 为公开合同,能够记录赋值别名;`typing``__future__` 等实现期导入不再被误冻结,既有数据库备份门面已补精确导出清单。
@@ -112,7 +112,7 @@
- 继续采用单进程控制面是正确选择,不建议现在拆成微服务;插件、调度器、工作流、事件和数据库共享进程内状态,拆分会放大部署、事务和兼容成本。 - 继续采用单进程控制面是正确选择,不建议现在拆成微服务;插件、调度器、工作流、事件和数据库共享进程内状态,拆分会放大部署、事务和兼容成本。
- `foundation/domain/runtime/adapters/application/chain/api/startup` 的职责方向基本成立;宿主架构基线、复杂度 ratchet、异步阻塞 ratchet 当前均通过。 - `foundation/domain/runtime/adapters/application/chain/api/startup` 的职责方向基本成立;宿主架构基线、复杂度 ratchet、异步阻塞 ratchet 当前均通过。
- 依赖图当前为 `806` 个 Python 模块、`6532` 条内部导入边;唯一非平凡 SCC 位于隔离的 TMDB 第三方移植包内部,不应为了指标归零重写。 - 依赖图当前为 `806` 个 Python 模块、`6544` 条内部导入边;唯一非平凡 SCC 位于隔离的 TMDB 第三方移植包内部,不应为了指标归零重写。
- 当前主要风险已经从“目录和依赖失控”转移到运行时协议、后台副作用的可靠性和遗留兼容面。换言之,下一阶段重点应是**语义收口和可验证性**,而不是继续搬文件或机械拆大文件。 - 当前主要风险已经从“目录和依赖失控”转移到运行时协议、后台副作用的可靠性和遗留兼容面。换言之,下一阶段重点应是**语义收口和可验证性**,而不是继续搬文件或机械拆大文件。
综合评价:架构方向可持续,生产可用性较高;可演进性仍处于中等水平。现阶段没有静态审计发现必须立即推倒重来的 P0 架构问题,但存在需要按 P1/P2 计划治理的真实债务。 综合评价:架构方向可持续,生产可用性较高;可演进性仍处于中等水平。现阶段没有静态审计发现必须立即推倒重来的 P0 架构问题,但存在需要按 P1/P2 计划治理的真实债务。
@@ -140,6 +140,8 @@
文件 worker 到达终态后再传播,避免提前释放 mutation owner;旧模块内私有名称保留为 canonical 别名。 文件 worker 到达终态后再传播,避免提前释放 mutation owner;旧模块内私有名称保留为 canonical 别名。
插件安装快照、取消补偿与市场临时文件清理原有两套协程终态循环也已合并为 插件安装快照、取消补偿与市场临时文件清理原有两套协程终态循环也已合并为
`await_task_to_terminal`;数据库 worker 的 interruptible 等待属于队列 owner,未被机械合并。 `await_task_to_terminal`;数据库 worker 的 interruptible 等待属于队列 owner,未被机械合并。
canonical 宿主原有 11 处 FastAPI `run_in_threadpool` 导入也已统一到 `runtime.execution`AST 门禁
防止再次出现框架直连;模块局部符号及调用参数不变,不改变插件运行和 monkeypatch 接缝。
2. **Model/Base 的数据库装饰器和隐式会话 ABI 已全部清零。** 查询、写事务和 `legacy_*` 装饰器均为 `0`;所有 Model `db` 参数要求显式 Session,Base CRUD 仅在调用方事务内查询或 stage。可无会话构造的入口统一留在 Oper,经组合根事务执行器运行;插件 SDK 不再导出宿主 Model。后续重点转为减少 ORM 对象跨层流转,并保持 Model 隐式事务零回退。 2. **Model/Base 的数据库装饰器和隐式会话 ABI 已全部清零。** 查询、写事务和 `legacy_*` 装饰器均为 `0`;所有 Model `db` 参数要求显式 Session,Base CRUD 仅在调用方事务内查询或 stage。可无会话构造的入口统一留在 Oper,经组合根事务执行器运行;插件 SDK 不再导出宿主 Model。后续重点转为减少 ORM 对象跨层流转,并保持 Model 隐式事务零回退。
Oper 内部的执行入口也已统一:最后一处 `AgentTaskOper` 直接 transaction runner 调用已迁入 Oper 内部的执行入口也已统一:最后一处 `AgentTaskOper` 直接 transaction runner 调用已迁入
+14 -2
View File
@@ -13,8 +13,8 @@
"runtime_to_db": [], "runtime_to_db": [],
"workflow_to_db": [] "workflow_to_db": []
}, },
"edge_count": 6532, "edge_count": 6544,
"edge_sha256": "3fdba2c6862f9ea28ab4f059c04dd6bb819b86fa9d9fb74d09a0cfcf69f6b711", "edge_sha256": "44812359c634235d327fdc499bc0ccf93e24923fba24bed4a29fc14adc26bf84",
"edges": [ "edges": [
"app -> app.runtime", "app -> app.runtime",
"app -> app.runtime.compat", "app -> app.runtime.compat",
@@ -180,6 +180,7 @@
"app.agent.callback -> app.agent.policy.sanitizer", "app.agent.callback -> app.agent.policy.sanitizer",
"app.agent.callback -> app.chain", "app.agent.callback -> app.chain",
"app.agent.callback -> app.runtime", "app.agent.callback -> app.runtime",
"app.agent.callback -> app.runtime.execution",
"app.agent.callback -> app.runtime.log", "app.agent.callback -> app.runtime.log",
"app.agent.callback -> app.schemas", "app.agent.callback -> app.schemas",
"app.agent.callback -> app.schemas.message", "app.agent.callback -> app.schemas.message",
@@ -363,6 +364,7 @@
"app.agent.orchestrator -> app.foundation.identity", "app.agent.orchestrator -> app.foundation.identity",
"app.agent.orchestrator -> app.runtime", "app.agent.orchestrator -> app.runtime",
"app.agent.orchestrator -> app.runtime.events", "app.agent.orchestrator -> app.runtime.events",
"app.agent.orchestrator -> app.runtime.execution",
"app.agent.orchestrator -> app.runtime.log", "app.agent.orchestrator -> app.runtime.log",
"app.agent.orchestrator -> app.runtime.observability", "app.agent.orchestrator -> app.runtime.observability",
"app.agent.orchestrator -> app.runtime.settings", "app.agent.orchestrator -> app.runtime.settings",
@@ -1674,6 +1676,7 @@
"app.api.endpoints.agent -> app.runtime", "app.api.endpoints.agent -> app.runtime",
"app.api.endpoints.agent -> app.runtime.config", "app.api.endpoints.agent -> app.runtime.config",
"app.api.endpoints.agent -> app.runtime.events", "app.api.endpoints.agent -> app.runtime.events",
"app.api.endpoints.agent -> app.runtime.execution",
"app.api.endpoints.agent -> app.runtime.localization", "app.api.endpoints.agent -> app.runtime.localization",
"app.api.endpoints.agent -> app.runtime.log", "app.api.endpoints.agent -> app.runtime.log",
"app.api.endpoints.agent -> app.schemas", "app.api.endpoints.agent -> app.schemas",
@@ -1760,6 +1763,8 @@
"app.api.endpoints.dashboard -> app.chain", "app.api.endpoints.dashboard -> app.chain",
"app.api.endpoints.dashboard -> app.chain.dashboard", "app.api.endpoints.dashboard -> app.chain.dashboard",
"app.api.endpoints.dashboard -> app.chain.storage", "app.api.endpoints.dashboard -> app.chain.storage",
"app.api.endpoints.dashboard -> app.runtime",
"app.api.endpoints.dashboard -> app.runtime.execution",
"app.api.endpoints.dashboard -> app.schemas", "app.api.endpoints.dashboard -> app.schemas",
"app.api.endpoints.dashboard -> app.schemas.dashboard", "app.api.endpoints.dashboard -> app.schemas.dashboard",
"app.api.endpoints.dashboard -> app.schemas.response", "app.api.endpoints.dashboard -> app.schemas.response",
@@ -2095,6 +2100,7 @@
"app.api.endpoints.plugin -> app.application.scheduling", "app.api.endpoints.plugin -> app.application.scheduling",
"app.api.endpoints.plugin -> app.runtime", "app.api.endpoints.plugin -> app.runtime",
"app.api.endpoints.plugin -> app.runtime.cache", "app.api.endpoints.plugin -> app.runtime.cache",
"app.api.endpoints.plugin -> app.runtime.execution",
"app.api.endpoints.plugin -> app.runtime.extensions", "app.api.endpoints.plugin -> app.runtime.extensions",
"app.api.endpoints.plugin -> app.runtime.extensions.plugin", "app.api.endpoints.plugin -> app.runtime.extensions.plugin",
"app.api.endpoints.plugin -> app.runtime.extensions.plugin.contracts", "app.api.endpoints.plugin -> app.runtime.extensions.plugin.contracts",
@@ -2985,6 +2991,7 @@
"app.chain._recognition -> app.runtime", "app.chain._recognition -> app.runtime",
"app.chain._recognition -> app.runtime.cache", "app.chain._recognition -> app.runtime.cache",
"app.chain._recognition -> app.runtime.events", "app.chain._recognition -> app.runtime.events",
"app.chain._recognition -> app.runtime.execution",
"app.chain._recognition -> app.runtime.log", "app.chain._recognition -> app.runtime.log",
"app.chain._recognition -> app.schemas", "app.chain._recognition -> app.schemas",
"app.chain._recognition -> app.schemas.media", "app.chain._recognition -> app.schemas.media",
@@ -3152,6 +3159,7 @@
"app.chain.media -> app.runtime", "app.chain.media -> app.runtime",
"app.chain.media -> app.runtime.cache", "app.chain.media -> app.runtime.cache",
"app.chain.media -> app.runtime.events", "app.chain.media -> app.runtime.events",
"app.chain.media -> app.runtime.execution",
"app.chain.media -> app.runtime.log", "app.chain.media -> app.runtime.log",
"app.chain.media -> app.schemas", "app.chain.media -> app.schemas",
"app.chain.media -> app.schemas.event", "app.chain.media -> app.schemas.event",
@@ -3272,6 +3280,7 @@
"app.chain.search -> app.runtime", "app.chain.search -> app.runtime",
"app.chain.search -> app.runtime.config", "app.chain.search -> app.runtime.config",
"app.chain.search -> app.runtime.events", "app.chain.search -> app.runtime.events",
"app.chain.search -> app.runtime.execution",
"app.chain.search -> app.runtime.log", "app.chain.search -> app.runtime.log",
"app.chain.search -> app.runtime.progress", "app.chain.search -> app.runtime.progress",
"app.chain.search -> app.schemas", "app.chain.search -> app.schemas",
@@ -3966,6 +3975,7 @@
"app.modules.acoustid -> app.adapters.network.http", "app.modules.acoustid -> app.adapters.network.http",
"app.modules.acoustid -> app.modules", "app.modules.acoustid -> app.modules",
"app.modules.acoustid -> app.runtime", "app.modules.acoustid -> app.runtime",
"app.modules.acoustid -> app.runtime.execution",
"app.modules.acoustid -> app.runtime.log", "app.modules.acoustid -> app.runtime.log",
"app.modules.acoustid -> app.runtime.settings", "app.modules.acoustid -> app.runtime.settings",
"app.modules.acoustid -> app.schemas", "app.modules.acoustid -> app.schemas",
@@ -4054,6 +4064,7 @@
"app.modules.discord.discord -> app.foundation", "app.modules.discord.discord -> app.foundation",
"app.modules.discord.discord -> app.foundation.size", "app.modules.discord.discord -> app.foundation.size",
"app.modules.discord.discord -> app.runtime", "app.modules.discord.discord -> app.runtime",
"app.modules.discord.discord -> app.runtime.execution",
"app.modules.discord.discord -> app.runtime.log", "app.modules.discord.discord -> app.runtime.log",
"app.modules.discord.discord -> app.runtime.settings", "app.modules.discord.discord -> app.runtime.settings",
"app.modules.discord.discord -> app.schemas", "app.modules.discord.discord -> app.schemas",
@@ -4589,6 +4600,7 @@
"app.modules.indexer.spider -> app.foundation.temporal", "app.modules.indexer.spider -> app.foundation.temporal",
"app.modules.indexer.spider -> app.foundation.url", "app.modules.indexer.spider -> app.foundation.url",
"app.modules.indexer.spider -> app.runtime", "app.modules.indexer.spider -> app.runtime",
"app.modules.indexer.spider -> app.runtime.execution",
"app.modules.indexer.spider -> app.runtime.log", "app.modules.indexer.spider -> app.runtime.log",
"app.modules.indexer.spider -> app.runtime.settings", "app.modules.indexer.spider -> app.runtime.settings",
"app.modules.indexer.spider -> app.schemas", "app.modules.indexer.spider -> app.schemas",
+27
View File
@@ -1,7 +1,9 @@
"""运行时同步 worker 的取消与容量合同回归。""" """运行时同步 worker 的取消与容量合同回归。"""
import asyncio import asyncio
import ast
import threading import threading
from pathlib import Path
import pytest import pytest
from anyio.to_thread import current_default_thread_limiter from anyio.to_thread import current_default_thread_limiter
@@ -14,6 +16,31 @@ from app.runtime.execution import (
) )
PROJECT_ROOT = Path(__file__).resolve().parents[1]
def test_host_uses_canonical_threadpool_boundary() -> None:
"""canonical 宿主不得重新直连框架线程池 helper。"""
violations: list[str] = []
for path in sorted((PROJECT_ROOT / "app").rglob("*.py")):
relative_path = path.relative_to(PROJECT_ROOT).as_posix()
if relative_path.startswith(
("app/plugins/", "app/runtime/compat/", "app/sdk/", "app/testing/")
):
continue
tree = ast.parse(path.read_text(encoding="utf-8-sig"), filename=str(path))
for node in ast.walk(tree):
if not isinstance(node, ast.ImportFrom) or node.module not in {
"fastapi.concurrency",
"starlette.concurrency",
}:
continue
if any(alias.name == "run_in_threadpool" for alias in node.names):
violations.append(f"{relative_path}:{node.lineno}")
assert violations == []
def test_plugin_file_adapters_share_runtime_completion_contract() -> None: def test_plugin_file_adapters_share_runtime_completion_contract() -> None:
"""市场与插件包适配器不得各自维护另一套线程取消实现。""" """市场与插件包适配器不得各自维护另一套线程取消实现。"""
assert market_adapter._await_thread_operation is run_in_threadpool_to_completion assert market_adapter._await_thread_operation is run_in_threadpool_to_completion