Skip to content

Storage Adapters

one-ea edited this page Apr 25, 2026 · 2 revisions

💾 存储适配器

Monolith 通过双适配器抽象,让数据库和对象存储都可以零代码改动整套迁移


设计原理

接口契约

server/src/storage/interfaces.ts 定义两个核心接口:

export interface IDatabase {
  // Posts
  getPostBySlug(slug: string): Promise<Post | null>;
  listPosts(opts?: ListOptions): Promise<Post[]>;
  createPost(input: NewPost): Promise<Post>;
  updatePost(id: number, patch: Partial<Post>): Promise<Post>;
  deletePost(id: number): Promise<void>;
  // ... 还有 categories / series / comments / reactions / versions / media
}

export interface IObjectStorage {
  put(key: string, body: ArrayBuffer | ReadableStream, opts?: PutOptions): Promise<void>;
  get(key: string): Promise<ReadableStream | null>;
  delete(key: string): Promise<void>;
  list(prefix?: string): Promise<string[]>;
}

Factory 路由

server/src/storage/factory.ts 根据环境变量返回具体实现:

export async function createDatabase(env: Env): Promise<IDatabase> {
  switch (env.DB_PROVIDER ?? 'd1') {
    case 'turso':    return new TursoDatabase(env);
    case 'postgres': return new PostgresDatabase(env);
    default:         return new D1Database(env.DB);
  }
}

export function createObjectStorage(env: Env): IObjectStorage {
  switch (env.STORAGE_PROVIDER ?? 'r2') {
    case 's3': return new S3Storage(env);
    default:   return new R2Storage(env.MEDIA);
  }
}

业务路由只依赖接口:

const db = await createDatabase(c.env);
const post = await db.getPostBySlug(slug);

数据库适配器对比

维度 D1 (默认) Turso (LibSQL) PostgreSQL
运行环境 Cloudflare 内置 任何边缘 自建/RDS/Supabase
协议 Workers Binding HTTP / libsql postgres 协议
驱动 drizzle-orm/d1 @libsql/client pg (Hyperdrive 推荐)
schema 文件 schema.ts schema.ts schema-pg.ts
免费额度 5GB · 5M 读/天 9GB · 1B 行读/月 取决于供应商
延迟 <5ms (同区域) <30ms (边缘节点) 取决于网络
强一致性 单 region 主备异步 取决于配置
适合场景 Cloudflare 全家桶 多区域低延迟 已有 PG 基础设施

切换示例

详见 部署指南 - 切换数据库后端


对象存储适配器对比

维度 R2 (默认) S3 兼容
服务 Cloudflare R2 AWS S3 / B2 / OSS / COS / MinIO
协议 Workers Binding aws-sdk-v4 签名
出流量费用 0 元 按 GB 计费
CDN 集成 同 Cloudflare 网络免费 需额外配置
签名 URL 支持 支持
适合场景 公开博客图片 私有文件 / 多云策略

切换到 S3

cd server
npx wrangler secret put STORAGE_PROVIDER  # s3
npx wrangler secret put S3_ENDPOINT       # https://s3.amazonaws.com
npx wrangler secret put S3_REGION         # us-east-1
npx wrangler secret put S3_BUCKET         # monolith-uploads
npx wrangler secret put S3_ACCESS_KEY_ID
npx wrangler secret put S3_SECRET_ACCESS_KEY

推荐组合

组合 适合场景 月成本预估
D1 + R2 个人博客 / 中小流量 ~$0 (免费额度)
Turso + R2 多区域低延迟 + 高读 ~$5
PostgreSQL + S3 已有团队基础设施 取决于自建
D1 + S3 私有云对象存储 ~$0 + S3 存储

添加新适配器

步骤

  1. 创建实现文件

    server/src/storage/db/mongo.ts          # 数据库
    server/src/storage/object/azure-blob.ts # 对象存储
    
  2. 实现接口

    export class MongoDatabase implements IDatabase {
      constructor(env: Env) { /* 初始化连接 */ }
      async getPostBySlug(slug: string) { /* ... */ }
      // 必须实现所有 IDatabase 方法
    }
  3. 注册到 factory

    case 'mongo': return new MongoDatabase(env);
  4. 更新 schema (如果是数据库)

    • SQL 数据库: 复用 schema.tsschema-pg.ts
    • NoSQL: 自定义类型映射
  5. 加迁移命令

    "db:push:mongo": "node scripts/mongo-push.mjs"

Drizzle ORM 三端同步

⚠️ 铁律: schema 修改必须同步到所有数据库适配器,否则 Turso/PG 会崩。

工作流

# 1. 修改 schema.ts (D1/Turso) 或 schema-pg.ts (PG)
vim server/src/db/schema.ts

# 2. 生成迁移 SQL
cd server
npm run db:generate

# 3. 人工审核 server/migrations/000X_*.sql
# - 是否有 DEFAULT 值(避免存量记录崩溃)
# - 外键级联模式是否明确

# 4. 同步 schema-pg.ts(手动 mirror 修改)

# 5. 同步三个适配器实现
# - server/src/storage/db/d1.ts
# - server/src/storage/db/turso.ts
# - server/src/storage/db/postgres.ts

# 6. 应用迁移
npx wrangler d1 migrations apply monolith-db --local
npx wrangler d1 migrations apply monolith-db --remote

drizzle-sync-check skill 在编辑 schema 时会自动触发提醒。


故障排查

Q: Turso 报 SQLITE_BUSY

并发写入冲突。检查是否有未关闭的事务,或加重试逻辑。

Q: PostgreSQL 报 too many connections

边缘环境每次冷启动都会建连。改用 Hyperdrive 或 PgBouncer 做连接池。

Q: R2 上传后 /cdn/* 404

确认 Pages Functions 中 client/functions/cdn/[[path]].ts 存在并指向正确的 Workers 域。

Q: 迁移失败:column already exists

D1 不支持 IF NOT EXISTS,手动编辑迁移 SQL 跳过已存在列,或直接回滚后重做。


延伸阅读

Clone this wiki locally