cloudflare-durable-objects
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCloudflare 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 deploybash
npx wrangler deployCore Concepts
核心概念
DurableObjectState
DurableObjectState
Available as in Durable Object class:
this.ctxtypescript
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.ctxtypescript
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 instanceSee 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; // numbertypescript
cursor.columnNames; // string[]
cursor.rowsRead; // number
cursor.rowsWritten; // numberCreate 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 are coalesced atomically:
awaittypescript
// 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多个不带的写入操作会被自动合并为原子操作:
awaittypescript
// 这些操作会被批处理为单个事务
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 alarmRetry 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 in constructor for migrations:
blockConcurrencyWhiletypescript
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.
在构造函数中使用进行迁移:
blockConcurrencyWhiletypescript
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 /
setTimeoutsetInterval - No in-progress
await fetch() - Using Hibernation WebSocket API (not standard WebSocket)
- No active request processing
必须同时满足以下所有条件:
- 没有待处理的/
setTimeoutsetInterval - 没有正在进行的
await fetch() - 使用的是休眠WebSocket API(而非标准WebSocket)
- 没有活跃的请求处理
Lifecycle
生命周期
- Active: Processing requests
- Idle hibernateable: ~10 seconds → may hibernate
- Hibernated: Removed from memory, WebSockets stay connected
- Wake: On message/event, constructor runs, handler invoked
Important: In-memory state is lost on hibernation. Restore from storage or attachments.
- 活跃状态:处理请求
- 可休眠空闲状态:约10秒后 → 可能进入休眠
- 休眠状态:从内存中移除,WebSocket保持连接
- 唤醒:收到消息/事件时,构造函数运行,处理器被调用
重要提示:休眠时内存中的状态会丢失,需从存储或附件中恢复。
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 logsbash
npx wrangler deploy # 带迁移的部署
wrangler tail # 查看日志Limits
限制
| Feature | Free | Paid |
|---|---|---|
| DO classes | 100 | 500 |
| Storage per DO | 10 GB | 10 GB |
| Storage per account | 5 GB | Unlimited |
| CPU per request | 30 sec | 30 sec (max 5 min) |
| WebSocket connections | 32,768 | 32,768 |
| SQL row/value size | 2 MB | 2 MB |
| KV value size | 128 KiB | 128 KiB |
| Batch size | 128 keys | 128 keys |
| 功能 | 免费版 | 付费版 |
|---|---|---|
| DO类数量 | 100 | 500 |
| 每个DO的存储容量 | 10 GB | 10 GB |
| 每个账户的存储容量 | 5 GB | 无限制 |
| 每个请求的CPU时间 | 30秒 | 30秒(最大5分钟) |
| WebSocket连接数 | 32,768 | 32,768 |
| SQLite行/值大小 | 2 MB | 2 MB |
| KV值大小 | 128 KiB | 128 KiB |
| 批量操作大小 | 128个键 | 128个键 |
Pricing
定价
| Metric | Free | Paid |
|---|---|---|
| Requests | 100K/day | 1M/mo included, +$0.15/M |
| Duration | 13K GB-s/day | 400K GB-s/mo, +$12.50/M GB-s |
| SQLite rows read | 5M/day | 25B/mo included, +$0.001/M |
| SQLite rows written | 100K/day | 50M/mo included, +$1.00/M |
| Storage | 5 GB | 5 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 GB | 5 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 with transaction control statements
sql.exec() - ❌ Do not expect to work (no effect in DO)
waitUntil
- ❌ 不要在存储之外保存状态(休眠时会丢失)
- ❌ 不要使用标准WebSocket API来实现休眠
- ❌ SQLite中行/值大小不要超过2 MB
- ❌ 不要在中使用事务控制语句
sql.exec() - ❌ 不要依赖(在DO中无效)
waitUntil
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
相关技能
- — Worker development
cloudflare-workers - — D1 database
cloudflare-d1 - — Global KV
cloudflare-kv - — Durable execution
cloudflare-workflows
- — Worker开发
cloudflare-workers - — D1数据库
cloudflare-d1 - — 全局KV存储
cloudflare-kv - — 持久化执行
cloudflare-workflows