LCL Engineers' Blog

バス比較なび・格安移動・バスとりっぷを運営する LCLの開発者ブログです。

Ruby on RailsでDB マイグレーションする際にシステム障害を回避するための工夫

Webエンジニアの森脇です。

LCLでは、DB設計変更で、DBマイグレーションをする際にシステム障害を回避するためのいくつか工夫をしています。今回は、その内容を簡単にご紹介いたします。

migration作成時の注意

ブランチ戦略

原則、migrationのブランチは独立させるようにしています。

  • migrationとコードのデプロイはタイミングを分けたほうが安全であり、さらに他のコード修正と混ぜると、切り戻し等がやりづらいくなるため
  • 新規JOBなど既存コードに影響しない場合は、独立させなくても可

既存コードに影響を与えない

migration実行後も既存コードで正常稼働するように、migrationを作成します。
以下にケース別の手順を紹介します。

テーブルの新規作成

テーブルの新規作成は特に意識することはありません。
ただし、既存テーブルへ外部キーを貼る場合は、後述の手順に従い注意が必要です。

テーブルの削除

以下の手順でリリースすることをルールとしています。

  1. 既存のソースを改修し、該当テーブルを参照しないようにする
  2. ローカル・テスト環境でテーブルを削除し、エラーにならないことを確認する
  3. いきなり削除せずに、まずはテーブルのrenameを行い数日様子を見る → 問題が発生すれば、再度renameをして元に戻す
  4. テーブル削除のmigationを適用する

columnの追加

column追加は基本的には安全ですが、PostgreSQLのalter tableはselect lockが掛かるため、defalut制約はつけないようにしています。

参照:明示的ロック

これは、大量レコードがあるテーブルにdefault制約をつけてalter table等してしまうと非常に時間がかかるためです。

add_column(:foods, :group_id, :integer, {:null=>false, :default=>0})
--- 失敗例:サイト側がSELECT滞留して障害
add_column(:foods, :group_id, :integer, {:null=>true})
--- 成功例:サイト影響なし

columnの削除

column削除は最も障害が発生しやすいです。
ignored_columns オプションを利用して削除前に事前に該当カラムを参照しないようにします。

参考:Rails アプリでオンラインでカラムの削除やリネームを行うには

columnのrename

columnのrenameを行うと既存コードに影響を与えてしまうため、column追加と削除の手順を組み合わせて対応します。

外部キーの追加

外部キーはロックがかかりやすいため、他のマイグレとは分けるようにしています。 また、一度に複数の外部キーを追加しないようにも注意しています。

migrationの実行手順

まず以下のコマンドを実行し、未実行のmigrationを把握します。

$ bundle exec rake db:abort_if_pending_migrations RAILS_ENV=production

安全のため、該当のバージョンのみ実行します。

$ bundle exec rake db:migrate:up VERSION=xxxxxxxxx  RAILS_ENV=production

一応downのmigrationをすぐに実行できるようにコマンドも準備しておくと良いです。

$ bundle exec rake db:migrate:down VERSION=xxxxxxxx  RAILS_ENV=production

さいごに

マイグレーションはひとつのミスが大きな障害に繋がる機能ですが、プロダクトが成長する上では割けては通れないものなので、手順をしっかり固めて、心理的安全性をできるだけ担保できるようにすることが大切です。