cloudflare-durable-objects

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Cloudflare Durable Objects

Cloudflare Durable Objects

Durable Objects combine compute with strongly consistent, transactional storage. Each object has a globally-unique name, enabling coordination across clients worldwide.

Durable Objects 将计算与强一致性的事务性存储相结合。每个对象都有一个全局唯一的名称,可实现全球客户端之间的协调。

Quick Start

快速入门

Durable Object Class

Durable Object 类

typescript
// src/counter.ts
import { DurableObject } from "cloudflare:workers";

export class Counter extends DurableObject<Env> {
  async increment(): Promise<number> {
    let count = (await this.ctx.storage.get<number>("count")) ?? 0;
    count++;
    await this.ctx.storage.put("count", count);
    return count;
  }

  async getCount(): Promise<number> {
    return (await this.ctx.storage.get<number>("count")) ?? 0;
  }
}
typescript
// src/counter.ts
import { DurableObject } from "cloudflare:workers";

export class Counter extends DurableObject<Env> {
  async increment(): Promise<number> {
    let count = (await this.ctx.storage.get<number>("count")) ?? 0;
    count++;
    await this.ctx.storage.put("count", count);
    return count;
  }

  async getCount(): Promise<number> {
    return (await this.ctx.storage.get<number>("count")) ?? 0;
  }
}

Worker Entry Point

Worker 入口文件

typescript
// src/index.ts
export { Counter } from "./counter";

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const id = env.COUNTER.idFromName("global");
    const stub = env.COUNTER.get(id);
    const count = await stub.increment();
    return new Response(`Count: ${count}`);
  },
};
typescript
// src/index.ts
export { Counter } from "./counter";

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const id = env.COUNTER.idFromName("global");
    const stub = env.COUNTER.get(id);
    const count = await stub.increment();
    return new Response(`Count: ${count}`);
  },
};

wrangler.jsonc

wrangler.jsonc

