fix(database): decouple worker errors from application

This commit is contained in:
InfinityPacer
2026-08-23 14:26:42 +08:00
parent d16ceb5804
commit d2b72f5729
14 changed files with 53 additions and 32 deletions
+13 -7
View File
@@ -2467,14 +2467,15 @@ class PluginHelper(metaclass=WeakSingleton):
"""异步恢复主程序运行依赖,避免修复命令绕过可取消进程边界。""" """异步恢复主程序运行依赖,避免修复命令绕过可取消进程边界。"""
repair_target = snapshot_file repair_target = snapshot_file
repair_desc = "主程序依赖快照" repair_desc = "主程序依赖快照"
if repair_target and not repair_target.exists(): if repair_target and not await _await_thread_operation(repair_target.exists):
repair_target = None repair_target = None
if repair_target is None: if repair_target is None:
repair_target = settings.ROOT_PATH / "pyproject.toml" repair_target = settings.ROOT_PATH / "pyproject.toml"
repair_desc = "主程序 uv.lock" repair_desc = "主程序 uv.lock"
if not repair_target.exists(): if not await _await_thread_operation(repair_target.exists):
return False, f"恢复依赖文件不存在:{repair_target}" return False, f"恢复依赖文件不存在:{repair_target}"
if snapshot_file is None and not (settings.ROOT_PATH / "uv.lock").exists(): lock_file = settings.ROOT_PATH / "uv.lock"
if snapshot_file is None and not await _await_thread_operation(lock_file.exists):
return False, f"恢复依赖文件不存在:{settings.ROOT_PATH / 'uv.lock'}" return False, f"恢复依赖文件不存在:{settings.ROOT_PATH / 'uv.lock'}"
request = cls.__build_package_install_request( request = cls.__build_package_install_request(
@@ -2568,7 +2569,7 @@ class PluginHelper(metaclass=WeakSingleton):
candidate_dirs = [] candidate_dirs = []
for dependency_file in resolved_dependency_files: for dependency_file in resolved_dependency_files:
wheels_dir = dependency_file.parent / "wheels" wheels_dir = dependency_file.parent / "wheels"
if wheels_dir.is_dir(): if await _await_thread_operation(wheels_dir.is_dir):
candidate_dirs.append(wheels_dir) candidate_dirs.append(wheels_dir)
if find_links_dirs: if find_links_dirs:
candidate_dirs.extend(find_links_dirs) candidate_dirs.extend(find_links_dirs)
@@ -2577,9 +2578,11 @@ class PluginHelper(metaclass=WeakSingleton):
seen_dirs = set() seen_dirs = set()
for candidate_dir in candidate_dirs: for candidate_dir in candidate_dirs:
candidate_path = Path(candidate_dir) candidate_path = Path(candidate_dir)
if not candidate_path.is_dir(): if not await _await_thread_operation(candidate_path.is_dir):
continue continue
candidate_key = str(candidate_path.resolve()) candidate_key = str(
await _await_thread_operation(candidate_path.resolve)
)
if candidate_key in seen_dirs: if candidate_key in seen_dirs:
continue continue
seen_dirs.add(candidate_key) seen_dirs.add(candidate_key)
@@ -2707,7 +2710,10 @@ class PluginHelper(metaclass=WeakSingleton):
if acquired: if acquired:
cls._package_install_lock.release() cls._package_install_lock.release()
if constraints_file: if constraints_file:
constraints_file.unlink(missing_ok=True) await _await_thread_operation(
constraints_file.unlink,
missing_ok=True,
)
async def __async_backup_plugin(self, pid: str) -> str: async def __async_backup_plugin(self, pid: str) -> str:
""" """
+1 -1
View File
@@ -57,7 +57,7 @@ from app.api.dependencies.plugin import (
from app.adapters.external.server import MoviePilotServerHelper from app.adapters.external.server import MoviePilotServerHelper
from app.adapters.external.market import PluginHelper from app.adapters.external.market import PluginHelper
from app.adapters.system.plugin.package import PluginPackageManager from app.adapters.system.plugin.package import PluginPackageManager
from app.application.database import DatabaseWorkerOverloadedError from app.schemas.exception import DatabaseWorkerOverloadedError
from app.runtime.log import logger from app.runtime.log import logger
from app.schemas.types import SystemConfigKey from app.schemas.types import SystemConfigKey
-8
View File
@@ -18,14 +18,6 @@ DatabaseProbe = Callable[[], Optional[str]]
T = TypeVar("T") T = TypeVar("T")
class DatabaseWorkerClosedError(RuntimeError):
"""数据库执行器尚未启动或已经停止。"""
class DatabaseWorkerOverloadedError(RuntimeError):
"""数据库执行器的运行与排队容量已经用尽。"""
class AsyncDatabaseExecutor(Protocol): class AsyncDatabaseExecutor(Protocol):
"""让异步业务调用同步短事务而不阻塞事件循环。""" """让异步业务调用同步短事务而不阻塞事件循环。"""
+3 -1
View File
@@ -10,10 +10,12 @@ from weakref import WeakValueDictionary
from app.application.database import ( from app.application.database import (
AsyncDatabaseExecutor, AsyncDatabaseExecutor,
)
from app.schemas.agent import AgentChatSessionDetail, AgentChatSessionSummary
from app.schemas.exception import (
DatabaseWorkerClosedError, DatabaseWorkerClosedError,
DatabaseWorkerOverloadedError, DatabaseWorkerOverloadedError,
) )
from app.schemas.agent import AgentChatSessionDetail, AgentChatSessionSummary
from app.runtime.observability import record_metric from app.runtime.observability import record_metric
+1 -1
View File
@@ -7,7 +7,7 @@ from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any, Optional from typing import Any, Optional
from app.application.database import DatabaseWorkerOverloadedError from app.schemas.exception import DatabaseWorkerOverloadedError
from app.application.plugin.lifecycle import plugin_lifecycle from app.application.plugin.lifecycle import plugin_lifecycle
from app.runtime.log import logger from app.runtime.log import logger
+1 -1
View File
@@ -10,7 +10,7 @@ from contextvars import copy_context
from dataclasses import dataclass from dataclasses import dataclass
from typing import Callable, TypeVar from typing import Callable, TypeVar
from app.application.database import ( from app.schemas.exception import (
DatabaseWorkerClosedError, DatabaseWorkerClosedError,
DatabaseWorkerOverloadedError, DatabaseWorkerOverloadedError,
) )
+1 -1
View File
@@ -14,7 +14,7 @@ from app.adapters.observability.otel import build_observation_port
from app.adapters.web.plugin.routes import FastAPIDynamicRouteRegistry from app.adapters.web.plugin.routes import FastAPIDynamicRouteRegistry
from app.adapters.web.health import install_health_routes from app.adapters.web.health import install_health_routes
from app.application.plugin.routes import configure_plugin_routes from app.application.plugin.routes import configure_plugin_routes
from app.application.database import ( from app.schemas.exception import (
DatabaseWorkerClosedError, DatabaseWorkerClosedError,
DatabaseWorkerOverloadedError, DatabaseWorkerOverloadedError,
) )
+8
View File
@@ -47,6 +47,14 @@ class StorageQueryError(Exception):
pass pass
class DatabaseWorkerClosedError(RuntimeError):
"""数据库执行器尚未启动或已经停止。"""
class DatabaseWorkerOverloadedError(RuntimeError):
"""数据库执行器的运行与排队容量已经用尽。"""
class TMDbException(Exception): class TMDbException(Exception):
""" """
用于表示TheMovieDB数据源请求失败的跨层异常契约 用于表示TheMovieDB数据源请求失败的跨层异常契约
+2
View File
@@ -81,6 +81,8 @@ SCHEMA_EXPORTS = {
'DashboardMemoryInfo': ('app.schemas.dashboard', 'DashboardMemoryInfo'), 'DashboardMemoryInfo': ('app.schemas.dashboard', 'DashboardMemoryInfo'),
'DashboardSystemInfo': ('app.schemas.dashboard', 'DashboardSystemInfo'), 'DashboardSystemInfo': ('app.schemas.dashboard', 'DashboardSystemInfo'),
'DataT': ('app.schemas.response', 'DataT'), 'DataT': ('app.schemas.response', 'DataT'),
'DatabaseWorkerClosedError': ('app.schemas.exception', 'DatabaseWorkerClosedError'),
'DatabaseWorkerOverloadedError': ('app.schemas.exception', 'DatabaseWorkerOverloadedError'),
'Dict': ('app.schemas.mcp', 'Dict'), 'Dict': ('app.schemas.mcp', 'Dict'),
'DiscoverMediaSource': ('app.schemas.event', 'DiscoverMediaSource'), 'DiscoverMediaSource': ('app.schemas.event', 'DiscoverMediaSource'),
'DiscoverSourceEventData': ('app.schemas.event', 'DiscoverSourceEventData'), 'DiscoverSourceEventData': ('app.schemas.event', 'DiscoverSourceEventData'),
+18 -7
View File
@@ -13,8 +13,8 @@
"runtime_to_db": [], "runtime_to_db": [],
"workflow_to_db": [] "workflow_to_db": []
}, },
"edge_count": 6440, "edge_count": 6451,
"edge_sha256": "93995005b1d5d1da95e9ec73a39c1d92e8e8e3e3ce3751ce1035e3f31103350d", "edge_sha256": "9075d8717e384580cd6a41bc36685438770db4a8d8f18c57e6c494f32937113a",
"edges": [ "edges": [
"app -> app.runtime", "app -> app.runtime",
"app -> app.runtime.compat", "app -> app.runtime.compat",
@@ -237,6 +237,8 @@
"app.agent.mcp -> app.schemas.types", "app.agent.mcp -> app.schemas.types",
"app.agent.memory -> app.application", "app.agent.memory -> app.application",
"app.agent.memory -> app.application.agentdata", "app.agent.memory -> app.application.agentdata",
"app.agent.memory -> app.application.messaging",
"app.agent.memory -> app.application.messaging.chat",
"app.agent.memory -> app.runtime", "app.agent.memory -> app.runtime",
"app.agent.memory -> app.runtime.log", "app.agent.memory -> app.runtime.log",
"app.agent.memory -> app.runtime.settings", "app.agent.memory -> app.runtime.settings",
@@ -345,6 +347,8 @@
"app.agent.orchestrator -> app.agent.tools.impl.query_system_settings", "app.agent.orchestrator -> app.agent.tools.impl.query_system_settings",
"app.agent.orchestrator -> app.application", "app.agent.orchestrator -> app.application",
"app.agent.orchestrator -> app.application.agentdata", "app.agent.orchestrator -> app.application.agentdata",
"app.agent.orchestrator -> app.application.messaging",
"app.agent.orchestrator -> app.application.messaging.chat",
"app.agent.orchestrator -> app.application.plugin", "app.agent.orchestrator -> app.application.plugin",
"app.agent.orchestrator -> app.application.plugin.runtime", "app.agent.orchestrator -> app.application.plugin.runtime",
"app.agent.orchestrator -> app.chain", "app.agent.orchestrator -> app.chain",
@@ -2052,7 +2056,6 @@
"app.api.endpoints.plugin -> app.application", "app.api.endpoints.plugin -> app.application",
"app.api.endpoints.plugin -> app.application.commands", "app.api.endpoints.plugin -> app.application.commands",
"app.api.endpoints.plugin -> app.application.configuration", "app.api.endpoints.plugin -> app.application.configuration",
"app.api.endpoints.plugin -> app.application.database",
"app.api.endpoints.plugin -> app.application.plugin", "app.api.endpoints.plugin -> app.application.plugin",
"app.api.endpoints.plugin -> app.application.plugin.config", "app.api.endpoints.plugin -> app.application.plugin.config",
"app.api.endpoints.plugin -> app.application.plugin.folders", "app.api.endpoints.plugin -> app.application.plugin.folders",
@@ -2068,6 +2071,7 @@
"app.api.endpoints.plugin -> app.runtime.log", "app.api.endpoints.plugin -> app.runtime.log",
"app.api.endpoints.plugin -> app.schemas", "app.api.endpoints.plugin -> app.schemas",
"app.api.endpoints.plugin -> app.schemas.common", "app.api.endpoints.plugin -> app.schemas.common",
"app.api.endpoints.plugin -> app.schemas.exception",
"app.api.endpoints.plugin -> app.schemas.plugin", "app.api.endpoints.plugin -> app.schemas.plugin",
"app.api.endpoints.plugin -> app.schemas.response", "app.api.endpoints.plugin -> app.schemas.response",
"app.api.endpoints.plugin -> app.schemas.token", "app.api.endpoints.plugin -> app.schemas.token",
@@ -2553,8 +2557,13 @@
"app.application.mediaserver -> app.schemas.types", "app.application.mediaserver -> app.schemas.types",
"app.application.messaging.agent -> app.schemas", "app.application.messaging.agent -> app.schemas",
"app.application.messaging.agent -> app.schemas.types", "app.application.messaging.agent -> app.schemas.types",
"app.application.messaging.chat -> app.application",
"app.application.messaging.chat -> app.application.database",
"app.application.messaging.chat -> app.runtime",
"app.application.messaging.chat -> app.runtime.observability",
"app.application.messaging.chat -> app.schemas", "app.application.messaging.chat -> app.schemas",
"app.application.messaging.chat -> app.schemas.agent", "app.application.messaging.chat -> app.schemas.agent",
"app.application.messaging.chat -> app.schemas.exception",
"app.application.messaging.interaction -> app.schemas", "app.application.messaging.interaction -> app.schemas",
"app.application.messaging.interaction -> app.schemas.message", "app.application.messaging.interaction -> app.schemas.message",
"app.application.messaging.interaction -> app.schemas.notification", "app.application.messaging.interaction -> app.schemas.notification",
@@ -2641,11 +2650,12 @@
"app.application.plugin.folders -> app.schemas", "app.application.plugin.folders -> app.schemas",
"app.application.plugin.folders -> app.schemas.types", "app.application.plugin.folders -> app.schemas.types",
"app.application.plugin.install -> app.application", "app.application.plugin.install -> app.application",
"app.application.plugin.install -> app.application.database",
"app.application.plugin.install -> app.application.plugin", "app.application.plugin.install -> app.application.plugin",
"app.application.plugin.install -> app.application.plugin.lifecycle", "app.application.plugin.install -> app.application.plugin.lifecycle",
"app.application.plugin.install -> app.runtime", "app.application.plugin.install -> app.runtime",
"app.application.plugin.install -> app.runtime.log", "app.application.plugin.install -> app.runtime.log",
"app.application.plugin.install -> app.schemas",
"app.application.plugin.install -> app.schemas.exception",
"app.application.recognition -> app.application", "app.application.recognition -> app.application",
"app.application.recognition -> app.application.configuration", "app.application.recognition -> app.application.configuration",
"app.application.recognition -> app.schemas", "app.application.recognition -> app.schemas",
@@ -3675,10 +3685,10 @@
"app.db.session -> app.runtime.config", "app.db.session -> app.runtime.config",
"app.db.session -> app.runtime.log", "app.db.session -> app.runtime.log",
"app.db.session -> app.runtime.observability", "app.db.session -> app.runtime.observability",
"app.db.worker -> app.application",
"app.db.worker -> app.application.database",
"app.db.worker -> app.runtime", "app.db.worker -> app.runtime",
"app.db.worker -> app.runtime.observability", "app.db.worker -> app.runtime.observability",
"app.db.worker -> app.schemas",
"app.db.worker -> app.schemas.exception",
"app.doctor.checks -> app.adapters", "app.doctor.checks -> app.adapters",
"app.doctor.checks -> app.adapters.system", "app.doctor.checks -> app.adapters.system",
"app.doctor.checks -> app.adapters.system.backup", "app.doctor.checks -> app.adapters.system.backup",
@@ -3802,7 +3812,6 @@
"app.factory -> app.api", "app.factory -> app.api",
"app.factory -> app.api.response", "app.factory -> app.api.response",
"app.factory -> app.application", "app.factory -> app.application",
"app.factory -> app.application.database",
"app.factory -> app.application.plugin", "app.factory -> app.application.plugin",
"app.factory -> app.application.plugin.routes", "app.factory -> app.application.plugin.routes",
"app.factory -> app.application.security", "app.factory -> app.application.security",
@@ -3816,6 +3825,7 @@
"app.factory -> app.runtime.observability", "app.factory -> app.runtime.observability",
"app.factory -> app.runtime.settings", "app.factory -> app.runtime.settings",
"app.factory -> app.schemas", "app.factory -> app.schemas",
"app.factory -> app.schemas.exception",
"app.factory -> app.schemas.mcp", "app.factory -> app.schemas.mcp",
"app.factory -> app.schemas.openai", "app.factory -> app.schemas.openai",
"app.factory -> app.schemas.response", "app.factory -> app.schemas.response",
@@ -6094,6 +6104,7 @@
"app.startup.modules_initializer -> app.application.history", "app.startup.modules_initializer -> app.application.history",
"app.startup.modules_initializer -> app.application.image", "app.startup.modules_initializer -> app.application.image",
"app.startup.modules_initializer -> app.application.messaging", "app.startup.modules_initializer -> app.application.messaging",
"app.startup.modules_initializer -> app.application.messaging.agent",
"app.startup.modules_initializer -> app.application.messaging.chat", "app.startup.modules_initializer -> app.application.messaging.chat",
"app.startup.modules_initializer -> app.application.messaging.message", "app.startup.modules_initializer -> app.application.messaging.message",
"app.startup.modules_initializer -> app.application.module", "app.startup.modules_initializer -> app.application.module",
+1 -1
View File
@@ -11,7 +11,7 @@ from uuid import uuid4
import pytest import pytest
from sqlalchemy import delete, select from sqlalchemy import delete, select
from app.application.database import ( from app.schemas.exception import (
DatabaseWorkerClosedError, DatabaseWorkerClosedError,
DatabaseWorkerOverloadedError, DatabaseWorkerOverloadedError,
) )
+1 -1
View File
@@ -23,7 +23,7 @@ from app.factory import (
localized_unhandled_exception_handler, localized_unhandled_exception_handler,
localized_validation_exception_handler, localized_validation_exception_handler,
) )
from app.application.database import ( from app.schemas.exception import (
DatabaseWorkerClosedError, DatabaseWorkerClosedError,
DatabaseWorkerOverloadedError, DatabaseWorkerOverloadedError,
) )
+2 -2
View File
@@ -6,8 +6,8 @@ from unittest.mock import patch
import pytest import pytest
from app.db.worker import ( from app.db.worker import DatabaseWorker
DatabaseWorker, from app.schemas.exception import (
DatabaseWorkerClosedError, DatabaseWorkerClosedError,
DatabaseWorkerOverloadedError, DatabaseWorkerOverloadedError,
) )
+1 -1
View File
@@ -3,7 +3,7 @@ from unittest.mock import AsyncMock, Mock
import pytest import pytest
from app.application.database import DatabaseWorkerOverloadedError from app.schemas.exception import DatabaseWorkerOverloadedError
from app.application.plugin.install import PluginInstallCommand from app.application.plugin.install import PluginInstallCommand