Durable Object Storage API を使うと、Durable Objects はトランザクション対応で強い一貫性を持つストレージにアクセスできます。Durable Object に付属するストレージは、その一意なインスタンス専用であり、ほかのオブジェクトからはアクセスできません。
Durable Object Storage API には、SQL、ポイントインタイムリカバリ(PITR)、キーバリュー(KV)、アラーム API など、いくつかのメソッドがあります。利用できる API メソッドは、Durable Objects クラスのストレージバックエンドが SQLite か KV かによって異なります。
| メソッド 1 | SQLite バックエンドの Durable Object クラス | KV バックエンドの Durable Object クラス |
|---|---|---|
| SQL API | ✅ | ❌ |
| PITR API | ✅ | ❌ |
| Synchronous KV API | ✅ 2, 3 | ❌ |
| Asynchronous KV API | ✅ 3 | ✅ |
| Alarms API | ✅ | ✅ |
脚注
1 各メソッドは暗黙的にトランザクションでラップされます。複数のキーバリューペアにアクセスする場合でも、結果はアトミックであり、ほかのストレージ操作から分離されます。
2 get()、put()、delete()、list() などの KV API メソッドは、隠し SQLite テーブル __cf_kv にデータを保存します。テーブル一覧ではこのテーブルを確認できますが、SQL API から中身へアクセスすることはできません。
3 SQLite バックエンドの Durable Objects は ctx.storage.kv を使う 同期 KV API メソッド も利用します。一方、KV バックエンドの Durable Objects が提供するのは 非同期 KV API メソッド だけです。
Durable Objects は、DurableObjectStorage インターフェイス経由で Storage API にアクセスし、DurableObjectState::storage プロパティで使います。Durable Object コンストラクターに渡される ctx パラメーターを使い、this.ctx.storage として参照することが多いです。
次のコードスニペットは、Durable Object Storage API でデータを保存・取得する方法を示します。
export class Counter extends DurableObject {
constructor(ctx, env) {
super(ctx, env);
}
async increment() {
let value = (await this.ctx.storage.get("value")) || 0;
value += 1;
await this.ctx.storage.put("value", value);
return value;
}
}export class Counter extends DurableObject {
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
}
async increment(): Promise<number> {
let value: number = (await this.ctx.storage.get("value")) || 0;
value += 1;
await this.ctx.storage.put("value", value);
return value;
}
}from workers import DurableObject
class Counter(DurableObject):
def __init__(self, ctx, env):
super().__init__(ctx, env)
async def increment(self):
value = (await self.ctx.storage.get("value")) or 0
value += 1
await self.ctx.storage.put("value", value)
return valueJavaScript はシングルスレッドで、イベント駆動のプログラミング言語です。そのため JavaScript ランタイムは、デフォルトではリクエスト同士のインターリーブを許可し、並行性のバグにつながることがあります。Durable Objects ランタイムは、input gates と output gates を組み合わせて、ストレージ操作時にこの種の並行性バグを避けます。詳細は ブログ記事 ↗ を参照してください。
KV バックエンド Durable Objects は、非同期の KV API メソッドを提供します。
-
ctx.storage.get(key:string, optionsObject任意)Promise<any>- 指定したキーに対応する値を取得します。戻り値の型は、そのキーに以前書き込まれた値と同じです。キーが存在しない場合は undefined です。
-
ctx.storage.get(keys:Array<string>, optionsObject任意)Promise<Map<string, any>>- 指定した各キーに対応する値を取得します。
Map↗ 内の各戻り値の型は、対応するキーに以前書き込まれた値と同じです。Mapの結果は UTF-8 エンコーディングの昇順で並び、存在しないキーは省略されます。一度に最大 128 個のキーを指定できます。
- 指定した各キーに対応する値を取得します。
-
allowConcurrency:boolean- デフォルトでは、予期しない競合状態を避けるため、ストレージ操作の実行中は Object への I/O イベントの配信を一時停止します。この動作を無効にして同時イベントの配信を許可するには、
allowConcurrency: trueを渡します。
- デフォルトでは、予期しない競合状態を避けるため、ストレージ操作の実行中は Object への I/O イベントの配信を一時停止します。この動作を無効にして同時イベントの配信を許可するには、
-
noCache:boolean- true の場合、キーと値はメモリ内キャッシュに挿入されません。キーがすでにキャッシュにある場合はキャッシュされた値を返しますが、最終使用時刻は更新しません。近い将来このキーを使わない見込みのときに使います。このフラグはヒントです。コードの意味は変わりませんが、パフォーマンスに影響する場合があります。
-
put(key:string, valueany, optionsObject任意)Promise-
値を保存し、指定したキーに関連付けます。値は structured clone algorithm ↗ がサポートする任意の型を使えます。ほとんどの型が対象です。
キーと値のサイズ上限は、利用している Durable Object のストレージバックエンドによって異なります。次のいずれかを参照してください。
KV バックエンドの Durable Object では、シリアライズ後の値が 128 KiB(131072 バイト)の値サイズ上限を超えると、書き込みが適用される前に
put()がRangeErrorを投げます(例:Values cannot be larger than 131072 bytes.)。
-
-
put(entries:Object, optionsObject任意)Promise- オブジェクトを受け取り、各キーと値をストレージに保存します。
- 各値は structured clone algorithm ↗ がサポートする任意の型を使えます。ほとんどの型が対象です。
- 一度に最大 128 個のキーと値のペアを指定できます。キーと値のサイズ上限は、利用している Durable Object の種類によって異なります。次のいずれかを参照してください。
-
delete(key:string, optionsObject任意)Promise<boolean>- キーと対応する値を削除します。キーが存在した場合は
true、存在しなかった場合はfalseを返します。
- キーと対応する値を削除します。キーが存在した場合は
-
delete(keys:Array<string>, optionsObject任意)Promise<number>- 指定したキーと対応する値を削除します。一度に最大 128 個のキーを指定できます。削除したキーと値のペアの数を返します。
-
put()、delete()、deleteAll()は次のオプションをサポートします。 -
allowUnconfirmedboolean-
デフォルトでは、以前の書き込みがディスクへフラッシュされたことを確認するまで、Durable Object からの送信ネットワークメッセージを一時停止します。書き込みが失敗した場合、システムは Object をリセットし、送信中のメッセージをすべて破棄して、クライアントにはエラーを返します。
-
こうすることで、書き込みが実際に成功しない限り外部から Object の動作は観測できないため、Durable Objects は書き込みが完了する前に確定してしまう心配なく、書き込みと並行して実行を続けられます。
-
書き込みのあと、後続のネットワークメッセージがわずかに遅れることがあります。未確認の書き込みを前提に通信しても問題ないアプリケーションもあります。ネットワークトラフィックをすぐに許可したいプログラムもあります。その場合は
allowUnconfirmedをtrueに設定し、デフォルトの動作を無効にします。 -
一部の送信ネットワークメッセージだけをすぐに進めたい場合は、
allowUnconfirmedオプションで進めたいメッセージのブロックを避け、別途sync()を呼びます。sync()は、以前の書き込みがすべてディスクへ正常に永続化されたときにだけ解決する Promise を返します。
-
-
noCacheboolean-
true の場合、ディスクへの書き込みが完了した時点で、キーと値はメモリから破棄されます。
-
近い将来キーを使わない場合は
noCacheを使います。noCacheはコードの意味を変えませんが、パフォーマンスに影響する場合があります。 -
書き込み完了前に
get()でキーを取得した場合は、書き込みバッファーのコピーが返されます。これにより、最新のput()呼び出しとの一貫性が保たれます。
-
list(options:Object任意)Promise<Map<string, any>>
-
startstring- リスト結果の開始キーです。このキーを含みます。
-
startAfterstring- リスト結果の開始位置となるキーの直後です。このキーは含みません。
startと同時には使えません。
- リスト結果の開始位置となるキーの直後です。このキーは含みません。
-
endstring- リスト結果の終了キーです。このキーは含みません。
-
prefixstring- キーがこのプレフィックスで始まるキーと値のペアだけに結果を限定します。
-
reverseboolean- true の場合、デフォルトの昇順ではなく降順で結果を返します。
reverseを有効にしても、start、startKey、endKeyの意味は変わりません。startは辞書順で返せる最小のキー(含む)を定義し、降順リストでは実質的な終点になります。endは辞書順でリストが対象とする最大のキー(含まない)を定義し、降順リストでは実質的な始点になります。
-
limitnumber- 返すキーと値のペアの最大数です。
-
allowConcurrencyboolean- 上記の
get()のオプションと同じです。
- 上記の
-
noCacheboolean- 上記の
get()のオプションと同じです。
- 上記の
getAlarm(options:Object任意)Promise<Number | null>- 現在のアラーム時刻(設定されている場合)を、エポックからの整数ミリ秒として取得します。アラームは、まだ開始していない場合、または失敗して再試行が始まっていない場合に、設定済みとみなされます。アラームが設定されていない場合、
getAlarm()はnullを返します。
- 現在のアラーム時刻(設定されている場合)を、エポックからの整数ミリ秒として取得します。アラームは、まだ開始していない場合、または失敗して再試行が始まっていない場合に、設定済みとみなされます。アラームが設定されていない場合、
get()と同じオプションです。ただしnoCacheはありません。
-
setAlarm(scheduledTime:Date | number, optionsObject任意)Promise- 現在のアラーム時刻を設定します。JavaScript の
Date、またはエポックからの整数ミリ秒を受け付けます。
setAlarm()にDate.now()以前の時刻を渡した場合、アラームはすぐあとで非同期実行されるようスケジュールされます。このときアラームハンドラーが実行中でも、キャンセルはされません。アラームはミリ秒単位で設定でき、通常は設定時刻の数ミリ秒後に実行されます。ただしメンテナンスやフェイルオーバー中の障害により、最大 1 分遅れることがあります。 - 現在のアラーム時刻を設定します。JavaScript の
deleteAlarm(options:Object任意)Promise- アラームが存在する場合は削除します。アラームハンドラーが実行中の場合はキャンセルしません。
setAlarm()とdeleteAlarm()はput()と同じオプションをサポートします。ただしnoCacheはありません。
deleteAll(options:Object任意)Promise- 保存されているデータをすべて削除し、Durable Object が使っているストレージを実質的に解放します。キーバリューストレージバックエンドの Durable Object では、
deleteAll()はその Durable Object のすべてのキーと対応する値を削除します。SQLite ストレージバックエンド の Durable Object では、deleteAll()はその Durable Object のプライベート SQLite データベースの内容をすべて削除します。SQL データとキーバリューデータの両方が対象です。 - キーバリューストレージバックエンドの Durable Object では、進行中の
deleteAll()が失敗し、一部のデータだけが残ることがあります。SQLite ストレージバックエンドの Durable Object では、deleteAll()はアトミック(すべて成功するか、まったく実行されないか)なので、部分削除の問題はありません。 - 互換日付が
2026-02-24以降の Workers では、deleteAll()は有効な アラーム も削除します。それより前の互換日付では、deleteAll()はアラームを削除しません。別途deleteAlarm()を使うか、delete_all_deletes_alarm互換フラグ を有効にしてください。
- 保存されているデータをすべて削除し、Durable Object が使っているストレージを実質的に解放します。キーバリューストレージバックエンドの Durable Object では、
transactionSync(callback):any-
SQLite バックエンドの Durable Object でのみ利用できます。
-
callback()をトランザクションで包んで実行し、その結果を返します。 -
callback()が例外を投げた場合、トランザクションはロールバックされます。 -
コールバックは同期的に完了する必要があります。つまり、
asyncとして宣言したり、Promise を返したりしてはいけません。トランザクションに含められるのは同期的なストレージ操作だけです。これは、同期的に完了するctx.storage.sql.exec()を使った SQL クエリ向けです。
-
-
transaction(closureFunction(txn)):Promise-
txnに対して呼ばれた一連のストレージ操作を、1 つのトランザクションとして実行します。コミットに成功するか、中止されます。 -
明示的なトランザクションは、もはや必要ありません。間に
awaitを挟まない一連の書き込み操作は、自動的にアトミックに送信されます。また、読み取り操作をawaitしている間は、システムが同時イベントの実行を防ぎます(allowConcurrency: trueを使っている場合を除く)。そのため、一連の読み取りのあとに一連の書き込み(間にほかの I/O がない場合)は自動的にアトミックになり、トランザクションのように振る舞います。
-
-
txn-
上記で説明した
put()、get()、delete()、list()メソッドに、現在のトランザクションコンテキストでアクセスできます。トランザクションクロージャ内でトランザクションの振る舞いを得るには、トップレベルのctx.storageオブジェクトではなく、txnオブジェクトのメソッドを呼ぶ必要があります。
また、トランザクション中の変更をコミットせずにロールバックするrollback()関数も使えます。rollback()を呼んだあと、txnオブジェクトへの以降の操作は例外で失敗します。rollback()はパラメーターを取らず、呼び出し元に何も返しません。 -
SQLite バックエンドのストレージエンジン を使う場合、
txnオブジェクトは不要です。ctx.storageオブジェクトに対して直接行うストレージ操作(ctx.storage.sql.exec()を使った SQL クエリを含む)は、トランザクションの一部と見なされます。
-
sync():Promise-
保留中の書き込みをディスクへ同期します。
-
自動的な書き込み合体(write coalescing)の通常の振る舞いと似ています。書き込みバッファーに保留中の書き込みがある場合(
allowUnconfirmedオプション で送信したものも含む)、返される Promise はそれらが完了したときに解決します。保留中の書き込みがない場合、返される Promise はすでに解決済みです。
-