jsonc
{
  "name": "counter-worker",
  "main": "src/index.ts",
  "durable_objects": {
    "bindings": [
      {
        "name": "COUNTER",
        "class_name": "Counter"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["Counter"]
    }
  ]
}
jsonc
{
  "name": "counter-worker",
  "main": "src/index.ts",
  "durable_objects": {
    "bindings": [
      {
        "name": "COUNTER",
        "class_name": "Counter"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["Counter"]
    }
  ]
}

Deploy

部署

bash
npx wrangler deploy

bash
npx wrangler deploy

Core Concepts

核心概念

DurableObjectState

DurableObjectState

Available as
this.ctx
in Durable Object class:
typescript
interface DurableObjectState {
  readonly id: DurableObjectId;
  readonly storage: DurableObjectStorage;

  blockConcurrencyWhile<T>(callback: () => Promise<T>): Promise<T>;
  waitUntil(promise: Promise<any>): void; // No effect in DO

  // WebSocket Hibernation
  acceptWebSocket(ws: WebSocket, tags?: string[]): void;
  getWebSockets(tag?: string): WebSocket[];
  getTags(ws: WebSocket): string[];
  setWebSocketAutoResponse(pair?: WebSocketRequestResponsePair): void;
  getWebSocketAutoResponse(): WebSocketRequestResponsePair | null;

  abort(message?: string): void; // Force reset DO
}
在Durable Object类中可通过
this.ctx
访问:
typescript
interface DurableObjectState {
  readonly id: DurableObjectId;
  readonly storage: DurableObjectStorage;

  blockConcurrencyWhile<T>(callback: () => Promise<T>): Promise<T>;
  waitUntil(promise: Promise<any>): void; // No effect in DO

  // WebSocket Hibernation
  acceptWebSocket(ws: WebSocket, tags?: string[]): void;
  getWebSockets(tag?: string): WebSocket[];
  getTags(ws: WebSocket): string[];
  setWebSocketAutoResponse(pair?: WebSocketRequestResponsePair): void;
  getWebSocketAutoResponse(): WebSocketRequestResponsePair | null;

  abort(message?: string): void; // Force reset DO
}

DurableObjectId

DurableObjectId

typescript
const id = env.MY_DO.idFromName("user-123"); // Deterministic ID
const id = env.MY_DO.newUniqueId(); // Random unique ID
const stub = env.MY_DO.get(id); // Get stub for DO instance
See api.md for full type definitions.

typescript
const id = env.MY_DO.idFromName("user-123"); // 确定性ID
const id = env.MY_DO.newUniqueId(); // 随机唯一ID
const stub = env.MY_DO.get(id); // 获取DO实例的存根
完整类型定义请查看api.md

Storage API (SQLite-backed)

存储API(基于SQLite)

SQLite is the recommended storage backend for new Durable Objects.
SQLite是新Durable Objects推荐使用的存储后端。

SQL API

SQL API

typescript
const cursor = this.ctx.storage.sql.exec("SELECT * FROM users WHERE id = ?", userId);

// Get single row (throws if not exactly one)
const user = cursor.one();

// Get all rows
const users = cursor.toArray();

// Iterate
for (const row of cursor) {
  console.log(row);
}
typescript
const cursor = this.ctx.storage.sql.exec("SELECT * FROM users WHERE id = ?", userId);

// 获取单行数据(如果不是恰好一行则抛出异常)
const user = cursor.one();

// 获取所有行
const users = cursor.toArray();

// 迭代
for (const row of cursor) {
  console.log(row);
}

SQL Cursor Properties

SQL游标属性

typescript
cursor.columnNames; // string[]
cursor.rowsRead; // number
cursor.rowsWritten; // number
typescript
cursor.columnNames; // string[]
cursor.rowsRead; // number
cursor.rowsWritten; // number

Create Tables

创建表

typescript
this.ctx.storage.sql.exec(`
  CREATE TABLE IF NOT EXISTS users (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    created_at INTEGER DEFAULT (unixepoch())
  )
`);
typescript
this.ctx.storage.sql.exec(`
  CREATE TABLE IF NOT EXISTS users (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    created_at INTEGER DEFAULT (unixepoch())
  )
`);

Insert/Update

插入/更新

typescript
this.ctx.storage.sql.exec("INSERT INTO users (id, name) VALUES (?, ?)", id, name);

this.ctx.storage.sql.exec("UPDATE users SET name = ? WHERE id = ?", newName, id);
typescript
this.ctx.storage.sql.exec("INSERT INTO users (id, name) VALUES (?, ?)", id, name);

this.ctx.storage.sql.exec("UPDATE users SET name = ? WHERE id = ?", newName, id);

Transactions

事务

typescript
// Synchronous transaction (SQLite only)
this.ctx.storage.transactionSync(() => {
  this.ctx.storage.sql.exec("INSERT INTO logs (msg) VALUES (?)", "start");
  this.ctx.storage.sql.exec("UPDATE counters SET value = value + 1");
});
typescript
// 同步事务(仅SQLite支持)
this.ctx.storage.transactionSync(() => {
  this.ctx.storage.sql.exec("INSERT INTO logs (msg) VALUES (?)", "start");
  this.ctx.storage.sql.exec("UPDATE counters SET value = value + 1");
});

Database Size

数据库大小

typescript
const sizeBytes = this.ctx.storage.sql.databaseSize;
See storage.md for KV API and advanced usage.

typescript
const sizeBytes = this.ctx.storage.sql.databaseSize;
KV API及高级用法请查看storage.md

Storage API (KV)

存储API(KV)

Synchronous KV (SQLite-backed)

同步KV(基于SQLite)

typescript
this.ctx.storage.kv.put("key", value);
const val = this.ctx.storage.kv.get("key");
const deleted = this.ctx.storage.kv.delete("key");

for (const [key, value] of this.ctx.storage.kv.list()) {
  console.log(key, value);
}
typescript
this.ctx.storage.kv.put("key", value);
const val = this.ctx.storage.kv.get("key");
const deleted = this.ctx.storage.kv.delete("key");

for (const [key, value] of this.ctx.storage.kv.list()) {
  console.log(key, value);
}

Async KV (Both backends)

异步KV(支持两种后端)

typescript
await this.ctx.storage.put("key", value);
const val = await this.ctx.storage.get<MyType>("key");

// Batch operations (up to 128 keys)
const values = await this.ctx.storage.get(["key1", "key2", "key3"]);
await this.ctx.storage.put({ key1: val1, key2: val2 });
await this.ctx.storage.delete(["key1", "key2"]);

// List with options
const map = await this.ctx.storage.list({ prefix: "user:" });

// Delete all
await this.ctx.storage.deleteAll();
typescript
await this.ctx.storage.put("key", value);
const val = await this.ctx.storage.get<MyType>("key");

// 批量操作(最多128个键)
const values = await this.ctx.storage.get(["key1", "key2", "key3"]);
await this.ctx.storage.put({ key1: val1, key2: val2 });
await this.ctx.storage.delete(["key1", "key2"]);

// 带选项的列表查询
const map = await this.ctx.storage.list({ prefix: "user:" });

// 删除所有数据
await this.ctx.storage.deleteAll();

Write Coalescing

写入合并

Multiple writes without
await
are coalesced atomically:
typescript
// These are batched into single transaction
this.ctx.storage.put("a", 1);
this.ctx.storage.put("b", 2);
this.ctx.storage.put("c", 3);
// All committed together

多个不带
await
的写入操作会被自动合并为原子操作:
typescript
// 这些操作会被批处理为单个事务
this.ctx.storage.put("a", 1);
this.ctx.storage.put("b", 2);
this.ctx.storage.put("c", 3);
// 所有操作会一起提交

Alarms

告警

Schedule single alarm per Durable Object for background processing.
可为每个Durable Object调度单个告警以进行后台处理。

Set Alarm

设置告警

typescript
// Schedule 1 hour from now
await this.ctx.storage.setAlarm(Date.now() + 60 * 60 * 1000);

// Schedule at specific time
await this.ctx.storage.setAlarm(new Date("2024-12-31T00:00:00Z"));
typescript
// 从现在起1小时后调度
await this.ctx.storage.setAlarm(Date.now() + 60 * 60 * 1000);

// 在特定时间调度
await this.ctx.storage.setAlarm(new Date("2024-12-31T00:00:00Z"));

Handle Alarm

处理告警

typescript
export class MyDO extends DurableObject<Env> {
  async alarm(info?: AlarmInfo): Promise<void> {
    console.log(`Alarm fired! Retry: ${info?.isRetry}, count: ${info?.retryCount}`);

    // Process scheduled work
    await this.processScheduledTasks();

    // Schedule next alarm if needed
    const nextRun = await this.getNextScheduledTime();
    if (nextRun) {
      await this.ctx.storage.setAlarm(nextRun);
    }
  }
}
typescript
export class MyDO extends DurableObject<Env> {
  async alarm(info?: AlarmInfo): Promise<void> {
    console.log(`告警触发!重试:${info?.isRetry}, 次数:${info?.retryCount}`);

    // 处理调度任务
    await this.processScheduledTasks();

    // 如有需要,调度下一次告警
    const nextRun = await this.getNextScheduledTime();
    if (nextRun) {
      await this.ctx.storage.setAlarm(nextRun);
    }
  }
}

Alarm Methods

告警方法

typescript
await this.ctx.storage.setAlarm(timestamp); // Set/overwrite alarm
const time = await this.ctx.storage.getAlarm(); // Get scheduled time (ms) or null
await this.ctx.storage.deleteAlarm(); // Cancel alarm
Retry behavior: Alarms retry with exponential backoff (2s initial, up to 6 retries) on exceptions.
See alarms.md for patterns.

typescript
await this.ctx.storage.setAlarm(timestamp); // 设置/覆盖告警
const time = await this.ctx.storage.getAlarm(); // 获取调度时间(毫秒)或null
await this.ctx.storage.deleteAlarm(); // 取消告警
重试机制:当出现异常时,告警会以指数退避方式重试(初始2秒,最多6次重试)。
模式示例请查看alarms.md

WebSocket Hibernation

WebSocket休眠

Keep WebSocket connections alive while Durable Object hibernates.
在Durable Object休眠时保持WebSocket连接存活。

Accept WebSocket

接受WebSocket连接

typescript
export class ChatRoom extends DurableObject<Env> {
  async fetch(request: Request): Promise<Response> {
    const upgradeHeader = request.headers.get("Upgrade");
    if (upgradeHeader === "websocket") {
      const [client, server] = Object.values(new WebSocketPair());

      // Accept with hibernation support
      this.ctx.acceptWebSocket(server, ["user:123"]); // Optional tags

      return new Response(null, { status: 101, webSocket: client });
    }
    return new Response("Expected WebSocket", { status: 400 });
  }

  async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): Promise<void> {
    // Handle incoming message (DO wakes from hibernation)
    this.broadcast(message);
  }

  async webSocketClose(ws: WebSocket, code: number, reason: string): Promise<void> {
    // Handle disconnect
  }
}
typescript
export class ChatRoom extends DurableObject<Env> {
  async fetch(request: Request): Promise<Response> {
    const upgradeHeader = request.headers.get("Upgrade");
    if (upgradeHeader === "websocket") {
      const [client, server] = Object.values(new WebSocketPair());

      // 接受连接并支持休眠
      this.ctx.acceptWebSocket(server, ["user:123"]); // 可选标签

      return new Response(null, { status: 101, webSocket: client });
    }
    return new Response("预期为WebSocket连接", { status: 400 });
  }

  async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): Promise<void> {
    // 处理收到的消息(DO从休眠中唤醒)
    this.broadcast(message);
  }

  async webSocketClose(ws: WebSocket, code: number, reason: string): Promise<void> {
    // 处理断开连接
  }
}

