mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-04 23:17:20 +08:00
fix(ci): isolate startup performance resources
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
@@ -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
@@ -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": [
|
||||||
"后台任务登记器",
|
"后台任务登记器",
|
||||||
|
|||||||
@@ -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 = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user