8.3 KiB
8.3 KiB
SAGA 分布式事务设计汇报
基于
gvsdsdk/msync+gvsds/apps/msync源码分析(2026-08-10)。 涉及文件:gvsds/apps/msync/transaction.py、gvsds/apps/msync/models.py、gvsds/apps/msync/local_tx.py、gvsdsdk/msync/local_tx.py、gvsds/apps/msync/MSYNC.md。
1. 架构定位:双包三层
┌────────────────────────────── 主服务器(账本服务器)──────────────────────────────┐
│ gvsds/apps/msync/transaction.py TransactionManager(Saga 编排引擎) │
│ gvsds/apps/msync/models.py DistributedTransaction / TransactionStep │
│ │ 正向: POST {domain}/api/msync/local/execute/ {StepName, payload} │
│ │ 反向: POST {domain}/api/msync/local/compensate/ {StepName, payload, │
│ │ ActionResult} │
└────────┼───────────────────────────────┬─────────────────────────────────────────┘
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ gvsdsdk/msync/ │ │ gvsdsdk/msync/ │
│ local_tx.py │ │ transaction.py │
│ LocalTransactionManager│ │ 薄封装 → 转发 gvsds 原生层│
│ (工作服务本地执行端) │ │ (仅主服务器可用) │
└────────────────────────┘ └────────────────────────┘
关键事实:
- 编排端在主服务器,执行端在每个工作服务,通过账本(
ServiceNode.ServiceDomain)发现参与服务域名,HTTP 调用本地端点。 - SDK 薄封装:
gvsdsdk/msync/transaction.py与ogm.py仅转发 gvsds 原生层,子服务器无 gvsds 依赖时抛 ImportError;local_tx.py是真实实现,子服务器可直接注册步骤处理器。 - 主服务器本身也当工作服务:
gvsds/apps/msync/local_tx.py从 gvsdsdk 导入LocalTransactionManager,主服务器同样可以参与 Saga 步骤。 - 双包环形依赖:gvsdsdk(SDK)→ gvsds(主服务器)→ gvsdsdk(local_tx),靠惰性加载(PEP 562)避免初始化时序问题。
2. 状态机
事务 7 态(DistributedTransaction)
Pending(0) ──▶ Running(1) ──▶ Committed(2) (全部步骤成功)
│
├─▶ Failed(5) ──▶ Compensating(3) ──▶ Compensated(4)
│
└─▶ TimedOut(6) ──▶ Compensating(3) ──▶ Compensated(4)
终态集合(IsTerminal)= Committed / Compensated / Failed / TimedOut。
步骤 7 态(TransactionStep)
Pending(0) ──▶ Running(1) ──▶ Completed(2)
│
├─▶ Compensating(3) ──▶ Compensated(4)
└─▶ Failed(5)(正向失败)/ Skipped(6)(事务取消跳过)
关键属性
IsTimedOut:CreateTime + TimeoutSeconds(默认 300s)判定,已终态恒 False。CanRetry:RetryCount < MaxRetries(默认 MaxRetries=3)。
3. 核心流程
3.1 正向执行(Execute)
select_for_update()锁行(防并发推进同一事务)。- 校验状态 ∈ {Pending, Running};超时则标记 TimedOut 并
on_commit补偿。 - 置 Running → 按
StepIndex逐步骤执行:- 已 Completed 步骤跳过(断点续跑语义);
_execute_step:标记步骤 Running → POST 参与服务 execute 端点 → 返回结果 → 标记 Completed + 保存 StepResult;- 任一失败:步骤标记 Failed,记录错误快照,
on_commit触发Compensate。
- 全部完成 → 事务 Committed。
3.2 反向补偿(Compensate)
select_for_update()锁行;已 Compensated 直接返回(幂等)。- 事务置 Compensating → 逆序遍历
StepStatus=Completed的步骤。 - 每步
_compensate_step:把该步骤原始StepResult作为ActionResult随请求传回 → 业务补偿函数可据此幂等回滚。 - 补偿成功 → 步骤 Compensated;补偿失败 → 记录
compensate_error(步骤状态停留在 Compensating)。 - 若存在仍在 Compensating 的步骤 → 事务 Failed;否则 Compensated。
3.3 超时与兜底
- 双保险:执行中逐步骤检测
IsTimedOut(on_commit补偿)+CheckTimeouts()轮询兜底(需 Celery 定时任务触发)。
3.4 重试
- 手动重试:
RetryStep(transaction_code, step_index)仅允许 Failed/Pending 步骤,校验CanRetry,重跑 action。 - 注意:Execute 正向流程中没有自动重试逻辑——步骤失败直接进入补偿,
MaxRetries仅约束手动重试次数。
4. 传输层设计
| 项 | 配置 |
|---|---|
| 会话 | 模块级 requests.Session,连接池 10 |
| TLS | 显式 verify=True,支持 REQUESTS_CA_BUNDLE 环境变量自定义 CA |
| 重试 | 502/503/504 自动重试 3 次,backoff 0.5s,仅 POST |
| 超时 | 单次请求 30s |
| 鉴权 | Authorization: Service <AccessToken>(HMAC 验权签发,2h 有效) |
| 响应契约 | 兼容 {code:200, data}(DRF)与 {success, result, error}(本地管理器) |
5. 设计亮点
- 远程编排 + 本地执行彻底解耦:子服务器仅依赖 SDK
local_tx注册处理器即可接入,无需理解编排。 - 补偿带回 ActionResult:补偿函数拿到原始执行结果,可精确、幂等地回滚(如按返回的 user_uuid 删除)。
- 行锁防并发:Execute / Compensate 均
select_for_update。 - on_commit 补偿:补偿在主事务提交后触发,避免"正向已回滚却补偿"的错乱。
- 断点续跑:Execute 跳过已完成步骤,支持中途失败后整体重跑推进。
- 协议双兼容:
_call_service同时解析两套响应契约。
6. 缺陷与风险
| 等级 | 问题 | 位置 | 说明 |
|---|---|---|---|
| 高 | 响应非 JSON 时事务卡死 | _call_service(transaction.py L538-566) |
resp.json() 解析失败抛 JSONDecodeError 未捕获 → Execute 中断 → 事务永久停 Running,不触发补偿,需等 300s 超时 |
| 高 | 补偿失败后无法重试补偿 | Compensate(L256-314) |
补偿异常时步骤状态停在 Compensating(非 Completed),再次 Compensate 查不到它 → 永远补不回来,事务 Failed 只能人工处理,无 re-compensate 入口 |
| 中 | MaxRetries 无自动重试 | Execute(L211-244) |
失败直接补偿,CanRetry 只在手动 RetryStep 生效;RetryStep 无 select_for_update,并发重试有竞态 |
| 中 | 无步骤间数据流 | Execute/_execute_step |
StepResult 不注入后续步骤 payload;TransactionPayload 创建后不参与执行。无法表达"A 步骤输出是 B 步骤输入"的链式场景 |
| 中 | 超时以 CreateTime 起算 | models.py IsTimedOut |
非"最近活动空闲超时",10 步慢事务总预算仅 300s,无活动推进重置机制 |
| 低 | TransactionResult 恒空 | Execute/Compensate |
模型有字段但从不写入,GetDetail().Result 永远 null |
| 低 | 幂等依赖业务自觉 | _call_service |
无全局幂等键,崩溃窗口内重复执行 action 的副作用靠业务方补偿兜底 |
7. 改进建议(按优先级)
_call_service捕获 JSON 解析/异常 → 统一转StepExecutionError,保证失败必进补偿链路。- 补偿失败步骤状态改为 Failed 并提供
RetryCompensate入口(复用 RetryStep 骨架 + 行锁)。 RetryStep补select_for_update;若需自动重试,在 Execute 循环内按CanRetry内联重试。- 支持步骤结果注入下一步 ActionPayload(可选项,向后兼容)。
- 超时改为基于
UpdateTime的空闲判定或提供事务级 heartbeat。 - Execute/Compensate 末尾统一写入
TransactionResult汇总。
汇报人:TraeCode(2026-08-10)