Broadcast to All

广播到所有连接

typescript
broadcast(message: string) {
  for (const ws of this.ctx.getWebSockets()) {
    ws.send(message);
  }
}
typescript
broadcast(message: string) {
  for (const ws of this.ctx.getWebSockets()) {
    ws.send(message);
  }
}

Per-Connection State

每个连接的状态

typescript
// Save state that survives hibernation (max 2048 bytes)
ws.serializeAttachment({ userId: "123", role: "admin" });

// Restore in message handler
const state = ws.deserializeAttachment();
typescript
// 保存可在休眠后保留的状态(最大2048字节)
ws.serializeAttachment({ userId: "123", role: "admin" });

// 在消息处理器中恢复状态
const state = ws.deserializeAttachment();

Auto-Response (No Wake)

自动响应(无需唤醒DO)

typescript
// Respond to pings without waking DO
this.ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair("ping", "pong"));
See websockets.md for details.

typescript
// 无需唤醒DO即可响应ping请求
this.ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair("ping", "pong"));
详细信息请查看websockets.md

RPC Methods

RPC方法

Call Durable Object methods directly (compatibility date >= 2024-04-03):
typescript
const stub = env.USER_SERVICE.get(id);
const user = await stub.getUser("123"); // Direct RPC call
await stub.updateUser("123", { name: "New Name" });
Note: Each RPC method call = one RPC session for billing.

