# 设计 Retry、Timeout、Fallback 与 Compensation

## When to use

Node 调用可能失败的依赖或执行可逆 effect 时使用。不能自动加入 retry；先判断重复执行是否安全，以及下游能否区分真实结果和降级结果。

## Inspect first

- 判断 Operator 属于纯计算、读取还是外部写入。
- 找到既有 idempotency key、对账和 compensation 行为。
- 阅读[第 6 章](/zh-CN/v/0.9.8-RC1/06-resilience-by-design)和[Conformance Fixture](/assets/v/0.9.8-RC1/chapters/06-resilience-by-design/assets/bloge/bloge-conformance/fixtures/snippets/basic/retry-fallback-output.bloge)。

## Required inputs

必须获得单次尝试 deadline、最大尝试次数、backoff 策略、可接受的降级结果、幂等合同和 compensation owner。任何外部写入保证不明确时都应停止。

## Implementation path

1. 用 `timeout` 限定每次尝试。
2. 只有业务依赖允许重复的错误和操作才增加 retry。
3. 根据依赖负载特征选择 fixed、exponential 或 jitter backoff。
4. 只有下游能安全消费显式降级值时才增加 fallback。
5. 为已经完成且可逆的 effect 增加 compensation，不对不可逆 effect 做虚假回滚承诺。
6. 测试一次成功、一次 retry 恢复、一次尝试耗尽和一次 effect 已发生但等待超时。

## MUST / SHOULD / MAY

- **MUST**：把 retry 视为重复调用，不视为回滚。
- **MUST**：Effectful retry 必须幂等或具备对账机制。
- **MUST**：Fallback 输出必须 schema 兼容，并能在语义上识别为降级结果。
- **MUST**：明确 engine timeout 只停止等待，不证明远端 effect 已停止。
- **SHOULD**：对过载的共享服务使用 jitter 或 exponential backoff。
- **MAY**：降级会产生无效业务结果时，不设置 fallback，让 graph 明确失败。

## Failure patterns

- Retry 重复扣款：启用前先增加 idempotency key 和提供方对账。
- Fallback 看起来像正常成功值：增加显式 degraded 状态或删除 fallback。
- 为无法撤销的动作声明 compensation：改成向前恢复或人工修复路径。

## Validation

使用受控 Operator double 让每次尝试可观察。断言尝试次数、单次 timeout、最终 node 状态、fallback 值和 compensation 顺序。只通过 mock 不能证明真实提供方的幂等或回滚语义。

## Evidence

- [韧性设计章节](/zh-CN/v/0.9.8-RC1/06-resilience-by-design)
- [Operator 设计章节](/zh-CN/v/0.9.8-RC1/07-designing-good-operators)
- [固定版本 Runtime 源码](https://github.com/xbdotl/bloge/tree/cc38fbe5bb79ccc603888e4307cfe566d4674ffc/bloge-core)
- [Retry/Fallback Fixture](/assets/v/0.9.8-RC1/chapters/06-resilience-by-design/assets/bloge/bloge-conformance/fixtures/snippets/basic/retry-fallback-output.bloge)
