docs: publish which errors reach callers and which the library absorbs
TransientError and SourceDeadError read like caller-facing contracts in the taxonomy table, but the retry loop catches both and repackages them as AllSourcesExhausted, so they never arrive. That is only discoverable by reading middleware/retry.py, and a downstream project wrote a whole design section on the false premise before checking. The new table states the split outright, including that GovernanceBackendError now sits on the caller-facing side and SourceNotConfiguredError deliberately does not join the retryable family.
This commit is contained in:
@@ -124,6 +124,21 @@ except RequestRejectedError:
|
||||
|
||||
预算耗尽/全源熔断时抛 `GatewayUnavailableError` 族(`CircuitOpenError` / `AllSourcesExhausted`),携带 `scope` / `reason` / `retry_after_s` / `per_source_reasons`,供任务队列做延期重投。
|
||||
|
||||
### 哪些异常会到达调用方
|
||||
|
||||
上表的"库内行为"一列描述的是**治理动作**,不是调用方要处理的东西。四类里有两类**根本到不了调用方**——它们被重试循环接住,预算耗尽时统一包成 `AllSourcesExhausted`。这个区分只看类型树和 docstring 是读不出来的,曾让下游据此写错整段设计文档,故在此列明:
|
||||
|
||||
| 会到达调用方 | 库内吸收(不必 catch) |
|
||||
|---|---|
|
||||
| `GatewayUnavailableError` 族——`CircuitOpenError` / `AllSourcesExhausted` / `GovernanceBackendError` | `TransientError`(退避后换源重试,耗尽即转为 `AllSourcesExhausted`) |
|
||||
| `RequestRejectedError` | `SourceDeadError`(立即熔断该源并换源,同上) |
|
||||
| `ResultInvalidError` | |
|
||||
| `SourceNotConfiguredError` | |
|
||||
|
||||
**`GovernanceBackendError` 属于第一列**: 限流/熔断的状态后端(如 Redis)自身故障时库 fail-closed——一个请求都发不出去,这就是"整个 scope 暂时不可用"。它继承 `GatewayUnavailableError`,所以 §4 那段 `except GatewayUnavailableError` 一条即覆盖完整,无需为它单列分支。`retry_after_s` 默认 5 秒(后端恢复时间不可知,取 0 会让积压任务零延迟冲击已挂掉的后端)。
|
||||
|
||||
**`SourceNotConfiguredError` 有意不在第一列的族内**: 源名不在限流后端的配置字典中是**装配缺陷**而非暂时故障,它应当消耗失败预算、进死信、让人看见——归入可重投家族只会让配置写错的任务永远重投且无人告警。
|
||||
|
||||
## 配置参考
|
||||
|
||||
配置只有两条装配路径:`from_env()`(读 `.env`/环境变量)或构造函数全量注入(测试/高级);库内部任何组件不自读环境变量。键名全集见 [.env.example](.env.example),约定速览:
|
||||
|
||||
Reference in New Issue
Block a user