Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rails: Active Recordモデルのカラムを安全に削除する(翻訳)

概要

元サイトの許諾を得て翻訳・公開いたします。

参考: Rails API ignored_columns= -- ActiveRecord::ModelSchema::ClassMethods

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を調べてみてください。

ankane/strong_migrations - GitHub

このgemは、コードが読み書きを数秒以上ブロックする場合や、今回のようにアプリケーションで他のエラーが発生する可能性が高い場合に、危険な可能性があるマイグレーションを制止して警告し、有用な指示を表示してくれます。

🔗 そうしない理由があるとすれば

トラフィックの少ないプロジェクトや、できて間もないRailsアプリケーションの場合は、そこまで厳密に必要ではありません。このような場合は、デプロイ時に少々発生するエラーに耐えながら作業すれば何とかなるかもしれません。

それでも、このような慎重な作業は早い時期から始めておくのが良い習慣です。

関連記事

Rails: データベーススキーマをダウンタイムなしで変更する(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。