💎

運用中のRuby on Railsアプリで安全にカラムを削除する

2022/07/02に公開

手順

  1. 削除するカラムを使ってるコードをすべて修正
  2. ignored_columns をカラムに付ける
  3. ここまでの変更をリリースする
  4. カラムを削除する(カラムを削除するマイグレーションファイルを作成し、schema.rbを更新)
  5. ignored_columns を削除
  6. リリースする

削除するカラムを使ってるコードをすべて修正

実際にカラムを削除する前に、削除予定カラムを使わないようにすべて修正しておきます。

ignored_columns をカラムに付ける

ActiveRecordはスキーマキャッシュでカラム情報をキャッシュしてます。

プロダクション環境などで運用中のサービスに対していきなりカラムを削除すると、削除前のスキーマがキャッシュされてるので、Railsアプリケーション側はすでに削除されたカラムが存在するようにふるまってしまいます。

以下のようにignored_columnsにカラムを追加すると、Railsは該当カラムを無視するようにふるまいます。

app/models/post.rb
class Post < ApplicationRecord
  self.ignored_columns = [:title]
end

仕組みとしてはignored_columnsに指定されてるカラムはスキーマを読み込むときに除外される実装になっています。
https://github.com/rails/rails/blob/7-0-stable/activerecord/lib/active_record/model_schema.rb#L581

ここまでを一度リリースして、本番環境などへ反映させます。

カラムを削除する(カラムを削除するマイグレーションファイルを作成し、schema.rbを更新)

以下のようなコマンドでマイグレーションファイルを生成できます。

bundle exec rails generate migration クラス名 カラム名:データ型

なので今回は bundle exec rails generate migration RemoveTitleFromPosts title:string

db/migrate/20220629204906_remove_title_from_posts.rb
class RemoveTitleFromPosts < ActiveRecord::Migration[6.1]
  def change
    remove_column :posts, :title, :string
  end
end

のようなマイグレーションファイルを生成しました。
記事によってはchangeメソッドとupdowmメソッドで書かれてる記事がありますがどちらでもよいはずです。

ただ、少なくともchangeで行く場合は型情報を書いてあげないとロールバックに失敗するらしいので必要です。

remove_columnは、第3引数でカラムの型を指定すれば逆進可能になります。この場合、元のカラムオプションも指定しておくこと。そうしないと、マイグレーションの逆進時にカラムを再作成できなくなります。
https://railsguides.jp/active_record_migrations.html#changeメソッドを使う

bundle exec rails db:migrateでマイグレーションしてschema.rbが更新されてるのを確認します。

外部キーやインデックスの設定があればそれも削除されるはずです。

ignored_columns を削除

追加したignored_columnsを削除します。

app/models/post.rb
class Post < ApplicationRecord
-   self.ignored_columns = [:title]
end

リリースします。

デプロイの構成としてRailsアプリケーションのコードがWebサーバーにリリースされる前に、DBのマイグレーションが完了することが保証されていなければ4. カラムを削除するとまとめてリリースできます。

そうでなければ4. カラムを削除する5. ignored_columns を削除は分けてリリースしたほうがよさそうです。

これで完了です。

参考

https://scrapbox.io/shimoju/Railsでのカラムの消し方

https://tech.prog-8.com/entry/2020/12/08/093310

https://qiita.com/azusanakano/items/a2847e4e582b9a627e3a

Discussion