远程副作用、outbox 与未知状态
本地数据库事务和远程邮件、支付或发布服务无法组成同一个原子提交。应用通常在本地事务中保存 outbox,再由 worker 使用业务幂等键发送;超时后先查询远程状态,再决定重试、等待或人工核对。没有查询和幂等能力的供应商只能返回 unknown,不能承诺 exactly-once。
第一个可运行实验:明确未知状态
保存为 remote.py,用 Python 3.12 运行。
python
def decide_after_timeout(lookup_result):
if lookup_result == 'succeeded':
return 'reuse_result'
if lookup_result == 'not_found':
return 'retry_with_same_key'
if lookup_result == 'pending':
return 'wait'
return 'manual_review'
assert decide_after_timeout('succeeded') == 'reuse_result'
assert decide_after_timeout('not_found') == 'retry_with_same_key'
assert decide_after_timeout('pending') == 'wait'
assert decide_after_timeout('unavailable') == 'manual_review'
print('remote outcome policy passed')outbox 记录租户、业务动作、payload 摘要、幂等键、尝试次数和远程状态。worker 重启后复用同一业务键;不能因为模型生成了新的 call ID 就产生新的付款或发信意图。补偿动作也有副作用,必须单独授权和审计。
变体实验
让远程查询本身超时,要求进入人工核对而不是盲目重发。再让供应商返回成功但本地 outbox 未更新,恢复时应查询并补写结果。
练习
- 设计 outbox 的状态迁移:pending、sent、succeeded、failed、unknown。
- 为查询不到结果和查询服务不可用区分重试策略。
- 说明为什么取消请求不能证明远程副作用没有发生。