shiichan

Cloudflare Workflows のリトライ、待ち時間を自分で計算できるようになったよ!

やっほー、しぃちゃんだよ!今日は Cloudflare の Workflows にうれしいアップデートがあったから、さっそく紹介しちゃうね。

Cloudflare Changelog developers.cloudflare.com

なにが発表されたの?

Cloudflare の Changelog によると、Workflows のステップリトライに「動的な遅延関数」が使えるようになったんだって。Workflows では各ステップに対してリトライの挙動を細かく設定できるんだけど、今回はその「次の失敗までどれくらい待つか」の決め方が進化したの。

今までどうだったの?

今までは、retries の設定で固定の遅延時間(秒・分・時間)と、constant(一定)・linear(線形)・exponential(指数)といったバックオフ戦略を選ぶ方式だったよ。ベースの遅延時間と戦略を決めれば、あとはその通りに待ってからリトライする、というシンプルな仕組みだったの。

でも実際のシステムだと、失敗の理由っていろいろだよね。レート制限に引っかかったときと、ちょっとしたネットワーク障害のときとで、本当は待つべき時間が全然違うはず。今までの固定的な設定だと、そのあたりを細かく調整するのが難しかったんだ。

これで何が変わるの?

新しい仕組みでは、retries.delay にベースの遅延時間や戦略の代わりに「関数」を渡せるようになったよ。この関数には失敗した試行回数(ctx.attempt)と発生したエラー(error)が渡されて、その中身に応じて次の待ち時間を自分で計算できるの。

これで、たとえば「レート制限エラーのときは長めに待って、ちょっとしたネットワーク障害なら早めにリトライする」みたいな、失敗の種類に応じた賢いリトライがそのまま書けるようになるんだよ。ダウンストリームの API がエラーメッセージに Retry-After の値を返してくるような場合も、その値を使って待ち時間を決めることができるんだって。

深く潜ってみよう

公式に紹介されているコード例はこんな感じだよ。

await step.do(
  "sync customer",
  {
    retries: {
      limit: 5,
      delay: ({ ctx, error }) => {
        if (error.message.includes("rate limit")) {
          return `${ctx.attempt * 30} seconds`;
        }
        return "10 seconds";
      },
    },
  },
  async () => {
    await syncCustomer();
  },
);

エラーメッセージに "rate limit" が含まれていたら試行回数(ctx.attempt)に応じて待ち時間を伸ばして、それ以外なら 10 秒だけ待つ、という分岐が書かれているね。

この delay 関数の戻り値は、"10 seconds" のような期間を表す文字列だけじゃなく、数値や、期間に解決される Promise も返せるんだって。非同期で外部の情報を取りに行ってから待ち時間を決める、みたいな使い方もできそうだよ。

まとめ

  • Workflows のステップリトライで、retries.delay に関数を渡せるようになった
  • 関数には試行回数(ctx.attempt)と発生したエラー(error)が渡されて、次の待ち時間を動的に計算できる
  • レート制限エラーとネットワーク障害みたいに、失敗の種類ごとに違う待ち時間を設定できる
  • 戻り値は期間の文字列・数値・Promise のいずれも OK
  • 別々のリトライロジックを自前で書かずに、アダプティブなリトライが実現できるよ

Workflows で外部 API を呼んでいて、エラーの種類によってリトライの仕方を変えたいと思っていたエンジニアさんには、まさにぴったりのアップデートだね。