直接调用Durable Object方法(兼容日期 >= 2024-04-03):
typescript
const stub = env.USER_SERVICE.get(id);
const user = await stub.getUser("123"); // 直接RPC调用
await stub.updateUser("123", { name: "New Name" });
注意:每个RPC方法调用对应一个计费的RPC会话。

Initialization

初始化

Use
blockConcurrencyWhile
in constructor for migrations:
typescript
constructor(ctx: DurableObjectState, env: Env) {
  super(ctx, env);
  ctx.blockConcurrencyWhile(async () => {
    this.ctx.storage.sql.exec(`CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT)`);
  });
}
Timeout: 30 seconds.

在构造函数中使用
blockConcurrencyWhile
进行迁移:
typescript
constructor(ctx: DurableObjectState, env: Env) {
  super(ctx, env);
  ctx.blockConcurrencyWhile(async () => {
    this.ctx.storage.sql.exec(`CREATE TABLE IF NOT EXISTS data (key TEXT PRIMARY KEY, value TEXT)`);
  });
}
超时时间:30秒。

Hibernation

休眠

Conditions for Hibernation

休眠条件

All must be true:
  • No pending
    setTimeout
    /
    setInterval
  • No in-progress
    await fetch()
  • Using Hibernation WebSocket API (not standard WebSocket)
  • No active request processing
必须同时满足以下所有条件:
  • 没有待处理的
    setTimeout
    /
    setInterval
  • 没有正在进行的
    await fetch()
  • 使用的是休眠WebSocket API(而非标准WebSocket)
  • 没有活跃的请求处理

Lifecycle

生命周期

  1. Active: Processing requests
  2. Idle hibernateable: ~10 seconds → may hibernate
  3. Hibernated: Removed from memory, WebSockets stay connected
  4. Wake: On message/event, constructor runs, handler invoked
