Rails中逐步从Paperclip升级到Active Storage的方法

290 阅读4分钟

如果你对我们不熟悉,FastRuby.io专门从事Ruby和Rails的升级工作。在过去的这10年中,我们有机会为客户进行了几十次升级,这给我们带来了各种各样的经验。我们经常遇到的情况是,升级并不直接,我们不能直接升级Ruby或Rails版本。

我们经常发现自己处于这样一种情况:在真正升级Ruby或Rails本身之前,我们需要升级一个或多个依赖项。例如,如果你想把你的应用从Rails 5.x升级到6.x,而该应用仍然使用Paperclip来管理你的文件附件,那么你首先需要替换该宝石,因为在Rails 5.2发布后,Paperclip被弃用,改用Active Storage

我们的一个客户就是这种情况,他已经和我们一起做了几年的升级。在这篇文章中,我将分享该团队发现的错误,以及我们采取的策略,将他们大量的附件迁移到Active Storage上,同时在迁移完成之前仍然保持Paperclip的活性。

问题所在

我们的客户有一个庞大的单体应用,有超过10年的代码和超过1200个Active Record模型,其中83个模型需要处理文件附件。当我们开始计划他们的Rails 6.0升级时,我们知道我们需要从Paperclip迁移,但我们仍有两个问题需要回答。

  1. 是否有可能迁移到Active Storage而不需要重新上传所有文件?
  2. 如果不能,什么是最好的方法?

经过几次失败的尝试,我们意识到Paperclip和Active Storage之间有太多的不兼容之处,不能继续尝试用猴子补丁来使其与现有的S3路径一起工作。所以我们决定使用Active Storage重新上传所有的文件。然后我们转到了第二个问题。

由于我们的客户拥有巨大的数据量,迁移过程将需要几个月(可能超过一年!),所以我们必须逐步进行。

为了确保在迁移过程中没有文件丢失,也没有文件不可用,我们的策略是继续使用Paperclip,直到我们使用Active Storage重新上传所有附件。是的,我们决定有一个重叠的时间,我们将使用两种文件管理机制。在完成迁移后,我们可以最终移除对Paperclip的依赖。

用后台作业迁移附件

为了执行迁移,我们创建了一个后台作业作为解析器,从Paperclip附件中获取信息并创建相应的Active Storage附件。由于Active Storage的工作方式与Paperclip不同,该工作所做的是为每个Paperclip附件创建附件和blob,并通过.attach 方法将其与相应的对象联系起来。

我们为每个模型创建了一个拉动请求,并进行了所有必要的修改,以开始使用Active Storage而不是Paperclip。这样,在我们等待作业队列处理和迁移现有附件时,新的附件将开始被保存在Active Storage中。

为附件创建一个后备方法

正如我所提到的,当后台作业被处理时,我们需要确保文件不会对用户不可用。对于只有几百个附件的最小模型来说,有几分钟的不可用是可以的。但对于有数百万附件的最大的模型,这个过程可能需要几天才能完成,所以不可用是不能接受的。

对于这些情况,我们创建了一个后备方法,在Active Storage附件还不可用的情况下返回Paperclip网址,正如我们在下面的代码片段中看到的那样。

def photo_url(user)
  return user.photo.service_url if user.photo.attached?
  ::Paperclip::Attachment.new(:photo, self).expiring_url
end

通过这个方法,我们确保了两种可能的情况。

  • 如果Active Storage的文件已经被附上,我们返回该文件的Active Storage url。
  • 如果没有,我们就创建一个回形针对象的实例并返回其网址。::Paperclip::Attachment.new(:photo, self) ,让我们访问回形针对象,即使我们在模型中不再有has_attached_file 指令。

当所有模型的所有文件都被处理后,我们可以创建一个新的PR,删除Paperclip gem和这个回退方法。

结论

正如我们看到的,有时我们不能直接迁移应用程序。在理想的情况下,我们只需运行bundle update rails ,一切就能正常工作。但是对于现实生活中的应用,我们经常需要事先更新一些依赖关系。在这篇文章中,我解释了我们是如何逐步迁移大量的Paperclip附件的,以确保我们没有为它们提供停机时间。

当然,由于客户应用程序的复杂性,我们不得不处理其他一些问题,我们不得不回传一些只有在Active Storage的未来版本中才能使用的功能,如一些模型的直接上传,非过期的URL,一些附件的不同服务配置和其他。但这些是另一个对话的主题。