mcpField
R
rails-migrations

rails-migrations

github.com/marckohlbrugge数据与数据库73053 次安装20 周前更新
代码仓库

关于 rails-migrations

rails-migrations 是用于规划和审查 Rails 安全架构变更与数据迁移的技能,涵盖可逆变更、分阶段发布,以及将高风险回填移出部署迁移窗口。

主要功能

  1. 01可逆迁移指引
  2. 02安全的架构与索引变更
  3. 03脚本批量回填
  4. 04多租户索引策略
  5. 05分阶段发布方案

适用场景

  1. 01审查迁移的锁表风险
  2. 02规划大型数据回填
  3. 03分多个部署替换列

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/marckohlbrugge/37signals-skills --skill rails-migrations

安装后在 Claude Code 中输入/rails-migrations确认已加载。

详细介绍

Rails 迁移

用于架构变更和迁移安全。

规则

  • 尽可能确保迁移可逆(数据迁移使用带有显式 up/down SQL 的 reversible 块)。
  • 在迁移中使用原始 SQL 操作数据;避免引用可能随时间发生变化的应用模型。
  • 避免在大表上长时间持有表锁;将有风险的操作拆分为多个可安全部署的步骤。
  • 将架构变更与大量数据回填分开。
  • 优先使用重复运行仍然安全的数据库操作(在迁移可能面对不同状态运行时,使用 table_exists? / column_exists? 守卫)。
  • 小规模的内联回填可以放在同一个迁移中(添加列 → execute "UPDATE ..." → 收紧约束);任何耗时较长的操作都应完全移出迁移流程。

脚本回填(并非所有操作都通过 db:migrate 执行)

耗时较长或有风险的数据回填放在 script/migrations/*.rb 中,手动运行(例如通过 Kamal),而不是在部署迁移窗口内运行:

  • 在文件头注释中说明前置条件和运行说明。
  • 变更数据前先运行预检查询,打印操作范围。
  • 保持幂等:跳过已处理的行(next if Entry.exists?(...)),确保重新运行安全。
  • 分批处理(find_each / in_batches),绝不一次性写入全部数据。

安全模式

  • 添加可空列 → 回填 → 强制 NOT NULL。
  • 在支持或需要时并发添加索引。
  • 在添加唯一索引之前,立即在同一个迁移中对数据去重(使用原始 SQL 删除较旧的重复记录)。
  • 仅变更数据的迁移也是合理的:向上迁移使用 find_each + update!,向下迁移使用 update_all。
  • 使用分批回填,而不是一次性写入全部数据。

约束:应审慎选择,而不是默认启用

  • 优先使用数据库级唯一索引,而不是 AR validates uniqueness(验证存在竞态条件;索引不存在此问题)。
  • 外键需要权衡:Fizzy 有意移除了所有外键(在引用上使用 foreign_key: false),以加快 DDL 并提高分片友好性,同时由 Rails 保证完整性。两种做法都可以,但应保持一致并记录说明,而不是无意为之。
  • 对于不便使用 CHECK 约束的特殊不变量,可通过成本低的架构技巧来强制执行:例如,在单例表中使用默认值为 0 的 singleton_guard 列,并为其创建唯一索引。
  • 原子化的租户级计数器:使用 account.increment!(:cards_count) 生成序列号,并创建唯一 [account_id, number] 索引;全局 ID 序列则使用加锁的序列行(在锁内执行 first_or_create!、increment!)。

多租户索引策略

  • 为应用引入租户隔离时,在专门的迁移阶段将全局索引替换为 [account_id, ...] 复合索引;删除现已多余的单列索引,并说明原因。
  • 范围唯一性应在数据库级别实现:add_index :tags, [:account_id, :title], unique: true。

多数据库 / 多适配器应用

  • Solid Queue/Cache/Cable 各自使用独立数据库,并配置单独的 migrations_paths 和架构文件。
  • 在同一代码库中支持 SQLite + MySQL:在迁移中使用提前返回的适配器守卫(return if connection.adapter_name == "SQLite")、适配器专用 DDL(FTS5 与分片全文搜索),并通过各配置的 schema_dump 同时导出架构(schema.rb / schema_sqlite.rb)。
  • 在生产环境使用 SQLite 时,设置 default_transaction_mode: immediate,以减少并发写入时出现的 SQLITE_BUSY。
  • 在生产环境中禁用 dump_schema_after_migration。

分阶段发布操作指南

  • 替换列

    • 部署 1:添加新的可空列。
    • 部署 2:双写 / 回填。
    • 部署 3:从新列读取。
    • 部署 4:强制执行约束,之后再删除旧列。
  • 强化约束

    • 先清理数据 / 回填。
    • 数据符合要求后,再添加索引 / 约束。
    • 约束生效后,再调整应用行为以依赖该约束。
  • 破坏性变更

    • 首先在应用代码中弃用相关读写操作。
    • 在单独的一次部署中移除相关用法。
    • 确认观察期结束后,再删除列 / 表。

危险信号

  • 没有明确理由就执行不可逆迁移。
  • 将模式重写和大量数据迁移合并到同一步。
  • 在事务密集型默认迁移中执行大量回填(移至 script/migrations/)。
  • 未分阶段弃用就删除列或表。
  • 在迁移中引用应用模型(模型行为会发生变化;请使用 SQL 或内联最精简的 AR 类)。
  • 未先清理重复数据就添加唯一索引。
  • 使用 validates uniqueness,但没有对应的唯一索引。

评论

支持 Markdown