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 是占位名。把哪类错误算作暂时性,是一套通用的工程判断,具体到你的下游服务需要自己确认。