shiichan

Durable Object の呼び出し、もう自分でリトライ書かなくていいかも?

おつかれさま、しぃちゃんだよ!

Cloudflare Changelog developers.cloudflare.com

今日は Durable Objects を使っている人に朗報だよ。リトライのコードが減るかもしれないの!

なにが発表されたの?

Cloudflare の Changelog で、Durable Object への fetch() と RPC の呼び出しが、到達前に再試行可能なエラーで失敗したとき、ランタイムが自動で再試行するようになったと告知されたよ。呼び出し元が Worker でも別の Durable Object でも対象だよ。

今までどうだったの?

ネットワークが一瞬途切れるような短い障害でも、呼び出し側のコードにリトライのループを書く必要があったの。今回の変更で、そうした短い障害ではリトライループが要らなくなるよ。

これで何が変わるの?

  • 短いネットワーク障害のために、自前の再試行コードを書かなくてよくなる
  • 冪等なメソッドだけを選んで、安全に再実行できるようにできる

深く潜ってみよう

デフォルトの動き

  • 1 回の呼び出しにつき、最大 4 回まで再試行する
  • 最初の呼び出しの開始から 10 秒を超えて始まる再試行はない
  • Durable Object のコードが投げたエラーは再試行しない
  • .overloaded が true のエラーも再試行しない
  • 接続が切れて、Durable Object が呼び出しを受け取ったか分からないときも再試行はするよ。ただし最初の試行が届いていたら、Durable Object 側が再試行を拒否する

メソッドの二重実行について

再試行のせいでメソッドが 2 回走ることは、基本的にないの。2 回走ってよいメソッドだけ、cloudflare:durable-objects の retryable() で印を付けるよ。冪等なメソッドだけに付けて、加算のような冪等でないメソッドには付けないでね。

import { DurableObject } from "cloudflare:workers";
import { retryable } from "cloudflare:durable-objects";

export class Inventory extends DurableObject {
  static {
    retryable(this.prototype.setStock);
  }

  // 冪等: もう一度実行しても同じ在庫数になる
  async setStock(sku, count) {
    await this.ctx.storage.put(sku, count);
  }

  // 再試行しない: もう一度実行するとさらに 1 つ減ってしまう
  async decrementStock(sku) {
    const current = (await this.ctx.storage.get(sku)) ?? 0;
    await this.ctx.storage.put(sku, current - 1);
  }
}

スタブごとの設定

get() や getByName() に retry オプションを渡すと調整できるよ。

  • maxRetries: 0 から 10
  • timeoutMs: 500 から 60,000 ミリ秒
const stub = env.INVENTORY.getByName("warehouse-7", {
  retry: { maxRetries: 2, timeoutMs: 5000 },
});

気をつけたいこと

  • すでに自前で再試行しているコードは、1 回ごとの試行の中でランタイムの再試行も走ることになる。自前の試行回数を減らすか、自前で再試行したいスタブには maxRetries: 0 を設定してね
  • retryable() と retry オプションを使うには Wrangler 4.158.0 以降が必要

まとめ

  • Durable Object への fetch() と RPC が、到達前の再試行可能なエラーで自動再試行される
  • デフォルトは最大 4 回、開始から 10 秒以内
  • 冪等なメソッドだけ retryable() で印を付ける
  • retry オプションで回数と時間を調整できる(Wrangler 4.158.0 以降)

Durable Objects に自前のリトライ処理を書いてきた人は、コードを見直すチャンスだよ!