fix(site): use explicit transactions for connectivity paths

This commit is contained in:
jxxghp
2026-08-23 10:33:14 +08:00
parent 38ddb59879
commit a8b3dd3988
7 changed files with 357 additions and 70 deletions
+15 -4
View File
@@ -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
View File
@@ -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",
+27
View File
@@ -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 条,超出时丢弃最旧的。
+28
View File
@@ -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