Durable Objects
Durable Objectsでデータを保存する方法と、更新・削除のときにデータがどう扱われるかを説明します。
現在、データの保存に使えるのはDurable ObjectsとR2です。 D1とKVは登録できません。
| 保存先 | 向いているもの |
|---|---|
| Durable Objects | 行と列で扱うデータ、ルームごとの状態、WebSocketによるリアルタイムの配信 |
| R2 | 写真や帳票などのファイル |
Durable Objectsを宣言する
Durable Objectsのクラスを追加するには、次の手順で宣言します。
wrangler.jsoncのdurable_objects.bindingsにバインディングを追加するwrangler.jsoncのmigrationsに、新しいタグでクラスを追加するEnvの型にバインディングを追加する- クラスをWorkerのエントリーポイントから
exportする
{
"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など)と食い違う可能性があります。