mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
refactor: expand runtime contracts and debt ratchets
This commit is contained in:
@@ -9,6 +9,7 @@ from app.application.messaging.chat import (
|
||||
AsyncUnitOfWork,
|
||||
)
|
||||
from app.application.outbox import AsyncOutboxTransaction
|
||||
from app.application.configuration import RuntimeConfiguration
|
||||
from app.application.subscription.delete import SubscribeDeletionRepository
|
||||
from app.application.subscription.identity import SubscribeIdentityDeletionRepository
|
||||
from app.application.subscription.mutation import (
|
||||
@@ -125,4 +126,5 @@ class HostRuntime:
|
||||
|
||||
agent_chat: AgentChatRuntime
|
||||
subscription: SubscriptionRuntime
|
||||
configuration: RuntimeConfiguration
|
||||
compatibility_api_data: CompatibilityApiData
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
"""下载失败冷却切片的显式会话与事务适配器。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import Any, cast
|
||||
|
||||
from app.db.oper.downloadfailure import DownloadFailureOper
|
||||
from app.db.uow import SqlAlchemyUnitOfWork
|
||||
|
||||
|
||||
class TransactionalDownloadFailureRepository:
|
||||
"""为 Chain 下载失败读写创建短生命周期会话并显式收口事务。"""
|
||||
|
||||
def __init__(self, session_factory: Callable[[], Any]) -> None:
|
||||
"""保存由启动组合根提供的同步会话工厂。"""
|
||||
self._session_factory = session_factory
|
||||
|
||||
def get_active_by_fingerprints(
|
||||
self,
|
||||
fingerprints: list[str],
|
||||
now_time: str,
|
||||
) -> dict[str, Any]:
|
||||
"""在独立只读会话中查询仍处于冷却期的失败记录。"""
|
||||
with self._session_factory() as session:
|
||||
return cast(
|
||||
dict[str, Any],
|
||||
DownloadFailureOper(db=session).get_active_by_fingerprints(
|
||||
fingerprints=fingerprints,
|
||||
now_time=now_time,
|
||||
),
|
||||
)
|
||||
|
||||
def record_failure(
|
||||
self,
|
||||
fingerprint: str,
|
||||
now_time: str,
|
||||
next_retry_at: str,
|
||||
**kwargs: object,
|
||||
) -> Any:
|
||||
"""在一个显式 UoW 中新增或更新下载失败记录。"""
|
||||
with self._session_factory() as session:
|
||||
transaction = SqlAlchemyUnitOfWork(session)
|
||||
try:
|
||||
failure = DownloadFailureOper(db=session).record_failure(
|
||||
fingerprint=fingerprint,
|
||||
now_time=now_time,
|
||||
next_retry_at=next_retry_at,
|
||||
**kwargs,
|
||||
)
|
||||
transaction.commit()
|
||||
return failure
|
||||
except Exception:
|
||||
transaction.rollback()
|
||||
raise
|
||||
@@ -36,8 +36,13 @@ from app.application.messaging.message import (
|
||||
stop_message,
|
||||
)
|
||||
from app.application.configuration import (
|
||||
ApiRuntimeConfig,
|
||||
ChainRuntimeConfig,
|
||||
RuntimeConfiguration,
|
||||
SchedulerRuntimeConfig,
|
||||
SystemConfigService,
|
||||
TransferRetryConfig,
|
||||
configure_runtime_configuration,
|
||||
configure_system_config,
|
||||
configure_transfer_retry_config,
|
||||
)
|
||||
@@ -86,7 +91,6 @@ from app.db.oper.transferhistory import TransferHistoryOper
|
||||
from app.db.oper.downloadhistory import DownloadHistoryOper
|
||||
from app.db.oper.transferpending import TransferPendingOper
|
||||
from app.db.oper.mediaserver import MediaServerOper
|
||||
from app.db.oper.downloadfailure import DownloadFailureOper
|
||||
from app.db.oper.site import SiteOper
|
||||
from app.db.oper.message import MessageOper
|
||||
from app.db.oper.subscribehistory import SubscribeHistoryOper
|
||||
@@ -108,6 +112,7 @@ from app.startup.subscription import (
|
||||
configure_transactional_subscription_scopes,
|
||||
)
|
||||
from app.startup.chain_events import TransactionalChainDurableEventWriter
|
||||
from app.startup.download_failure import TransactionalDownloadFailureRepository
|
||||
from app.startup.context import AgentChatRuntime, HostRuntime, SubscriptionRuntime
|
||||
from app.adapters.web.security.access import set_superuser_token_payload_provider
|
||||
from app.application.security.auth import build_superuser_token_payload
|
||||
@@ -151,11 +156,68 @@ def _build_chain_runtime_context() -> ChainRuntimeContext:
|
||||
send_callback=callback
|
||||
),
|
||||
module_dispatcher_factory=ModuleInvocationDispatcher,
|
||||
configuration=_build_chain_runtime_config(),
|
||||
data_ports=get_chain_data_ports(),
|
||||
durable_event_writer=TransactionalChainDurableEventWriter(SessionFactory),
|
||||
)
|
||||
|
||||
|
||||
def _normalize_subscribe_rss_interval(value: object) -> int:
|
||||
"""把无效或过小的 RSS 间隔收敛为兼容的安全值。"""
|
||||
try:
|
||||
return max(int(value), 5)
|
||||
except (TypeError, ValueError):
|
||||
return 30
|
||||
|
||||
|
||||
def _build_api_runtime_config() -> ApiRuntimeConfig:
|
||||
"""从可热更新 settings 构建一次 API 请求配置快照。"""
|
||||
return ApiRuntimeConfig(
|
||||
advanced_mode=settings.ADVANCED_MODE,
|
||||
access_token_expire_minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES,
|
||||
btrfs_fsid_dedup=settings.BTRFS_FSID_DEDUP,
|
||||
ai_agent_enable=settings.AI_AGENT_ENABLE,
|
||||
)
|
||||
|
||||
|
||||
def _build_scheduler_runtime_config() -> SchedulerRuntimeConfig:
|
||||
"""从可热更新 settings 构建一次 Scheduler 操作配置快照。"""
|
||||
return SchedulerRuntimeConfig(
|
||||
dev=settings.DEV,
|
||||
timezone=settings.TZ,
|
||||
scheduler_workers=settings.CONF.scheduler,
|
||||
db_backup_enable=settings.DB_BACKUP_ENABLE,
|
||||
db_backup_cron=settings.DB_BACKUP_CRON,
|
||||
cookiecloud_interval=settings.COOKIECLOUD_INTERVAL,
|
||||
mediaserver_sync_interval=settings.MEDIASERVER_SYNC_INTERVAL,
|
||||
subscribe_search=settings.SUBSCRIBE_SEARCH,
|
||||
subscribe_search_interval=settings.SUBSCRIBE_SEARCH_INTERVAL,
|
||||
subscribe_mode=settings.SUBSCRIBE_MODE,
|
||||
subscribe_rss_interval=_normalize_subscribe_rss_interval(
|
||||
settings.SUBSCRIBE_RSS_INTERVAL
|
||||
),
|
||||
data_cleanup_enable=settings.DATA_CLEANUP_ENABLE,
|
||||
sitedata_refresh_interval=settings.SITEDATA_REFRESH_INTERVAL,
|
||||
memory_gc_interval=settings.MEMORY_GC_INTERVAL,
|
||||
ai_agent_enable=settings.AI_AGENT_ENABLE,
|
||||
ai_agent_job_interval=settings.AI_AGENT_JOB_INTERVAL,
|
||||
usage_statistic_share=settings.USAGE_STATISTIC_SHARE,
|
||||
site_link=settings.MP_DOMAIN("#/site"),
|
||||
)
|
||||
|
||||
|
||||
def _build_chain_runtime_config() -> ChainRuntimeConfig:
|
||||
"""构建 Chain 通用媒体文件后缀配置快照。"""
|
||||
return ChainRuntimeConfig(
|
||||
media_extensions=tuple(
|
||||
settings.RMT_MEDIAEXT
|
||||
+ settings.DOWNLOAD_TMPEXT
|
||||
+ settings.RMT_SUBEXT
|
||||
+ settings.RMT_AUDIOEXT
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def configure_runtime_data_providers() -> None:
|
||||
"""在启动组合层装配运行时和外部服务所需的数据库读取能力。"""
|
||||
configure_service_config_reader(lambda key: SystemConfigOper().get(key))
|
||||
@@ -478,6 +540,11 @@ async def init_modules() -> HostRuntime:
|
||||
"sync": SqlAlchemyUnitOfWork,
|
||||
},
|
||||
)
|
||||
runtime_configuration = RuntimeConfiguration(
|
||||
api=_build_api_runtime_config,
|
||||
scheduler=_build_scheduler_runtime_config,
|
||||
chain=_build_chain_runtime_config,
|
||||
)
|
||||
host_runtime = HostRuntime(
|
||||
agent_chat=AgentChatRuntime(
|
||||
async_session=get_async_db,
|
||||
@@ -491,8 +558,10 @@ async def init_modules() -> HostRuntime:
|
||||
transaction=SqlAlchemyAsyncUnitOfWork,
|
||||
outbox=SqlAlchemyAsyncOutboxStager,
|
||||
),
|
||||
configuration=runtime_configuration,
|
||||
compatibility_api_data=api_data,
|
||||
)
|
||||
configure_runtime_configuration(host_runtime.configuration)
|
||||
configure_api_data_runtime(host_runtime.compatibility_api_data)
|
||||
configure_runtime_data_providers()
|
||||
configure_chain_data_ports(
|
||||
@@ -503,7 +572,9 @@ async def init_modules() -> HostRuntime:
|
||||
transfer_history=lambda: TransferHistoryOper(),
|
||||
transfer_pending=lambda: TransferPendingOper(),
|
||||
media_server=lambda: MediaServerOper(),
|
||||
download_failure=lambda: DownloadFailureOper(),
|
||||
download_failure=lambda: TransactionalDownloadFailureRepository(
|
||||
SessionFactory
|
||||
),
|
||||
user=lambda: UserOper(),
|
||||
)
|
||||
configure_system_config(SystemConfigService(repository=SystemConfigOper()))
|
||||
|
||||
Reference in New Issue
Block a user