mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-04 23:17:20 +08:00
feat(database): bound async configuration writes
This commit is contained in:
@@ -6,7 +6,7 @@ from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional, Protocol
|
||||
from typing import Any, Optional, Protocol, cast
|
||||
|
||||
from app.application.database import AsyncDatabaseExecutor
|
||||
from app.schemas.types import MediaType
|
||||
@@ -315,7 +315,8 @@ class SystemConfigService:
|
||||
"""异步写入配置,并等待数据库提交或回滚完成。"""
|
||||
if self._async_executor is None:
|
||||
raise RuntimeError("系统配置异步数据库执行端口尚未配置")
|
||||
return await self._async_executor.run(partial(self._writer.set, key, value))
|
||||
result = await self._async_executor.run(partial(self._writer.set, key, value))
|
||||
return cast(bool | None, result)
|
||||
|
||||
def delete(self, key: Any) -> Any:
|
||||
"""删除配置。"""
|
||||
|
||||
@@ -18,6 +18,14 @@ DatabaseProbe = Callable[[], Optional[str]]
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class DatabaseWorkerClosedError(RuntimeError):
|
||||
"""数据库执行器尚未启动或已经停止。"""
|
||||
|
||||
|
||||
class DatabaseWorkerOverloadedError(RuntimeError):
|
||||
"""数据库执行器的运行与排队容量已经用尽。"""
|
||||
|
||||
|
||||
class AsyncDatabaseExecutor(Protocol):
|
||||
"""让异步业务调用同步短事务而不阻塞事件循环。"""
|
||||
|
||||
|
||||
@@ -24,19 +24,25 @@ class ServerReportService:
|
||||
config_writer: Callable[[Any, Any], Any],
|
||||
installed_plugins_provider: Callable[[], list[str]],
|
||||
subscribes_provider: Callable[[], list[Any]],
|
||||
async_subscribes_provider: Callable[[], Awaitable[list[Any]]] | None = None,
|
||||
plugin_report_sender: Callable[[list[dict]], Any],
|
||||
async_plugin_report_sender: Callable[[list[dict]], Awaitable[Any]],
|
||||
subscribe_report_sender: Callable[[list[dict]], Any],
|
||||
repo_url_sanitizer: Callable[[Optional[str]], Optional[str]],
|
||||
async_subscribe_report_sender: Callable[[list[dict]], Awaitable[Any]] | None = None,
|
||||
async_config_writer: Callable[[Any, Any], Awaitable[Any]] | None = None,
|
||||
) -> None:
|
||||
"""保存本地读取端口和只负责 I/O 的中心服务发送端口。"""
|
||||
self._config_reader = config_reader
|
||||
self._config_writer = config_writer
|
||||
self._installed_plugins_provider = installed_plugins_provider
|
||||
self._subscribes_provider = subscribes_provider
|
||||
self._async_subscribes_provider = async_subscribes_provider
|
||||
self._plugin_report_sender = plugin_report_sender
|
||||
self._async_plugin_report_sender = async_plugin_report_sender
|
||||
self._subscribe_report_sender = subscribe_report_sender
|
||||
self._async_subscribe_report_sender = async_subscribe_report_sender
|
||||
self._async_config_writer = async_config_writer
|
||||
self._repo_url_sanitizer = repo_url_sanitizer
|
||||
|
||||
def init_report(
|
||||
@@ -50,6 +56,22 @@ class ServerReportService:
|
||||
if enabled and not self._config_reader(state_key) and reporter():
|
||||
self._config_writer(state_key, "1")
|
||||
|
||||
async def async_init_report(
|
||||
self,
|
||||
*,
|
||||
enabled: bool,
|
||||
state_key: Any,
|
||||
reporter: Callable[[], Awaitable[bool]],
|
||||
) -> None:
|
||||
"""异步完成首次上报,并通过异步配置端口持久化完成标记。"""
|
||||
if not enabled or self._config_reader(state_key):
|
||||
return
|
||||
if not await reporter():
|
||||
return
|
||||
if self._async_config_writer is None:
|
||||
raise RuntimeError("中心服务上报未配置异步配置写入端口")
|
||||
await self._async_config_writer(state_key, "1")
|
||||
|
||||
def build_subscribe_payload(self, item: Optional[dict]) -> Optional[dict]:
|
||||
"""构造中心服务订阅统计载荷并移除本地运行字段。"""
|
||||
if not isinstance(item, dict):
|
||||
@@ -132,3 +154,24 @@ class ServerReportService:
|
||||
return False
|
||||
response = await self._async_plugin_report_sender(payload)
|
||||
return bool(response is not None and response.status_code == 200)
|
||||
|
||||
async def async_report_subscribes(self, *, enabled: bool) -> bool:
|
||||
"""异步上报当前全部有效订阅的公开统计字段。"""
|
||||
if not enabled:
|
||||
return False
|
||||
if self._async_subscribe_report_sender is None:
|
||||
raise RuntimeError("中心服务上报未配置异步订阅发送端口")
|
||||
if self._async_subscribes_provider is None:
|
||||
raise RuntimeError("中心服务未配置异步订阅读取端口")
|
||||
subscribes = await self._async_subscribes_provider()
|
||||
if not subscribes:
|
||||
return True
|
||||
payloads = [
|
||||
payload
|
||||
for subscribe in subscribes
|
||||
if (payload := self.build_subscribe_payload(subscribe.to_dict()))
|
||||
]
|
||||
if not payloads:
|
||||
return True
|
||||
response = await self._async_subscribe_report_sender(payloads)
|
||||
return bool(response is not None and response.status_code == 200)
|
||||
|
||||
Reference in New Issue
Block a user