如何在ActiveRecord中优雅地处理“Mysql2 ::错误:无效日期”?

时间:2013-05-05 21:28:30

标签: ruby-on-rails activerecord error-handling database-migration legacy-database

我正在遗留数据库上构建一个Rails 3.2应用程序,该数据库在不同的表中也有一些损坏的记录。最令人头痛的问题之一是它包含无效日期。

我已经设置了一个沙盒,我手动修复了一次以使我的代码正常工作。现在是部署的时候了。因此,沙箱每晚重置并从实时数据库中复制,重建ferret索引,并重新应用迁移。在部署到实时设置之前,我们将经常部署到沙箱以获取最后的修复程序。

由于传统的PHP应用程序和这个新的Rails应用程序需要并行运行几周到几个月,我们不能简单地一次性修复日期(更新:只是为了澄清,意味着它们同时在同一个数据库上运行)。我需要一种自动化方法,可能是迁移或rake任务(我会选择后者)。

但问题是:ActiveRecord在加载这些记录时会窒息,因此我无法调查记录并通过ruby代码中的一些硬编码假设来修复日期。

第二个问题是遗留数据库存在不一致,因为PHP代码没有使用事务,一些代码路径被破坏,孤立和破坏表约束落后。我将在它们发生时处理它,其中大部分已经在模型中处理。第一个问题与日期有关。

你通常如何解决这个问题?也许甚至还有一些神奇的宝石,它通过拦截异常和运行一些试图修复的代码来支持迁移具有破碎记录的旧数据库......

迁移路径使用MySQL和三个生产环境(使用实时数据库稳定,使用相同数据库暂存,以及每天晚上重置数据库克隆的沙箱)。我们决定不进行一次性数据映射/迁移,因为我们不能一步取代完整的遗留应用程序(它包含一个包含大约50000篇文章的CMS,数百个主题,包含图像和下载的巨大文件数据库,支持大约10个网站,大约12年的数据和工作,来自不同编程技能的凌乱的PHP代码,来自不同迁移阶段的重复代码,从合作伙伴网站提取RSS内容以将文章/帖子混合到我们自己的应用程序主题中的文章时间轴中,以及更有趣的东西...

第一步是迁移后端应用程序以获得一致的管理和发布界面。传统的前端应用程序仍然需要写入数据库(评论和访问者创建的其他内容)。因此,修复数据库的过程必须能够定期无人值守。

我们已经有了适当的修复方法,可以优雅地处理belongs_to和has_many中损坏的模型依赖项。 Paperclip集成设计用于处理所有发明的奇妙文件名映射。 airbrake gem会将所有应用程序崩溃报告给我们的redmine安装程序,以便我们快速了解所有左侧怪癖。

旧版应用程序已经过修改,可以使用最新的MySQL版本,并已迁移到当前的MySQL数据库服务器。

4 个答案:

答案 0 :(得分:2)

我遇到了同样的问题。解决方案是告诉mysql2不要执行转换,如下所示:

client.query(sql, cast: false).each do |row|
  row['some_date'] = Date.parse(row['some_date']) rescue(nil)
end

有关如何构建客户端对象的详细信息,请参阅mysql2 documentation。如果需要,可通过ActiveRecord::Base.configurations访问rails db config。

答案 1 :(得分:0)

创建数据导入rake任务,执行所需的所有转换和修复(包括数据解析和修复),并在每次从旧应用程序获得最新更新时运行它。该任务可以使用原始SQL(查找“执行”和“exec_query”方法),它不必与模型一起使用。这将是你正在寻找的神奇“宝石”。显然,你不可能拥有一个万能的工具,因为每一个破碎的数据都是独一无二的。 但是,不要在新的代码库中创建kludges。

答案 2 :(得分:0)

类似于:Rails: How to handle existing invalid dates in database?并且没有正确答案,所以我在下面重新发布我的解决方案。

我认为对我有用的最简单的解决方案是设置 database.yml 文件写 cast:false ,例如发展部分

development
  <<: *default
  adapter: mysql2    
  (... some other settings ...)
  cast: false

答案 3 :(得分:-1)

我相信它会解决您的问题Date.parse()

e.g。 Date.parse(foo.created_at)