Rails: Active Recordモデルのカラムを安全に削除する(翻訳)
既存のActive Recordモデルに新しいカラムを追加してデプロイするのは、多くの場合問題なくできます。通常は、最初のデプロイでマイグレーションを実行し、それが終わってから、次にデータベースに追加されたカラムを利用する新しいコードをリリースするという手順になります。
しかし、カラムを削除する場合は問題が起きやすい傾向があります。Railsアプリケーションを起動すると、Active Recordがデータベースカラムをキャッシュします。このカラムをデータベーステーブルから削除すると、アプリを再起動するか再デプロイするまで例外が発生します。
データベースからカラムを削除するときは、手順を分割する戦略が有用です。
🔗 以下のように削除すること
作業を多段階に分ける戦略を用いる。
最初に、ignored_columns
でActive Recordがそのカラムを無視するようにします。これにより、アプリケーションの他の部分はこのカラムを参照できなくなります。
class Thing < ApplicationRecord
# ...
self.ignored_columns = ["old_column"]
# ...
end
カラムを使っている場所を削除する前なら、カラムがまだ使われている箇所をテストスイートで見つける方法が使えます。テストがパスしたら、そのコードをデプロイします。
次に、データベースのカラムを実際に削除するマイグレーションを作成します。
class RemoveOldColumnFromThings < ActiveRecord::Migration[7.0]
def change
remove_column :things, :old_column
end
end
これをデプロイして、カラムを忘却の彼方へマイグレーションします。
それが終わったら、最後の仕上げとして冒頭で追加したignored_columns
行を削除し、再度デプロイします。
🔗 そうする理由
チームやアプリケーションの規模が大きい場合は、エラーやダウンタイムが発生しないよう、安定かつ予測可能な手法でデータベースの変更作業を進めることが非常に重要です。
Railsにはデータベースを変更するツールが組み込まれていますが、稼働中のproduction環境でこれを行うときは、多くの場合いつもよりさらに注意深く作業を進めることが要求されます。
「シンプルな」カラムを削除するだけでもデプロイが3回必要になることを考えれば、この方法の前提条件として、ある程度以上のまともなテストカバレッジと、1日に数回に分けてデプロイするための健全な手順を揃えておく必要があります。
そのためのgemがあります
InstaCartで最初に開発されたstrong_migrations gemを調べてみてください。
このgemは、コードが読み書きを数秒以上ブロックする場合や、今回のようにアプリケーションで他のエラーが発生する可能性が高い場合に、危険な可能性があるマイグレーションを制止して警告し、有用な指示を表示してくれます。
🔗 そうしない理由があるとすれば
トラフィックの少ないプロジェクトや、できて間もないRailsアプリケーションの場合は、そこまで厳密に必要ではありません。このような場合は、デプロイ時に少々発生するエラーに耐えながら作業すれば何とかなるかもしれません。
それでも、このような慎重な作業は早い時期から始めておくのが良い習慣です。
概要
元サイトの許諾を得て翻訳・公開いたします。
参考: Rails API
ignored_columns=
--ActiveRecord::ModelSchema::ClassMethods