Important: In-memory state is lost on hibernation. Restore from storage or attachments.

  1. 活跃状态:处理请求
  2. 可休眠空闲状态:约10秒后 → 可能进入休眠
  3. 休眠状态:从内存中移除,WebSocket保持连接
  4. 唤醒:收到消息/事件时,构造函数运行,处理器被调用
重要提示:休眠时内存中的状态会丢失,需从存储或附件中恢复。

Bindings & Migrations

绑定与迁移

jsonc
{
  "durable_objects": {
    "bindings": [{ "name": "MY_DO", "class_name": "MyDO" }]
  },
  "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDO"] }]
}
Note: Cannot enable SQLite on existing deployed classes.

jsonc
{
  "durable_objects": {
    "bindings": [{ "name": "MY_DO", "class_name": "MyDO" }]
  },
  "migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDO"] }]
}
注意:无法为已部署的现有类启用SQLite。

Wrangler Commands

Wrangler命令

bash
npx wrangler deploy   # Deploy with migrations
wrangler tail         # Tail logs

bash
npx wrangler deploy   # 带迁移的部署
wrangler tail         # 查看日志

Limits

限制

FeatureFreePaid
DO classes100500
Storage per DO10 GB10 GB
Storage per account5 GBUnlimited
CPU per request30 sec30 sec (max 5 min)
WebSocket connections32,76832,768
SQL row/value size2 MB2 MB
KV value size128 KiB128 KiB
Batch size128 keys128 keys

功能免费版付费版
DO类数量100500
每个DO的存储容量10 GB10 GB
每个账户的存储容量5 GB无限制
每个请求的CPU时间30秒30秒(最大5分钟)
WebSocket连接数32,76832,768
SQLite行/值大小2 MB2 MB
KV值大小128 KiB128 KiB
批量操作大小128个键128个键

Pricing

定价

MetricFreePaid
Requests100K/day1M/mo included, +$0.15/M
Duration13K GB-s/day400K GB-s/mo, +$12.50/M GB-s
SQLite rows read5M/day25B/mo included, +$0.001/M
SQLite rows written100K/day50M/mo included, +$1.00/M
Storage5 GB5 GB/mo included, +$0.20/GB-mo
WebSocket: 20:1 billing ratio (1M messages = 50K requests).
See pricing.md for details.

指标免费版付费版
请求数10万/天100万/月免费,超出后$0.15/百万
执行时长13000 GB-秒/天40万GB-秒/月免费,超出后$12.50/百万GB-秒
SQLite读取行数500万/天250亿/月免费,超出后$0.001/百万
SQLite写入行数10万/天5000万/月免费,超出后$1.00/百万
存储容量5 GB5 GB/月免费,超出后$0.20/GB-月
WebSocket计费:20:1的计费比例(100万条消息 = 5万次请求)。
详细计费信息请查看pricing.md

Prohibitions

禁止操作

  • ❌ Do not store state outside storage (lost on hibernation)
  • ❌ Do not use standard WebSocket API for hibernation
  • ❌ Do not exceed 2 MB per row/value in SQLite
  • ❌ Do not call
    sql.exec()
    with transaction control statements
  • ❌ Do not expect
    waitUntil
    to work (no effect in DO)

  • ❌ 不要在存储之外保存状态(休眠时会丢失)
  • ❌ 不要使用标准WebSocket API来实现休眠
  • ❌ SQLite中行/值大小不要超过2 MB
  • ❌ 不要在
    sql.exec()
    中使用事务控制语句
  • ❌ 不要依赖
    waitUntil
    (在DO中无效)

References

参考资料

  • api.md — Full API reference
  • storage.md — Storage API details
  • websockets.md — WebSocket hibernation
  • alarms.md — Alarm patterns
  • pricing.md — Billing details
  • api.md — 完整API参考
  • storage.md — 存储API详情
  • websockets.md — WebSocket休眠
  • alarms.md — 告警模式
  • pricing.md — 计费详情

Related Skills

相关技能

  • cloudflare-workers
    — Worker development
  • cloudflare-d1
    — D1 database
  • cloudflare-kv
    — Global KV
  • cloudflare-workflows
    — Durable execution
  • cloudflare-workers
    — Worker开发
  • cloudflare-d1
    — D1数据库
  • cloudflare-kv
    — 全局KV存储
  • cloudflare-workflows
    — 持久化执行