Effect 里失败了该直接重试吗?
对一个「用户不存在」的错误重试一万次,结果还是「用户不存在」,只会放大负载、拖慢响应。
答案是:不该直接重试,要先给错误分类,只重试暂时性的错误。 重试的前提是再试一次有可能成功,分类就是回答这个问题;在 Effect 里,这个分类就是 Effect.retry 的 while 谓词。
分类之所以做得出来,是因为 Effect 的类型化错误把不同的失败在 E 里就区分开了。这篇默认你知道 E 里记录着可能的失败;不熟悉的话先看 Effect 里的错误为什么写在类型里。
哪些错误值得重试?
- 网络抖动、下游 503、数据库死锁:暂时性,值得重试。
- 参数校验失败、用户不存在、权限不足:确定性错误,重试结果一样。
- 缺陷:不该重试,它是 bug。
在 Effect 里怎么写分类?
const resilient = callDownstream.pipe(
Effect.retry({
times: 3,
while: (e) => e._tag === "Network" || (e._tag === "Http" && e.status >= 500)
})
)while 里的谓词就是分类本身:只有 Network 错误和状态码不低于 500 的 Http 错误会被重试,其余错误第一次失败就直接向上传。callDownstream 是占位的下游调用。
退避、抖动和超时呢?
这里只解决「哪些错误值得重试」。更丰富的重试策略,包括退避、抖动、上限、超时,属于 Schedule,在 Effect 的重试与 Schedule 和 Effect 里怎么处理时间与重试 里展开。
重试策略的第一行永远是哪些错误值得重试。没有分类的重试,是在给确定性错误添堵。
重试耗尽后剩下的失败交给谁,见 Effect 里的错误该由哪一层处理?。
文中代码是示意,没有在这里重新运行,没有给出 effect 的具体版本号;callDownstream 是占位名。把哪类错误算作暂时性,是一套通用的工程判断,具体到你的下游服务需要自己确认。