運用中のRuby on Railsアプリで安全にカラムを削除する
投稿日: 2024/06/25
手順
- 削除するカラムを使ってるコードをすべて修正
- ignored_columns をカラムに付ける
- ここまでの変更をリリースする
- カラムを削除する(カラムを削除するマイグレーションファイルを作成し、schema.rbを更新)
- ignored_columns を削除
- リリースする
削除するカラムを使ってるコードをすべて修正
実際にカラムを削除する前に、削除予定カラムを使わないようにすべて修正しておきます。
ignored_columns をカラムに付ける
ActiveRecordはスキーマキャッシュでカラム情報をキャッシュしてます。
プロダクション環境などで運用中のサービスに対していきなりカラムを削除すると、削除前のスキーマがキャッシュされてるので、Railsアプリケーション側はすでに削除されたカラムが存在するようにふるまってしまいます。
以下のようにignored_columns
にカラムを追加すると、Railsは該当カラムを無視するようにふるまいます。
app/models/post.rb
class Post < ApplicationRecord self.ignored_columns = [:title] end
仕組みとしてはignored_columns
に指定されてるカラムはスキーマを読み込むときに除外される実装になっています。
activerecord/lib/active_record/model_schema.rb
def load_schema! unless table_name raise ActiveRecord::TableNotSpecified, "#{self} has no table configured. Set one with #{self}.table_name=" end columns_hash = connection.schema_cache.columns_hash(table_name) columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
ここまでを一度リリースして、本番環境などへ反映させます。
カラムを削除する(カラムを削除するマイグレーションファイルを作成し、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
メソッドとup
&dowm
メソッドで書かれてる記事がありますがどちらでもよいはずです。
ただ、少なくともchange
で行く場合は型情報を書いてあげないとロールバックに失敗するらしいので必要です。
remove_columnは、第3引数でカラムの型を指定すれば逆進可能になります。この場合、元のカラムオプションも指定しておくこと。そうしないと、マイグレーションの逆進時にカラムを再作成できなくなります。
https://railsguides.jp/active_record_migrations.html#change%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%82%92%E4%BD%BF%E3%81%86
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 を削除
は分けてリリースしたほうがよさそうです。
これで完了です。