SuperAPI AI 工具集

convex-migration-helper

数据与分析

查看项目

更新于 10-16 16:03

技能介绍

用途

Convex Migration Helper 用于在对 Convex 进行破坏性变更时,安全地迁移 schema 与数据。它关注的不是如何写业务代码,而是如何把已经存在于生产或开发环境中的数据,平滑地迁移到新的结构上。

何时使用

  • 给已有表添加新的必填字段
  • 修改字段类型或结构
  • 拆分或合并表
  • 重命名字段或删除字段
  • 从嵌套数据迁移为关系型数据

何时不应使用

  • 全新 schema,生产或开发环境没有现存数据
  • 添加无需回填的可选字段
  • 添加没有存量数据需要迁移的新表
  • 增删索引且不涉及正确性问题
  • 只是咨询 Convex schema 设计,没有迁移需求

核心概念

Schema 校验驱动整个工作流

Convex 不允许部署与静态数据不匹配的 schema,这是塑造每一次迁移的根本约束:

  • 如果现存文档没有某个字段,就不能把它设为必填
  • 如果现存文档是旧类型,就不能修改该字段的类型
  • 如果现存文档仍有某字段,就不能把它从 schema 中移除

因此迁移遵循可预测的模式:加宽 schema、迁移数据、收窄 schema。

在线迁移

Convex 迁移是「在线」进行的,应用在数据被分批异步更新的同时仍然持续对外服务。在迁移窗口期内,代码必须同时处理新旧两种数据格式。

优先新增字段而非修改类型

改变数据形态时,应创建新字段,而不是修改已有字段,这样过渡更安全、更容易回滚。

不要删除数据

除非确有把握,优先弃用字段而不是删除。把字段标记为 v.optional,并加代码注释说明它已弃用以及当初存在的原因。

安全变更(无需迁移)

添加可选字段:

// Before users: defineTable({ name: v.string(), });

// After - safe, new field is optional users: defineTable({ name: v.string(), bio: v.optional(v.string()), });

添加新表:

posts: defineTable({ userId: v.id("users"), title: v.string(), }).index("by_user", ["userId"]);

添加索引:

users: defineTable({ name: v.string(), email: v.string(), }).index("by_email", ["email"]);

破坏性变更:部署工作流

每一次破坏性迁移都遵循相同的多阶段部署模式:

Deploy 1 - 加宽 schema:

  • 更新 schema,允许新旧两种格式(例如添加可选的新字段)
  • 更新代码,读取时兼容两种格式
  • 更新代码,新文档按新格式写入
  • 部署

两次部署之间 - 迁移数据:

  • 运行迁移回填现有文档
  • 验证所有文档都已迁移

Deploy 2 - 收窄 schema:

  • 更新 schema,只要求新格式
  • 移除处理旧格式的代码
  • 部署

使用 migrations 组件

对于任何非平凡的迁移,使用 @convex-dev/migrations 组件。它负责批处理、基于游标的分页、状态跟踪、失败续跑、dry run 以及进度监控。

关于安装、配置、用 npx convex run migrations:myMigration 定义并直接运行迁移、dry run、状态监控和配置项,参见 references/migrations-component.md。

常见迁移模式

覆盖以下带代码示例的完整模式,参见 references/migration-patterns.md:

  • 添加必填字段
  • 删除字段
  • 修改字段类型
  • 把嵌套数据拆分到独立表
  • 清理孤立文档
  • 零停机策略(双写、双读)
  • 小表捷径(不使用组件、单个 internalMutation)
  • 验证迁移已完成

常见陷阱

  • 在迁移数据前就把字段设为必填:Convex 会因为现存文档缺少该字段而拒绝部署。务必先加宽 schema。
  • 在大表上使用 .collect():会触及事务限制或造成超时。应使用 migrations 组件做正确的分批分页。.collect() 只对确定很小的表安全。
  • 迁移前未写入新格式:迁移窗口期内创建的文档会被漏掉,导致迁移「完成」后仍有未迁移数据。
  • 跳过 dry run:用 dryRun: true 在把改动提交到生产数据前验证迁移逻辑,能在影响真实文档之前发现 bug。
  • 过早删除字段:优先用 v.optional 加注释的方式弃用。只有在确信数据不再需要且没有任何代码引用后才删除。
  • 用 cron 做迁移批次:migrations 组件内部通过递归调度处理批处理。cron 需要手动清理,还要额外部署一次才能移除。

迁移清单

  • 识别破坏性变更并规划多阶段部署工作流
  • 更新 schema,允许新旧两种格式
  • 更新代码,读取时兼容两种格式
  • 更新代码,新文档按新格式写入
  • 部署加宽后的 schema 与更新后的代码
  • 使用 @convex-dev/migrations 组件定义迁移
  • 用 npx convex run migrations:myMigration '{"dryRun": true}' 测试
  • 用 npx convex run migrations:myMigration 直接运行迁移并监控状态
  • 验证所有文档都已迁移
  • 更新 schema,只要求新格式
  • 清理处理旧格式的代码
  • 部署最终的 schema 与代码
  • 确认稳定后移除迁移代码