Files
SMANAGE/SAGA_Report.md
T

8.3 KiB
Raw Blame History

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)

  1. select_for_update() 锁行(防并发推进同一事务)。
  2. 校验状态 ∈ {Pending, Running};超时则标记 TimedOut 并 on_commit 补偿。
  3. 置 Running → 按 StepIndex 逐步骤执行:
    • 已 Completed 步骤跳过(断点续跑语义);
    • _execute_step:标记步骤 Running → POST 参与服务 execute 端点 → 返回结果 → 标记 Completed + 保存 StepResult;
    • 任一失败:步骤标记 Failed,记录错误快照,on_commit 触发 Compensate。
  4. 全部完成 → 事务 Committed。

3.2 反向补偿(Compensate)

  1. select_for_update() 锁行;已 Compensated 直接返回(幂等)。
  2. 事务置 Compensating → 逆序遍历 StepStatus=Completed 的步骤。
  3. 每步 _compensate_step:把该步骤原始 StepResult 作为 ActionResult 随请求传回 → 业务补偿函数可据此幂等回滚。
  4. 补偿成功 → 步骤 Compensated;补偿失败 → 记录 compensate_error(步骤状态停留在 Compensating)。
  5. 若存在仍在 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. 设计亮点

  1. 远程编排 + 本地执行彻底解耦:子服务器仅依赖 SDK local_tx 注册处理器即可接入,无需理解编排。
  2. 补偿带回 ActionResult:补偿函数拿到原始执行结果,可精确、幂等地回滚(如按返回的 user_uuid 删除)。
  3. 行锁防并发:Execute / Compensate 均 select_for_update。
  4. on_commit 补偿:补偿在主事务提交后触发,避免"正向已回滚却补偿"的错乱。
  5. 断点续跑:Execute 跳过已完成步骤,支持中途失败后整体重跑推进。
  6. 协议双兼容:_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. 改进建议(按优先级)

  1. _call_service 捕获 JSON 解析/异常 → 统一转 StepExecutionError,保证失败必进补偿链路。
  2. 补偿失败步骤状态改为 Failed 并提供 RetryCompensate 入口(复用 RetryStep 骨架 + 行锁)。
  3. RetryStep 补 select_for_update;若需自动重试,在 Execute 循环内按 CanRetry 内联重试。
  4. 支持步骤结果注入下一步 ActionPayload(可选项,向后兼容)。
  5. 超时改为基于 UpdateTime 的空闲判定或提供事务级 heartbeat。
  6. Execute/Compensate 末尾统一写入 TransactionResult 汇总。

汇报人:TraeCode(2026-08-10)