mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
fix(site): use explicit transactions for connectivity paths
This commit is contained in:
+15
-4
@@ -148,6 +148,8 @@ def configure_plugin_system_services():
|
||||
from app.db.oper.message import MessageOper
|
||||
from app.db.oper.passkey import PassKeyOper
|
||||
from app.startup.subscription import TransactionalSubscribeWriter
|
||||
from app.startup.download_failure import TransactionalDownloadFailureRepository
|
||||
from app.startup.site import TransactionalSiteRepository
|
||||
from app.startup.workflow import TransactionalWorkflowExecutionService
|
||||
from app.startup.transaction import TransactionalWriteRunner
|
||||
|
||||
@@ -202,15 +204,24 @@ def configure_plugin_system_services():
|
||||
)
|
||||
)
|
||||
|
||||
def site_repository() -> TransactionalSiteRepository:
|
||||
"""按生产组合根方式创建显式事务站点仓储。"""
|
||||
return TransactionalSiteRepository(
|
||||
sync_session=SessionFactory,
|
||||
async_session=async_session_scope,
|
||||
)
|
||||
|
||||
configure_chain_data_ports(
|
||||
site=lambda: SiteOper(),
|
||||
site=site_repository,
|
||||
subscribe=lambda: SubscribeOper(),
|
||||
workflow=lambda: WorkflowOper(),
|
||||
download_history=lambda: DownloadHistoryOper(),
|
||||
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_chain_runtime_context_provider(lambda: ChainRuntimeContext(
|
||||
@@ -228,7 +239,7 @@ def configure_plugin_system_services():
|
||||
configuration=build_chain_runtime_config(settings),
|
||||
))
|
||||
configure_site_query_service(SiteQueryService(repository=SiteOper()))
|
||||
configure_site_health_service(SiteHealthService(repository=SiteOper()))
|
||||
configure_site_health_service(SiteHealthService(repository=site_repository()))
|
||||
configure_workflow_query(WorkflowQueryService(repository=WorkflowOper()))
|
||||
from app.db.oper.agenttask import AgentTaskOper
|
||||
from app.db.oper.plugindata import PluginDataOper
|
||||
@@ -236,7 +247,7 @@ def configure_plugin_system_services():
|
||||
agent_chat=lambda: AgentChatOper(),
|
||||
agent_task=lambda: AgentTaskOper(),
|
||||
user=lambda: UserOper(),
|
||||
site=lambda: SiteOper(),
|
||||
site=site_repository,
|
||||
subscribe=lambda: SubscribeOper(),
|
||||
subscribe_history=lambda: SubscribeHistoryOper(),
|
||||
transfer_history=lambda: TransferHistoryOper(),
|
||||
|
||||
+9
-3
@@ -13,8 +13,8 @@
|
||||
"runtime_to_db": [],
|
||||
"workflow_to_db": []
|
||||
},
|
||||
"edge_count": 6429,
|
||||
"edge_sha256": "664681a500ba1c3d273568829b1b860e4dae7fe7a6c053c566583341fee7f903",
|
||||
"edge_count": 6434,
|
||||
"edge_sha256": "5c47cae41f5d90e1030757d0cb47db8eed373424dda5db17c900271e1d03a9c8",
|
||||
"edges": [
|
||||
"app -> app.runtime",
|
||||
"app -> app.runtime.compat",
|
||||
@@ -6168,6 +6168,7 @@
|
||||
"app.startup.modules_initializer -> app.startup.download_failure",
|
||||
"app.startup.modules_initializer -> app.startup.managed_resources_initializer",
|
||||
"app.startup.modules_initializer -> app.startup.outbox",
|
||||
"app.startup.modules_initializer -> app.startup.site",
|
||||
"app.startup.modules_initializer -> app.startup.subscription",
|
||||
"app.startup.modules_initializer -> app.startup.transaction",
|
||||
"app.startup.modules_initializer -> app.startup.workflow",
|
||||
@@ -6228,6 +6229,10 @@
|
||||
"app.startup.scheduler_initializer -> app.application",
|
||||
"app.startup.scheduler_initializer -> app.application.scheduling",
|
||||
"app.startup.scheduler_initializer -> app.scheduler",
|
||||
"app.startup.site -> app.db",
|
||||
"app.startup.site -> app.db.oper",
|
||||
"app.startup.site -> app.db.oper.site",
|
||||
"app.startup.site -> app.db.uow",
|
||||
"app.startup.subscription -> app.adapters",
|
||||
"app.startup.subscription -> app.adapters.external",
|
||||
"app.startup.subscription -> app.adapters.external.server",
|
||||
@@ -6446,7 +6451,7 @@
|
||||
"app.workflow.actions.transfer_file -> app.workflow",
|
||||
"app.workflow.actions.transfer_file -> app.workflow.actions"
|
||||
],
|
||||
"module_count": 797,
|
||||
"module_count": 798,
|
||||
"modules": [
|
||||
"app",
|
||||
"app.adapters",
|
||||
@@ -7219,6 +7224,7 @@
|
||||
"app.startup.plugins_initializer",
|
||||
"app.startup.routers_initializer",
|
||||
"app.startup.scheduler_initializer",
|
||||
"app.startup.site",
|
||||
"app.startup.subscription",
|
||||
"app.startup.transaction",
|
||||
"app.startup.transfer_initializer",
|
||||
|
||||
@@ -204,6 +204,22 @@ def test_site_oper_update_icon_creates_then_only_overwrites_with_content(db):
|
||||
assert first.startswith("data:image/ico;base64,")
|
||||
|
||||
|
||||
def test_site_oper_icon_without_explicit_session_uses_transaction_runner(db):
|
||||
"""无显式会话的图标写入应由兼容事务执行器完成提交。"""
|
||||
oper = SiteOper()
|
||||
|
||||
oper.update_icon(
|
||||
"兼容站点",
|
||||
"op-icon-no-session.test",
|
||||
"https://op-icon-no-session.test/favicon.ico",
|
||||
"AAA",
|
||||
)
|
||||
|
||||
icon = SiteIcon.get_by_domain(db.session, "op-icon-no-session.test")
|
||||
assert icon.name == "兼容站点"
|
||||
assert icon.base64 == "data:image/ico;base64,AAA"
|
||||
|
||||
|
||||
def test_site_oper_success_accumulates_and_records_state(db):
|
||||
"""
|
||||
访问成功累加计数并把最后状态标记为成功。
|
||||
@@ -219,6 +235,17 @@ def test_site_oper_success_accumulates_and_records_state(db):
|
||||
assert stat.seconds
|
||||
|
||||
|
||||
def test_site_oper_statistics_without_explicit_session_use_transaction_runner(db):
|
||||
"""无显式会话的站点统计必须由兼容事务执行器完成提交。"""
|
||||
oper = SiteOper()
|
||||
|
||||
oper.success("op-stat-no-session.test", seconds=3)
|
||||
oper.fail("op-stat-no-session.test")
|
||||
|
||||
stat = SiteStatistic.get_by_domain(db.session, "op-stat-no-session.test")
|
||||
assert (stat.success, stat.fail, stat.lst_state) == (1, 1, 1)
|
||||
|
||||
|
||||
def test_site_oper_success_caps_the_timing_note_at_ten_entries(db):
|
||||
"""
|
||||
耗时记录最多保留最近 10 条,超出时丢弃最旧的。
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
"""站点连通性测试链路的数据库回归测试。"""
|
||||
|
||||
from app.chain.site import SiteChain
|
||||
from app.db.models.site import Site
|
||||
from app.db.models.sitestatistic import SiteStatistic
|
||||
|
||||
|
||||
def test_site_connectivity_records_result_without_injected_session(db, monkeypatch):
|
||||
"""默认站点端口完成测试后应提交统计,而不是对空会话调用 execute。"""
|
||||
db.watermark(Site, SiteStatistic)
|
||||
db.add(Site(
|
||||
name="连通性测试站点",
|
||||
domain="connectivity.test",
|
||||
url="https://connectivity.test/",
|
||||
is_active=True,
|
||||
))
|
||||
monkeypatch.setattr(
|
||||
SiteChain,
|
||||
"_SiteChain__test",
|
||||
lambda _self, _site: (True, "连接成功"),
|
||||
)
|
||||
|
||||
status, message = SiteChain().test("https://connectivity.test/")
|
||||
|
||||
statistic = SiteStatistic.get_by_domain(db.session, "connectivity.test")
|
||||
assert (status, message) == (True, "连接成功")
|
||||
assert statistic.success == 1
|
||||
assert statistic.lst_state == 0
|
||||
Reference in New Issue
Block a user