Cloud Service
Durable Objects
強一貫性を持つ状態とコードを1つの単位にまとめ、世界中のエッジから同じ相手に到達できるようにする調停用の実行環境。分散ロックやセッション管理に強い。
- 1.IDごとに単一インスタンスが存在し、状態(メモリ/内蔵SQLite)とロジックを同じ場所に持てる。
- 2.同一オブジェクトへのアクセスは直列化されるため、分散排他制御やカウンタ管理を安全に実装できる。
- 3.WebSocketの集約やアラーム(定時実行)と組み合わせ、チャットやゲーム、キュー処理の調停役として使う。
解決する課題
- 複数リクエスト・複数拠点からアクセスされても、同じ状態に対して一貫した処理をしたい(在庫の排他制御、ルームの参加者管理など)
- WorkersはステートレスなためリクエストごとにDBへ読み書きするとレイテンシと競合が増える → 状態そのものをエッジ上に置きたい
- WebSocket接続を多数の利用者から受けて1か所に集約し、ブロードキャストしたい
- 「一定時間後に処理する」「定期的に起こす」という状態を持った遅延実行をしたい
主要概念と用語
- Durable Object(DO): 状態とコードを1つにまとめたオブジェクト。一意のIDに対して常に同じインスタンスが対応する
- Durable Objectクラス: DOの実体を定義するコード(Workersのコードと同様にJavaScript/TypeScriptで書く)
- ID(名前付き/ユニーク): 名前文字列から決定的に導出するIDと、ランダムに払い出されるIDの2種類がある
- Storage API: 各DOに紐づく永続ストレージ。内蔵SQLiteベースのストレージと、キーバリュー形式のAPIを提供
- 単一スレッド性(直列化): 同一DOインスタンスへの操作は常に1つずつ順番に実行され、同時実行による競合が起きない
- アラーム(Alarms): DOに「将来のある時刻に起こしてほしい」と予約できる仕組み。タイムアウト処理や定期実行に使う
- ロケーションヒント: DOを最初に生成する際、地理的に近いリージョンを指定するオプション
- Hibernation(休止): WebSocket接続を保持したままDO自体は非活性化し、課金対象の稼働時間を抑える仕組み
仕様・制限・クォータ
- 1つのDOインスタンスは単一スレッドで直列実行される。高スループットが必要な処理はID(シャード)を分けて並列化する設計が前提
- ストレージ容量やリクエスト数、CPU時間には利用プランに応じた上限がある。大量データを扱う場合はR2などの併用を検討する
- DOはリクエストのたびに新規生成されるわけではなく、IDに紐づいて長期間存在し続ける(アイドル時は自動的に休止する)
- WebSocketは「Hibernatable WebSockets」機能を使うことで、接続を維持したままDOのメモリ状態を破棄でき、課金対象の稼働時間を抑えられる
- 単一のDOに対する同時書き込みは直列化されるため、極端に高頻度な更新が集中する設計は避けるべきという制約がある
内部の仕組み
Durable Objectは、名前またはIDから一意に決まる論理アドレスを持ち、Cloudflareのネットワーク上のどこかの物理ノードに配置されます。あるIDへのリクエストはネットワーク上でそのDOが存在する場所まで自動的にルーティングされ、同じIDであれば常に同じインスタンスに到達します。
DO内部では、着信したリクエストやRPC呼び出しはキューイングされて1つずつ処理されるため、開発者はロックやミューテックスを自前で書かなくても、状態への読み書きが競合しないコードを書けます。永続化はDOごとに紐づく内蔵ストレージ(SQLiteベース)を介して行われ、メモリ上の状態が失われても再起動時にストレージから復元できます。
DOは初回アクセス時にどこかのノードで生成され、以後はそのID宛のリクエストがそこへ集約されます。利用者が世界中に分散している場合は、代表的なアクセス元に近いロケーションヒントを与えるか、地域ごとにDOを分割する設計が有効です。
設計パターン / ベストプラクティス
- 1エンティティ=1DO: チャットルーム、ゲームセッション、注文などドメイン上の1単位に対して1つのIDを割り当てる
- ホットスポットを避ける: 全ユーザーが1つのDOに集中する設計は直列実行のボトルネックになる。ユーザー単位・ルーム単位などでIDを分散させる
- WebSocketの集約: 多人数チャットやマルチプレイヤーゲームでは、DOが全接続を保持しブロードキャストする役割を担う。Hibernationを使い、アイドル時のコストを抑える
- アラームで期限管理: 「一定時間操作がなければセッションを閉じる」「注文が確定しなければキャンセルする」といったタイムアウト処理はアラームで実装する
- Workersとの役割分担: リクエストの受け口や認証・ルーティングはWorkers、状態を伴う調停はDOという分業にする
運用・監視
- Cloudflareダッシュボードおよびログ基盤(Workers Logs/Logpush)から、DOの呼び出し状況やエラーを確認できる
- CPU時間や例外はWorkersと同様の観測手段(wranglerのtail機能など)でリアルタイムに追える
- 特定のIDに負荷が偏っていないか(ホットスポット化)を、呼び出し頻度の集計から定期的に点検する
- デプロイ時はDOクラスの互換性(ストレージスキーマの変更など)に注意し、段階的なマイグレーションを計画する
コスト
- 課金はリクエスト数、実行時間(DOがアクティブな時間)、ストレージ使用量の組み合わせで決まる
- Hibernationを活用してWebSocket接続中のアイドル時間を非アクティブ化すると、稼働時間ベースのコストを抑えられる
- 無料枠や具体的な単価は変更されうるため、利用前に公式の料金ページで最新情報を確認することが望ましい
セキュリティ
- DOへのアクセスはWorkers経由のバインディングを通じて行われるため、まずWorkers側での認証・認可を適切に実装する
- DO間・DOとWorkers間の通信はCloudflareのネットワーク内で完結し、外部に公開されるのは呼び出し元のWorkersエンドポイントのみにするのが基本
- 保存するデータの機密度に応じて、アプリケーション層での暗号化やアクセス制御を検討する
- 入力値の検証やレート制限は、DOに到達する前段のWorkersやWAFで行うと安全性が高い
関連サービス・比較
| 観点 | Durable Objects | Workers KV |
|---|---|---|
| 得意な処理 | 強一貫性・逐次処理・調停 | 読み取り中心の分散キャッシュ |
| 一貫性 | 単一インスタンスで強一貫性 | 結果整合性(グローバル分散) |
| 状態の持ち方 | コードと状態が同居 | 純粋なキーバリューストア |
| 向いた用途 | チャット・ゲーム・排他制御・キュー | 設定値・静的データの高速配信 |
ハンズオン / CLI例
# プロジェクトのひな形を作成(Durable Objectsテンプレートを選択)
npm create cloudflare@latest my-do-app
# wrangler.jsonc / wrangler.toml にDurable Objectsのバインディングと
# マイグレーション設定を追記した後、ローカルで動作確認
npx wrangler dev
# 本番環境へデプロイ
npx wrangler deploy
# デプロイ後のログをリアルタイムで確認
npx wrangler tail
Cloudflare Service
Durable Objectsを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
コンピューティング
比較で見る軸
クラウド: Cloudflare / カテゴリ: コンピューティング / 難易度: intermediate
導入後に効く点
同一オブジェクトへのアクセスは直列化されるため、分散排他制御やカウンタ管理を安全に実装できる。
先に潰すリスク
サービス単体ではなく、権限、ネットワーク、監視、課金、バックアップを含めて設計する必要がある。
- クラウド
- Cloudflare
- カテゴリ
- コンピューティング
- 難易度
- intermediate
- 関連資格
- —
- 設計柱
- reliability / performance / operational
判断チェックリスト
- 自社の用途が「コンピューティング / reliability」に近いか確認する。
- 強みである「IDごとに単一インスタンスが存在し、状態(メモリ/内蔵SQLite)とロジックを同じ場所に持てる。」が本当に評価軸になるか確認する。
- 注意点の「サービス単体ではなく、権限、ネットワーク、監視、課金、バックアップを含めて設計する必要がある。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。