fix(ci): isolate startup performance resources

This commit is contained in:
jxxghp
2026-08-23 23:42:04 +08:00
parent 85439d3f9c
commit ba2eb869e2
4 changed files with 191 additions and 153 deletions
+16 -6
View File
@@ -136,12 +136,22 @@ def ensure_sites_stub() -> None:
try: try:
import app.application.site.sites # noqa: F401 本地已拉取时用真实模块 import app.application.site.sites # noqa: F401 本地已拉取时用真实模块
except (ModuleNotFoundError, ImportError): except (ModuleNotFoundError, ImportError):
from importlib.util import spec_from_loader install_sites_stub()
from types import ModuleType
stub = ModuleType("app.application.site.sites")
stub.SitesHelper = _SitesHelperStub def install_sites_stub() -> None:
stub.__spec__ = spec_from_loader("app.application.site.sites", None) """强制安装确定性的站点资源垫片,供隔离探针排除本机动态资源差异。
sys.modules["app.application.site.sites"] = stub
与 :func:`ensure_sites_stub` 的“真实资源优先”语义不同,性能与架构探针需要在开发机和
source-only CI 中使用完全相同的 import 前提,因此必须在导入被测宿主模块前覆盖资源模块。
"""
from importlib.util import spec_from_loader
from types import ModuleType
stub = ModuleType("app.application.site.sites")
setattr(stub, "SitesHelper", _SitesHelperStub)
stub.__spec__ = spec_from_loader("app.application.site.sites", None)
sys.modules["app.application.site.sites"] = stub
def ensure_optional_stub(name: str, **attrs) -> None: def ensure_optional_stub(name: str, **attrs) -> None:
+5 -2
View File
@@ -40,6 +40,9 @@ import json
import sys import sys
import time import time
from app.testing.bootstrap import install_sites_stub
install_sites_stub()
before = set(sys.modules) before = set(sys.modules)
started_at = time.perf_counter() started_at = time.perf_counter()
importlib.import_module({target!r}) importlib.import_module({target!r})
@@ -94,9 +97,9 @@ import time
from fastapi import FastAPI from fastapi import FastAPI
from app.testing.bootstrap import ensure_sites_stub from app.testing.bootstrap import install_sites_stub
ensure_sites_stub() install_sites_stub()
from app.startup import lifecycle from app.startup import lifecycle
+145 -145
View File
@@ -1,41 +1,41 @@
{ {
"schema_version": 2, "schema_version": 2,
"generated_at": "2026-08-23T15:32:22.484415+00:00", "generated_at": "2026-08-23T15:39:20.210777+00:00",
"platform": "macOS-26.5.2-arm64-arm-64bit-Mach-O", "platform": "macOS-26.5.2-arm64-arm-64bit-Mach-O",
"python": "3.14.3", "python": "3.14.3",
"repeat": 3, "repeat": 3,
"targets": { "targets": {
"app.startup.lifecycle": { "app.startup.lifecycle": {
"loaded_app_module_count": 366, "loaded_app_module_count": 355,
"max_ms": 985.623, "max_ms": 914.989,
"median_ms": 947.803, "median_ms": 909.991,
"min_ms": 932.937, "min_ms": 907.871,
"samples_ms": [ "samples_ms": [
947.803, 914.989,
985.623, 907.871,
932.937 909.991
] ]
}, },
"app.factory": { "app.factory": {
"loaded_app_module_count": 378, "loaded_app_module_count": 367,
"max_ms": 957.03, "max_ms": 916.407,
"median_ms": 950.108, "median_ms": 914.085,
"min_ms": 947.713, "min_ms": 913.804,
"samples_ms": [ "samples_ms": [
950.108, 916.407,
957.03, 913.804,
947.713 914.085
] ]
}, },
"app.main": { "app.main": {
"loaded_app_module_count": 380, "loaded_app_module_count": 369,
"max_ms": 1098.929, "max_ms": 1096.074,
"median_ms": 1095.482, "median_ms": 1051.665,
"min_ms": 1076.479, "min_ms": 1050.168,
"samples_ms": [ "samples_ms": [
1076.479, 1051.665,
1095.482, 1050.168,
1098.929 1096.074
] ]
} }
}, },
@@ -47,87 +47,87 @@
{ {
"mode": "normal", "mode": "normal",
"enabled_component_count": 23, "enabled_component_count": 23,
"startup_ms": 0.622, "startup_ms": 0.672,
"full_lifespan_ms": 0.781, "full_lifespan_ms": 0.831,
"stage_ms": { "stage_ms": {
"后台任务登记器": 0.076, "后台任务登记器": 0.081,
"数据库准备": 0.037, "数据库准备": 0.037,
"HTTP 基础能力": 0.029, "HTTP 基础能力": 0.035,
"领域依赖装配": 0.031, "领域依赖装配": 0.029,
"数据库引擎预热": 0.026,
"数据库连接预算": 0.025,
"路由": 0.022,
"模块服务": 0.02,
"插件备份恢复": 0.023,
"插件": 0.023,
"定时器": 0.024,
"监控器": 0.025,
"待处理整理回放": 0.02,
"命令服务": 0.022,
"工作流": 0.02,
"插件同步与启动收尾": 0.033
},
"threads_before": 2,
"threads_started": 2,
"threads_after": 2,
"tasks_before": 1,
"tasks_started": 2,
"tasks_after": 1,
"database_connections_started": 0
},
{
"mode": "normal",
"enabled_component_count": 23,
"startup_ms": 0.629,
"full_lifespan_ms": 0.782,
"stage_ms": {
"后台任务登记器": 0.075,
"数据库准备": 0.039,
"HTTP 基础能力": 0.029,
"领域依赖装配": 0.031,
"数据库引擎预热": 0.024,
"数据库连接预算": 0.022,
"路由": 0.023,
"模块服务": 0.021,
"插件备份恢复": 0.023,
"插件": 0.023,
"定时器": 0.025,
"监控器": 0.02,
"待处理整理回放": 0.024,
"命令服务": 0.021,
"工作流": 0.022,
"插件同步与启动收尾": 0.033
},
"threads_before": 2,
"threads_started": 2,
"threads_after": 2,
"tasks_before": 1,
"tasks_started": 2,
"tasks_after": 1,
"database_connections_started": 0
},
{
"mode": "normal",
"enabled_component_count": 23,
"startup_ms": 0.631,
"full_lifespan_ms": 0.785,
"stage_ms": {
"后台任务登记器": 0.08,
"数据库准备": 0.04,
"HTTP 基础能力": 0.031,
"领域依赖装配": 0.028,
"数据库引擎预热": 0.025, "数据库引擎预热": 0.025,
"数据库连接预算": 0.023, "数据库连接预算": 0.024,
"路由": 0.022, "路由": 0.022,
"模块服务": 0.022, "模块服务": 0.024,
"插件备份恢复": 0.022,
"插件": 0.02,
"定时器": 0.032,
"监控器": 0.022,
"待处理整理回放": 0.019,
"命令服务": 0.023,
"工作流": 0.023,
"插件同步与启动收尾": 0.033
},
"threads_before": 2,
"threads_started": 2,
"threads_after": 2,
"tasks_before": 1,
"tasks_started": 2,
"tasks_after": 1,
"database_connections_started": 0
},
{
"mode": "normal",
"enabled_component_count": 23,
"startup_ms": 0.598,
"full_lifespan_ms": 0.742,
"stage_ms": {
"后台任务登记器": 0.069,
"数据库准备": 0.042,
"HTTP 基础能力": 0.033,
"领域依赖装配": 0.031,
"数据库引擎预热": 0.025,
"数据库连接预算": 0.022,
"路由": 0.021,
"模块服务": 0.021,
"插件备份恢复": 0.021, "插件备份恢复": 0.021,
"插件": 0.025, "插件": 0.019,
"定时器": 0.022, "定时器": 0.024,
"监控器": 0.024, "监控器": 0.023,
"待处理整理回放": 0.02, "待处理整理回放": 0.019,
"命令服务": 0.024, "命令服务": 0.021,
"工作流": 0.02, "工作流": 0.023,
"插件同步与启动收尾": 0.034 "插件同步与启动收尾": 0.03
},
"threads_before": 2,
"threads_started": 2,
"threads_after": 2,
"tasks_before": 1,
"tasks_started": 2,
"tasks_after": 1,
"database_connections_started": 0
},
{
"mode": "normal",
"enabled_component_count": 23,
"startup_ms": 0.765,
"full_lifespan_ms": 0.951,
"stage_ms": {
"后台任务登记器": 0.088,
"数据库准备": 0.067,
"HTTP 基础能力": 0.094,
"领域依赖装配": 0.039,
"数据库引擎预热": 0.031,
"数据库连接预算": 0.022,
"路由": 0.024,
"模块服务": 0.027,
"插件备份恢复": 0.024,
"插件": 0.021,
"定时器": 0.026,
"监控器": 0.023,
"待处理整理回放": 0.023,
"命令服务": 0.023,
"工作流": 0.023,
"插件同步与启动收尾": 0.062
}, },
"threads_before": 2, "threads_before": 2,
"threads_started": 2, "threads_started": 2,
@@ -138,8 +138,8 @@
"database_connections_started": 0 "database_connections_started": 0
} }
], ],
"median_startup_ms": 0.629, "median_startup_ms": 0.672,
"median_full_lifespan_ms": 0.782, "median_full_lifespan_ms": 0.831,
"enabled_component_count": 23, "enabled_component_count": 23,
"enabled_components": [ "enabled_components": [
"后台任务登记器", "后台任务登记器",
@@ -172,18 +172,18 @@
{ {
"mode": "safe", "mode": "safe",
"enabled_component_count": 11, "enabled_component_count": 11,
"startup_ms": 0.597, "startup_ms": 0.465,
"full_lifespan_ms": 0.772, "full_lifespan_ms": 0.605,
"stage_ms": { "stage_ms": {
"后台任务登记器": 0.075, "后台任务登记器": 0.072,
"数据库准备": 0.044, "数据库准备": 0.047,
"HTTP 基础能力": 0.033, "HTTP 基础能力": 0.035,
"领域依赖装配": 0.059, "领域依赖装配": 0.03,
"数据库引擎预热": 0.061, "数据库引擎预热": 0.024,
"数据库连接预算": 0.032, "数据库连接预算": 0.022,
"路由": 0.027, "路由": 0.023,
"模块服务": 0.046, "模块服务": 0.022,
"插件同步与启动收尾": 0.044 "插件同步与启动收尾": 0.031
}, },
"threads_before": 2, "threads_before": 2,
"threads_started": 2, "threads_started": 2,
@@ -196,42 +196,42 @@
{ {
"mode": "safe", "mode": "safe",
"enabled_component_count": 11, "enabled_component_count": 11,
"startup_ms": 0.461, "startup_ms": 0.478,
"full_lifespan_ms": 0.642, "full_lifespan_ms": 0.647,
"stage_ms": { "stage_ms": {
"后台任务登记器": 0.076, "后台任务登记器": 0.083,
"数据库准备": 0.037, "数据库准备": 0.038,
"HTTP 基础能力": 0.034,
"领域依赖装配": 0.025,
"数据库引擎预热": 0.022,
"数据库连接预算": 0.025,
"路由": 0.025,
"模块服务": 0.025,
"插件同步与启动收尾": 0.052
},
"threads_before": 2,
"threads_started": 2,
"threads_after": 2,
"tasks_before": 1,
"tasks_started": 2,
"tasks_after": 1,
"database_connections_started": 0
},
{
"mode": "safe",
"enabled_component_count": 11,
"startup_ms": 0.477,
"full_lifespan_ms": 0.651,
"stage_ms": {
"后台任务登记器": 0.074,
"数据库准备": 0.044,
"HTTP 基础能力": 0.029, "HTTP 基础能力": 0.029,
"领域依赖装配": 0.029, "领域依赖装配": 0.029,
"数据库引擎预热": 0.026, "数据库引擎预热": 0.027,
"数据库连接预算": 0.023, "数据库连接预算": 0.023,
"路由": 0.024, "路由": 0.022,
"模块服务": 0.023, "模块服务": 0.021,
"插件同步与启动收尾": 0.06 "插件同步与启动收尾": 0.057
},
"threads_before": 2,
"threads_started": 2,
"threads_after": 2,
"tasks_before": 1,
"tasks_started": 2,
"tasks_after": 1,
"database_connections_started": 0
},
{
"mode": "safe",
"enabled_component_count": 11,
"startup_ms": 0.483,
"full_lifespan_ms": 0.659,
"stage_ms": {
"后台任务登记器": 0.077,
"数据库准备": 0.039,
"HTTP 基础能力": 0.034,
"领域依赖装配": 0.035,
"数据库引擎预热": 0.025,
"数据库连接预算": 0.026,
"路由": 0.025,
"模块服务": 0.024,
"插件同步与启动收尾": 0.059
}, },
"threads_before": 2, "threads_before": 2,
"threads_started": 2, "threads_started": 2,
@@ -242,8 +242,8 @@
"database_connections_started": 0 "database_connections_started": 0
} }
], ],
"median_startup_ms": 0.483, "median_startup_ms": 0.477,
"median_full_lifespan_ms": 0.659, "median_full_lifespan_ms": 0.647,
"enabled_component_count": 11, "enabled_component_count": 11,
"enabled_components": [ "enabled_components": [
"后台任务登记器", "后台任务登记器",
+25
View File
@@ -2,12 +2,37 @@
from __future__ import annotations from __future__ import annotations
import builtins import builtins
import sys
import types import types
from pathlib import Path from pathlib import Path
from app.testing import bootstrap from app.testing import bootstrap
def test_install_sites_stub_replaces_loaded_dynamic_resource(monkeypatch):
"""隔离探针必须覆盖本机资源模块,保证 source-only CI 与开发机前提一致。"""
real_module = types.ModuleType("app.application.site.sites")
real_module.SitesHelper = object
monkeypatch.setitem(sys.modules, "app.application.site.sites", real_module)
bootstrap.install_sites_stub()
installed = sys.modules["app.application.site.sites"]
assert installed is not real_module
assert installed.SitesHelper is bootstrap._SitesHelperStub
def test_ensure_sites_stub_preserves_loaded_dynamic_resource(monkeypatch):
"""常规测试引导仍应优先复用已经加载的真实站点资源。"""
real_module = types.ModuleType("app.application.site.sites")
real_module.SitesHelper = object
monkeypatch.setitem(sys.modules, "app.application.site.sites", real_module)
bootstrap.ensure_sites_stub()
assert sys.modules["app.application.site.sites"] is real_module
def test_isolate_config_cleanup_uses_loaded_db_module_without_late_import(monkeypatch): def test_isolate_config_cleanup_uses_loaded_db_module_without_late_import(monkeypatch):
"""清理回调只读取已加载模块,避免解释器关停期触发二次导入。""" """清理回调只读取已加载模块,避免解释器关停期触发二次导入。"""
captured = {} captured = {}