メインコンテンツまでスキップ

Durable Objects

Durable Objectsでデータを保存する方法と、更新・削除のときにデータがどう扱われるかを説明します。

現在、データの保存に使えるのはDurable ObjectsとR2です。 D1とKVは登録できません。

保存先向いているもの
Durable Objects行と列で扱うデータ、ルームごとの状態、WebSocketによるリアルタイムの配信
R2写真や帳票などのファイル

Durable Objectsを宣言する​

Durable Objectsのクラスを追加するには、次の手順で宣言します。

  1. wrangler.jsoncのdurable_objects.bindingsにバインディングを追加する
  2. wrangler.jsoncのmigrationsに、新しいタグでクラスを追加する
  3. Envの型にバインディングを追加する
  4. クラスをWorkerのエントリーポイントからexportする
wrangler.jsonc
{
"durable_objects": {
"bindings": [
{ "name": "ITEM_STORE", "class_name": "ItemStore" },
{ "name": "COUNTER_STORE", "class_name": "CounterStore" }
]
},

"migrations": [
{ "tag": "v1", "new_sqlite_classes": ["ItemStore"] },
{ "tag": "v2", "new_sqlite_classes": ["CounterStore"] }
]
}

新しいクラスには、SQLiteのストレージを使うnew_sqlite_classesを指定してください。 wrangler.jsoncはZIPに含まれ、そのまま本番のWorkerに適用されます。

migrationsは追記だけにする​

migrationsは累積の記録です。 Cloudflareは適用済みのタグを記録し、それより後のタグだけを適用します。

  • 適用済みのエントリーは変更、削除しないでください。変更すると登録がエラーになります。
  • クラスを追加するときは、新しいタグのエントリーを末尾に追加します。
  • クラスの名前を変えるときは、renamed_classesを使います。データは引き継がれます。
  • 登録済みのクラスをwrangler.jsoncから外しただけのZIPは、登録がエラーになります。
危険

deleted_classesでクラスを削除すると、そのクラスのデータはすべて失われます。 プラットフォームは削除を止めません。登録が成功した時点でデータは元に戻せません。 クラスは削除せず、追加だけにしてください。

インスタンスの分け方​

Durable Objectsのクラスは、IDごとに別のインスタンスを持ちます。 インスタンスはそれぞれ専用のSQLiteデータベースを持ちます。

まずは、固定の名前でクラスごとに1つのインスタンスを使うことをおすすめします。 通常のデータベースと同じように、テーブルと行でデータを扱えます。

const stub = env.ITEM_STORE.get(env.ITEM_STORE.idFromName('items'))

次の場合は、ルームごとなどにインスタンスを分けます。

  • 所有者ごとにデータを分離したい
  • 1つのインスタンスでは処理が追いつかない(インスタンスはリクエストを1つずつ処理します)

インスタンスを分けると、インスタンスをまたぐ検索や集計はできなくなります。 Workerからインスタンスの一覧も取得できません。

読み取りと書き込みを組み合わせる処理は、Durable Objectsのメソッドの中にまとめてください。 Workerから読み取りと書き込みを別々に呼ぶと、その間に別のリクエストが処理され、更新を失うことがあります。

スキーマを移行する​

プラットフォームは、Durable Objectsのインスタンスの中のテーブルを変更しません。 スキーマを変更する場合、既存のインスタンスのテーブルはコードで移行する必要があります。

simple-api-with-durable-objectsテンプレートのsrc/migrations.tsは、次の方法で移行します。

  • クラスごとに、移行の手順を追記だけのリストとして持つ
  • コンストラクターで、そのインスタンスにまだ適用していない手順を順に適用する
  • 適用済みの手順を_schema_migrationsテーブルに記録する
export const ITEM_STORE_MIGRATIONS: string[][] = [
[`CREATE TABLE items (...)`],
[
`ALTER TABLE items ADD COLUMN updated_at TEXT NOT NULL DEFAULT ''`,
`UPDATE items SET updated_at = created_at`,
],
// 変更はここに追記する
]

移行では次の点に注意してください。

  • 適用済みの手順は変更、削除しないでください。
  • ALTER TABLEで追加する列の既定値に、datetime('now')のような式は使えません。テーブルに行があるとエラーになります。定数を既定値にして追加し、既存の行はUPDATEで埋めてください。
  • Durable ObjectsのSQLiteではPRAGMA user_versionを使えません。
  • 列は削除しないでください。移行は前進のみで、削除した列のデータは戻りません。
  • 移行に失敗すると、コンストラクターで毎回例外が発生し、そのインスタンスへのリクエストはすべて失敗します。

移行をテストする​

pnpm devはDurable Objectsのデータを.wrangler/stateに保存します。 同じディレクトリーでは、一度適用した手順は再び実行されません。 登録の前に、空のデータと既存のデータの両方で移行を確認してください。

# 空のディレクトリー: すべての手順を順に適用する
pnpm dev --persist-to /tmp/fresh

# 以前のバージョンで書き込んだデータ
pnpm dev --persist-to /tmp/upgraded

2つ目は、以前のバージョンのプラグインを同じ--persist-toで起動し、データを書き込んでから新しいバージョンを起動します。

定期実行​

Cron Triggersは使えません。 wrangler.jsoncでtriggersを宣言すると、登録がエラーになります。 定期的な処理には、Durable ObjectsのAlarmを使ってください。

更新時のデータの扱い​

新しいZIPを登録すると、コードは置き換わり、データは残ります。 以前のZIPを登録し直しても、元に戻るのはコードだけです。

対象新しいZIPを登録したとき以前のZIPを登録し直したとき
コード置き換わる戻る
静的ファイル置き換わる(assets/がないZIPでは配信されなくなる)戻る
Durable Objectsのデータ残る戻らない
R2のファイル残る戻らない
シークレット残る残る

以前のコードが新しいスキーマのデータの上で動作する点に注意してください。

警告

プラットフォームは登録されたZIPを保存しません。 以前のバージョンに戻すには、以前のZIPをもう一度登録する必要があります。 登録したZIPは手元で保管してください。

ワーカープラグインを削除すると、Durable Objectsのデータとシークレットはすべて失われ、元に戻せません。

管理画面でデータを確認する​

組織管理者は、管理画面でDurable Objectsのテーブルと行を確認し、SQLを実行できます。 一度でも起動したインスタンスが表示されます。 操作はワーカープラグイン管理を参照してください。

警告

管理画面から変更したデータは、コードの移行の記録(_schema_migrationsなど)と食い違う可能性があります。