Cloudflare Workflowsは、step retryのdelayへ関数を渡せるようになった。ctx.attemptとthrowされたerrorを使い、rate limitなら長く、短いnetwork failureなら短く待つ設計が可能になる。固定exponential backoffより柔軟だが、provider responseを無制限に信頼するとworkflowの滞留や重複処理を招く。
何が変わったか
step.doのretries.delayは、duration string、millisecond numberに加え、次回delayを返すfunctionを受け付ける。functionはctxとerrorを受け取り、string、number、またはそれらをresolveするpromiseを返せる。Retry-Afterやerror codeに応じたadaptive retryをWorkflow内で実装できる。
変更前後の差
従来はbase delayとconstant、linear、exponential backoffを組み合わせるのが中心だった。変更後はfailure typeごとに異なる待機を選べる。ただしdynamic delayを使っても、retry limit、per-attempt timeout、stepのidempotencyは別途必要である。
誰に影響するか
外部SaaS API、LLM API、payment、notification、data warehouseへ接続するWorkflowが対象である。429、503、network timeoutを同じ例外として処理している実装は、error taxonomyを作り直す余地がある。
期限
強制移行期限はない。既存retry設定は継続できる。rate limitや長時間障害で無駄なretryが多いstepから段階的に変更する。
必要な対応
HTTP status、provider error code、Retry-After、network errorを正規化する。delayには最小・最大値を設け、jitterを加えてthundering herdを避ける。write operationにはidempotency keyを使い、retry後の二重作成を防ぐ。永久errorはretryせずdead-letter相当の処理へ送る。
実装・移行手順
- retry対象errorと非対象errorを分類する
- provider別の
Retry-After形式をparseする - delay calculationをpure functionとしてtestする
- minimum、maximum、jitterを設定する
- retry limitとtimeoutを合わせて見直す
- write stepへidempotency keyを追加する
- stagingで429、503、timeoutを注入する
- retry回数と完了時間を監視する
失敗しやすい点
error messageの文字列一致だけに依存する、巨大なRetry-Afterをそのまま採用する、promise rejectionを考慮しない、attempt numberの起点を誤る、writeを非idempotentなまま再試行する、といった問題が起きやすい。
リスク
retry storm、workflow滞留、外部API費用増加、二重決済・二重通知、timeout超過がある。dynamic functionが複雑になると、障害時に次回実行時刻を説明できなくなる。計算結果をstructured logへ残すべきである。
評価方法
- error type別retry回数
- successまでの平均attempt数
- provider rate limit再発率
- retry後の重複処理件数
- workflow完了時間
- maximum delay到達件数
- permanent errorの誤retry率
- 外部API request数と費用
ロールバック
dynamic functionをfeature flagで切り替え、問題時は既知の固定delayとexponential backoffへ戻す。進行中instanceへの影響を確認し、新規instanceだけ旧設定へ切り替える。重複処理が疑われる場合はdownstream recordをidempotency keyで照合する。
編集部分析
retryは可用性機能であると同時に、障害を増幅する機能でもある。dynamic delayの価値は待ち時間を賢くすることではなく、failure semanticsをcodeとして明示することにある。error分類、idempotency、observabilityがない状態で導入すると、挙動が読みにくくなるだけである。
実務チェックリスト
- [ ] retry対象と永久errorを分類した
- [ ] delayの最小・最大を設定した
- [ ] jitterを追加した
- [ ] writeをidempotentにした
- [ ] 429・503・timeoutをtestした
- [ ] delay計算をlogへ残す
- [ ] 固定backoffへの切替を用意した