本番稼働中のRailsアプリで、カラム名を変更したいという状況が最近ありました。
単純に rename_column :models, :before_column, :after_column
とやろうと思いましたが、こうするとダウンタイムが発生してしまうことが分かりました。
そこでいろいろ調査して、実際に自分がダウンタイムを発生させずにカラム名を変更できた方法を書き残したいと思います。
この他にもやり方はいくつもありそうですが、1つの例として参考にしていただけますと幸いです。
環境
- Ruby 3.0.1
- Rails 6.0.4
手順
- カラム追加とデータ移行
- 既存ロジックの修正とignored_columns指定
- カラム削除
カラム追加とデータ移行
カラム追加
リネーム後のカラムを追加します。
add_column :models, :after_column, :boolean
データ移行
before_column
→ after_column
のデータ移行を行います。
自分は直接SQLを叩いて対応しました。
UPDATE models SET after_column = before_column;
既存ロジックの修正とignored_columns指定
既存ロジックの修正
before_column
を参照している箇所を修正します。
after_column
を参照するように修正します。
APIの破壊的な変更ではない場合
- if before_column
+ if after_column
# 処理
end
APIの破壊的な変更の場合
フロントやアプリが参照している場合は注意が必要です。
ざっくり以下の手順が必要になると思います。
- バックエンド側で、既存APIのロジック修正と新規APIの追加
- フロントエンド側で、既存APIから新規APIに参照先を変更
- バックエンド側で、既存APIの削除
以下にGraphQLを使っている場合の例を示します。
バックエンド側で、既存APIのロジック修正と新規APIの追加
app/graphql/types/model.rb
field before_column, Boolean, 'リネーム前のカラム'
+ field after_column, Boolean, 'リネーム後のカラム'
+ def before_column
+ after_column
+ end
フロントエンド側で、既存APIから新規APIに参照先を変更
query.graphql
query ModelQuery($id: Int!) {
model(id: $id) {
- beforeColumn
+ afterColumn
}
}
バックエンド側で、既存APIの削除
app/graphql/types/model.rb
- field before_column, Boolean, 'リネーム前のカラム'
field after_column, Boolean, 'リネーム後のカラム'
- def before_column
- after_column
- end
ignored_columns指定
以下の記述を該当モデルに追加します。
これにより、Railsからリネーム前のカラムが見えないようにします。
self.ignored_columns = %i[before_column]
カラム削除
リネーム前のカラムを削除します。
remove_column :models, :before_column