Sequel中rake db:schema:dump/load等效实现及加载失败求助
我之前也踩过类似的Sequel迁移坑,结合官方设计逻辑和你的场景,给你几个实用的解决方案:
先理清楚问题根源
你遇到的Sequel::Migrator.run加载单个schema.rb失败,本质是Migrator的设计目标是执行增量迁移文件——它期望传入的是包含多个按版本命名的迁移文件的目录,而非全量的schema快照文件。另外官方的sequel -d和sequel -m组合确实是推荐的schema管理方式,但sequel -d生成的schema.rb是全量结构,用sequel -m加载时会把它当作一个独立的迁移版本处理,你看到的“清除现有迁移信息”其实是指生成的schema.rb里不包含之前的迁移版本记录,这是正常的,因为它是快照而非增量迁移。
方案一:用官方命令行完成schema加载
既然你已经用DB.dump_schema_migration生成了正确的schema.rb,直接用sequel命令行加载是最贴合官方设计的方式,不需要调用Migrator:
namespace :schema do task :dump => :migrations_environment do schema = without_sequel_logging{ DB.dump_schema_migration } File.open("db/schema.rb", 'w') {|f| f.write(schema) } end task :load => :migrations_environment do # 直接调用sequel命令加载schema文件,等同于执行sequel -m db/schema.rb 数据库URL system("sequel -m db/schema.rb #{DB.url}") end end
执行这个schema:load任务后,Sequel会把schema.rb作为一个新的迁移版本插入到schema_migrations表,后续的增量迁移可以正常基于这个快照继续执行,不会出现冲突。
方案二:自定义加载逻辑,绕过Migrator
如果不想依赖命令行,可以直接执行schema.rb里的代码——因为dump_schema_migration生成的内容本身就是可执行的Sequel DSL,用来创建所有表结构:
namespace :schema do task :dump => :migrations_environment do schema = without_sequel_logging{ DB.dump_schema_migration } File.open("db/schema.rb", 'w') {|f| f.write(schema) } end task :load => :migrations_environment do # 直接加载并执行schema.rb的代码,快速创建所有表 load("db/schema.rb") # 手动标记迁移版本,避免后续Migrator重复执行历史迁移 # 用时间戳作为快照版本,确保唯一性 snapshot_version = "schema_snapshot_#{Time.now.strftime('%Y%m%d%H%M%S')}" DB[:schema_migrations].insert(:version => snapshot_version) end end
这个方式更灵活,适合需要在加载前后加自定义逻辑的场景。手动插入版本号是为了让Sequel的Migrator知道当前数据库已经处于快照状态,后续只会执行比这个版本新的迁移文件。
方案三:修复测试环境的迁移冲突(根源解决)
你提到的测试环境问题:Sequel认为表不存在但实际存在,本质是schema_migrations表的记录和实际数据库结构不一致。可以通过补全迁移版本记录来解决:
namespace :db do task :fix_migration_versions => :migrations_environment do # 获取所有本地迁移文件的版本号 local_versions = Sequel::Migrator.migration_files("db/migrate").map do |file| File.basename(file).split('_').first end # 获取数据库中已记录的版本号 db_versions = DB[:schema_migrations].select_map(:version) # 把本地有但数据库缺失的版本号补全到schema_migrations (local_versions - db_versions).each do |version| DB[:schema_migrations].insert(:version => version) end puts "已补全#{(local_versions - db_versions).count}个缺失的迁移版本记录" end end
执行rake db:fix_migration_versions后,再运行rake db:migrate,Sequel就会正确识别哪些迁移已经执行过,不会再尝试创建已存在的表。
总结
- 如果是快速搭建全新数据库:方案一或方案二都可以,推荐方案一(贴合官方流程)
- 如果是修复测试环境的迁移冲突:先执行方案三补全版本,再执行增量迁移
- 避免全量执行历史迁移的最佳实践:定期生成schema快照,用快照作为数据库初始化的基础,后续只执行快照之后的增量迁移
内容的提问来源于stack exchange,提问作者bbozo

