Loading...
Loading...
Compare original and translation side by side
// 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;
}
}// 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;
}
}// 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}`);
},
};// 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}`);
},
};{
"name": "counter-worker",
"main": "src/index.ts",
"durable_objects": {
"bindings": [
{
"name": "COUNTER",
"class_name": "Counter"
}
]
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["Counter"]
}
]
}{
"name": "counter-worker",
"main": "src/index.ts",
"durable_objects": {
"bindings": [
{
"name": "COUNTER",
"class_name": "Counter"
}
]
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["Counter"]
}
]
}npx wrangler deploynpx wrangler deploythis.ctxinterface 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
}this.ctxinterface 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
}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 instanceconst id = env.MY_DO.idFromName("user-123"); // 确定性ID
const id = env.MY_DO.newUniqueId(); // 随机唯一ID
const stub = env.MY_DO.get(id); // 获取DO实例的存根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);
}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);
}cursor.columnNames; // string[]
cursor.rowsRead; // number
cursor.rowsWritten; // numbercursor.columnNames; // string[]
cursor.rowsRead; // number
cursor.rowsWritten; // numberthis.ctx.storage.sql.exec(`
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
created_at INTEGER DEFAULT (unixepoch())
)
`);this.ctx.storage.sql.exec(`
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
created_at INTEGER DEFAULT (unixepoch())
)
`);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);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);// 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");
});// 同步事务(仅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");
});const sizeBytes = this.ctx.storage.sql.databaseSize;const sizeBytes = this.ctx.storage.sql.databaseSize;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);
}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);
}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();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();await// 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 togetherawait// 这些操作会被批处理为单个事务
this.ctx.storage.put("a", 1);
this.ctx.storage.put("b", 2);
this.ctx.storage.put("c", 3);
// 所有操作会一起提交// 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"));// 从现在起1小时后调度
await this.ctx.storage.setAlarm(Date.now() + 60 * 60 * 1000);
// 在特定时间调度
await this.ctx.storage.setAlarm(new Date("2024-12-31T00:00:00Z"));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);
}
}
}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);
}
}
}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 alarmawait this.ctx.storage.setAlarm(timestamp); // 设置/覆盖告警
const time = await this.ctx.storage.getAlarm(); // 获取调度时间(毫秒)或null
await this.ctx.storage.deleteAlarm(); // 取消告警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
}
}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(message: string) {
for (const ws of this.ctx.getWebSockets()) {
ws.send(message);
}
}broadcast(message: string) {
for (const ws of this.ctx.getWebSockets()) {
ws.send(message);
}
}// Save state that survives hibernation (max 2048 bytes)
ws.serializeAttachment({ userId: "123", role: "admin" });
// Restore in message handler
const state = ws.deserializeAttachment();// 保存可在休眠后保留的状态(最大2048字节)
ws.serializeAttachment({ userId: "123", role: "admin" });
// 在消息处理器中恢复状态
const state = ws.deserializeAttachment();// Respond to pings without waking DO
this.ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair("ping", "pong"));// 无需唤醒DO即可响应ping请求
this.ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair("ping", "pong"));const stub = env.USER_SERVICE.get(id);
const user = await stub.getUser("123"); // Direct RPC call
await stub.updateUser("123", { name: "New Name" });const stub = env.USER_SERVICE.get(id);
const user = await stub.getUser("123"); // 直接RPC调用
await stub.updateUser("123", { name: "New Name" });blockConcurrencyWhileconstructor(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)`);
});
}blockConcurrencyWhileconstructor(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)`);
});
}setTimeoutsetIntervalawait fetch()setTimeoutsetIntervalawait fetch(){
"durable_objects": {
"bindings": [{ "name": "MY_DO", "class_name": "MyDO" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDO"] }]
}{
"durable_objects": {
"bindings": [{ "name": "MY_DO", "class_name": "MyDO" }]
},
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyDO"] }]
}npx wrangler deploy # Deploy with migrations
wrangler tail # Tail logsnpx wrangler deploy # 带迁移的部署
wrangler tail # 查看日志| 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个键 |
| 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 |
| 指标 | 免费版 | 付费版 |
|---|---|---|
| 请求数 | 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-月 |
sql.exec()waitUntilsql.exec()waitUntilcloudflare-workerscloudflare-d1cloudflare-kvcloudflare-workflowscloudflare-workerscloudflare-d1cloudflare-kvcloudflare-workflows