durable-objects
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDurable Objects
Durable Objects
Build stateful, coordinated applications on Cloudflare's edge using Durable Objects.
使用Durable Objects在Cloudflare边缘构建有状态的协调型应用。
When to Use
适用场景
- Creating new Durable Object classes for stateful coordination
- Implementing RPC methods, alarms, or WebSocket handlers
- Reviewing existing DO code for best practices
- Configuring wrangler.jsonc/toml for DO bindings and migrations
- Writing tests with
@cloudflare/vitest-pool-workers - Designing sharding strategies and parent-child relationships
- 为有状态协调系统创建新的Durable Object类
- 实现RPC方法、告警或WebSocket处理器
- 审核现有DO代码以遵循最佳实践
- 配置wrangler.jsonc/toml以实现DO绑定与迁移
- 使用编写测试
@cloudflare/vitest-pool-workers - 设计分片策略与父子对象关系
Reference Documentation
参考文档
- - Core rules, storage, concurrency, RPC, alarms
./references/rules.md - - Vitest setup, unit/integration tests, alarm testing
./references/testing.md - - Workers handlers, types, wrangler config, observability
./references/workers.md
Search: , , , ,
blockConcurrencyWhileidFromNamegetByNamesetAlarmsql.exec- - 核心规则、存储、并发、RPC、告警
./references/rules.md - - Vitest配置、单元/集成测试、告警测试
./references/testing.md - - Workers处理器、类型定义、wrangler配置、可观测性
./references/workers.md
搜索关键词:, , , ,
blockConcurrencyWhileidFromNamegetByNamesetAlarmsql.execCore Principles
核心原则
Use Durable Objects For
Durable Objects的适用场景
| Need | Example |
|---|---|
| Coordination | Chat rooms, multiplayer games, collaborative docs |
| Strong consistency | Inventory, booking systems, turn-based games |
| Per-entity storage | Multi-tenant SaaS, per-user data |
| Persistent connections | WebSockets, real-time notifications |
| Scheduled work per entity | Subscription renewals, game timeouts |
| 适用场景 | 示例 |
|---|---|
| 协调系统 | 聊天室、多人游戏、协作文档 |
| 强一致性需求 | 库存管理、预订系统、回合制游戏 |
| 单实体存储 | 多租户SaaS、用户专属数据 |
| 持久化连接 | WebSocket、实时通知 |
| 单实体定时任务 | 订阅续费、游戏超时处理 |
Do NOT Use For
Durable Objects的不适用场景
- Stateless request handling (use plain Workers)
- Maximum global distribution needs
- High fan-out independent requests
- 无状态请求处理(使用普通Workers即可)
- 需要全局最大程度分发的场景
- 高扇出的独立请求
Quick Reference
快速参考
Wrangler Configuration
Wrangler配置
jsonc
// wrangler.jsonc
{
"durable_objects": {
"bindings": [{ "name": "MY_DO", "class_name": "MyDurableObject" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDurableObject"] }]
}jsonc
// wrangler.jsonc
{
"durable_objects": {
"bindings": [{ "name": "MY_DO", "class_name": "MyDurableObject" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDurableObject"] }]
}Basic Durable Object Pattern
基础Durable Object模式
typescript
import { DurableObject } from "cloudflare:workers";
export interface Env {
MY_DO: DurableObjectNamespace<MyDurableObject>;
}
export class MyDurableObject extends DurableObject<Env> {
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
ctx.blockConcurrencyWhile(async () => {
this.ctx.storage.sql.exec(`
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data TEXT NOT NULL
)
`);
});
}
async addItem(data: string): Promise<number> {
const result = this.ctx.storage.sql.exec<{ id: number }>(
"INSERT INTO items (data) VALUES (?) RETURNING id",
data
);
return result.one().id;
}
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const stub = env.MY_DO.getByName("my-instance");
const id = await stub.addItem("hello");
return Response.json({ id });
},
};typescript
import { DurableObject } from "cloudflare:workers";
export interface Env {
MY_DO: DurableObjectNamespace<MyDurableObject>;
}
export class MyDurableObject extends DurableObject<Env> {
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
ctx.blockConcurrencyWhile(async () => {
this.ctx.storage.sql.exec(`
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data TEXT NOT NULL
)
`);
});
}
async addItem(data: string): Promise<number> {
const result = this.ctx.storage.sql.exec<{ id: number }>(
"INSERT INTO items (data) VALUES (?) RETURNING id",
data
);
return result.one().id;
}
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const stub = env.MY_DO.getByName("my-instance");
const id = await stub.addItem("hello");
return Response.json({ id });
},
};Critical Rules
核心规则
- Model around coordination atoms - One DO per chat room/game/user, not one global DO
- Use for deterministic routing - Same input = same DO instance
getByName() - Use SQLite storage - Configure in migrations
new_sqlite_classes - Initialize in constructor - Use for schema setup only
blockConcurrencyWhile() - Use RPC methods - Not fetch() handler (compatibility date >= 2024-04-03)
- Persist first, cache second - Always write to storage before updating in-memory state
- One alarm per DO - replaces any existing alarm
setAlarm()
- 围绕协调原子建模 - 每个聊天室/游戏/用户对应一个DO实例,而非使用全局单一DO
- 使用实现确定性路由 - 相同输入对应同一个DO实例
getByName() - 使用SQLite存储 - 在迁移中配置
new_sqlite_classes - 在构造函数中完成初始化 - 仅在Schema设置时使用
blockConcurrencyWhile() - 使用RPC方法 - 而非fetch()处理器(兼容性日期需≥2024-04-03)
- 先持久化,后缓存 - 始终先写入存储,再更新内存状态
- 每个DO最多一个告警 - 会替换已存在的告警
setAlarm()
Anti-Patterns (NEVER)
反模式(严禁使用)
- Single global DO handling all requests (bottleneck)
- Using on every request (kills throughput)
blockConcurrencyWhile() - Storing critical state only in memory (lost on eviction/crash)
- Using between related storage writes (breaks atomicity)
await - Holding across
blockConcurrencyWhile()or external I/Ofetch()
- 使用单一全局DO处理所有请求(会造成性能瓶颈)
- 在每个请求中都使用(严重降低吞吐量)
blockConcurrencyWhile() - 仅在内存中存储关键状态(实例被回收/崩溃时会丢失)
- 相关存储写入操作之间使用(破坏原子性)
await - 在或外部I/O操作期间持有
fetch()锁blockConcurrencyWhile()
Stub Creation
Stub创建
typescript
// Deterministic - preferred for most cases
const stub = env.MY_DO.getByName("room-123");
// From existing ID string
const id = env.MY_DO.idFromString(storedIdString);
const stub = env.MY_DO.get(id);
// New unique ID - store mapping externally
const id = env.MY_DO.newUniqueId();
const stub = env.MY_DO.get(id);typescript
// 确定性路由 - 大多数场景的首选方式
const stub = env.MY_DO.getByName("room-123");
// 从现有ID字符串创建
const id = env.MY_DO.idFromString(storedIdString);
const stub = env.MY_DO.get(id);
// 创建新的唯一ID - 需在外部存储映射关系
const id = env.MY_DO.newUniqueId();
const stub = env.MY_DO.get(id);Storage Operations
存储操作
typescript
// SQL (synchronous, recommended)
this.ctx.storage.sql.exec("INSERT INTO t (c) VALUES (?)", value);
const rows = this.ctx.storage.sql.exec<Row>("SELECT * FROM t").toArray();
// KV (async)
await this.ctx.storage.put("key", value);
const val = await this.ctx.storage.get<Type>("key");typescript
// SQL(同步,推荐使用)
this.ctx.storage.sql.exec("INSERT INTO t (c) VALUES (?)", value);
const rows = this.ctx.storage.sql.exec<Row>("SELECT * FROM t").toArray();
// KV(异步)
await this.ctx.storage.put("key", value);
const val = await this.ctx.storage.get<Type>("key");Alarms
告警
typescript
// Schedule (replaces existing)
await this.ctx.storage.setAlarm(Date.now() + 60_000);
// Handler
async alarm(): Promise<void> {
// Process scheduled work
// Optionally reschedule: await this.ctx.storage.setAlarm(...)
}
// Cancel
await this.ctx.storage.deleteAlarm();typescript
// 调度告警(会替换已存在的告警)
await this.ctx.storage.setAlarm(Date.now() + 60_000);
// 告警处理器
async alarm(): Promise<void> {
// 处理定时任务
// 可选:重新调度告警: await this.ctx.storage.setAlarm(...)
}
// 取消告警
await this.ctx.storage.deleteAlarm();Testing Quick Start
测试快速开始
typescript
import { env } from "cloudflare:test";
import { describe, it, expect } from "vitest";
describe("MyDO", () => {
it("should work", async () => {
const stub = env.MY_DO.getByName("test");
const result = await stub.addItem("test");
expect(result).toBe(1);
});
});typescript
import { env } from "cloudflare:test";
import { describe, it, expect } from "vitest";
describe("MyDO", () => {
it("should work", async () => {
const stub = env.MY_DO.getByName("test");
const result = await stub.addItem("test");
expect(result).toBe(1);
});
});