From 29583b374201c8c186b475016118deea24ecc9cc Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Mon, 21 Sep 2026 10:39:38 +0800 Subject: [PATCH] fix(lifecycle): stop scheduler before task registry (#6746) --- app/startup/lifecycle/__init__.py | 5 +++-- tests/test_lifecycle_shutdown.py | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/startup/lifecycle/__init__.py b/app/startup/lifecycle/__init__.py index 8f5e6f0ff..8470ac625 100644 --- a/app/startup/lifecycle/__init__.py +++ b/app/startup/lifecycle/__init__.py @@ -399,7 +399,7 @@ def build_lifecycle_components(app: FastAPI) -> tuple[LifecycleComponent, ...]: start=lambda: initialize_task_registry(app), stop=lambda: stop_task_registry(app), start_order=5, - stop_order=5, + stop_order=6, start_timeout_seconds=30, stop_timeout_seconds=60, stop_failure=LifecycleFailurePolicy.FAIL_FAST, @@ -554,7 +554,8 @@ def build_lifecycle_components(app: FastAPI) -> tuple[LifecycleComponent, ...]: start=init_scheduler, stop=stop_scheduler, start_order=100, - stop_order=50, + # 调度器先于任务登记器关闭,确保取消中的任务完成真实收尾后再封口。 + stop_order=5, start_timeout_seconds=120, stop_timeout_seconds=120, stop_failure=LifecycleFailurePolicy.FAIL_FAST, diff --git a/tests/test_lifecycle_shutdown.py b/tests/test_lifecycle_shutdown.py index d1aa52455..7c95729e7 100644 --- a/tests/test_lifecycle_shutdown.py +++ b/tests/test_lifecycle_shutdown.py @@ -378,11 +378,11 @@ def test_lifespan_settles_plugin_handlers_before_legacy_hooks(monkeypatch) -> No _ORDERED_SHUTDOWN_STEPS = ( + "stop_scheduler", "stop_plugin_monitor", "backup_plugins", "stop_workflow", "stop_monitor", - "stop_scheduler", "stop_agent", "stop_transfer", "quiesce_plugins", @@ -474,8 +474,9 @@ def test_task_registry_nonconvergence_blocks_all_dependency_release(monkeypatch) asyncio.run(run_lifespan()) shutdown.assert_awaited_once_with(timeout_seconds=30.0) + _assert_completed_once(shutdown_steps["stop_scheduler"]) for name, step in shutdown_steps.items(): - if name == "logger": + if name in {"logger", "stop_scheduler"}: _assert_completed_once(step) else: step.assert_not_called() @@ -533,8 +534,9 @@ def test_plugin_settlement_cannot_bypass_task_registry_shutdown_budget( asyncio.run(run_lifespan()) shutdown.assert_awaited_once_with(timeout_seconds=30.0) + _assert_completed_once(shutdown_steps["stop_scheduler"]) for name, step in shutdown_steps.items(): - if name == "logger": + if name in {"logger", "stop_scheduler"}: _assert_completed_once(step) else: step.assert_not_called() @@ -712,13 +714,13 @@ def test_lifecycle_manifest_declares_normal_and_safe_mode_order() -> None: ] assert normal_stop == [ "停止信号", + "定时器", "后台任务登记器", "插件变更监控", "插件备份", "工作流", "命令服务", "监控器", - "定时器", "AI智能体会话", "整理后台服务", "插件事件入口",