本番データに触れる前に必ずデータベースマイグレーションをドライランすべき理由

マイグレーションスクリプトを書いた。見た目は正しい。ロジックも問題ない。ステージングで実行してもすべてパスする。しかし本番で実行すると、何かがうまくいかない。カラムの制約に失敗する。外部キー違反が発生する。マイグレーションが予想の5分ではなく45分かかり、重要なテーブルをロックして、アプリケーション全体にタイムアウトの連鎖を引き起こす。

このシナリオはよくある。そして防ぐことができる。その手法はシンプルだ。本番データに触れる前に、必ずマイグレーションをドライラン(予行演習)することだ。

ドライランで実際に行われること

ドライランとは、文字通りの意味だ。マイグレーションスクリプトを実行するが、変更をコミットしない。テーブルは変更されない。行は移動されない。カラムは削除されない。スクリプトがエラーなく実行されるかどうかを確認するだけであり、問題があればデータに影響が出る前に発見できる。

最も一般的な方法は、マイグレーションをデータベーストランザクションでラップし、COMMITではなくROLLBACKで終了することだ。新しいカラムを追加して別のテーブルからデータを投入するスクリプトがある場合、全体をBEGIN TRANSACTION ... ROLLBACKで囲む。途中でエラーが発生すれば、トランザクションは自動的にキャンセルされ、データベースは元の状態に戻る。エラーが発生しなくても、手動でロールバックする。変更を適用するためではなく、スクリプトが有効であることを確認するために行うのだ。

SQLでの具体的な例を示す。

BEGIN TRANSACTION;

-- マイグレーションの例: 新しいカラムを追加して値を投入
ALTER TABLE users ADD COLUMN last_login_at TIMESTAMP;

UPDATE users
SET last_login_at = NOW()
WHERE last_login_at IS NULL;

-- ロールバック前にデータが正しいか確認
SELECT id, email, last_login_at FROM users LIMIT 10;

-- トランザクションをロールバックし、データベースを変更前の状態に戻す
ROLLBACK;

ドライランから得られるもの(構文エラー以外)

構文エラーや制約違反のチェックは明白な利点だ。しかし、ドライランからはさらに多くの有用な情報が得られる。

マイグレーションにかかる時間がわかる。影響を受ける行数がわかる。テーブルがロックされるかどうか、その期間もわかる。これは特に、1秒間に数千のリクエストを処理する本番データベースを対象とする場合に極めて重要な情報だ。ドライランでマイグレーションに30分かかり、その間メインテーブルがロックされることがわかれば、別の戦略が必要だと判断できる。オフピーク時に実行する、長時間ロックを回避するオンラインマイグレーション手法を使う、マイグレーションを小さなバッチに分割する、といった選択肢がある。

ドライランなしでは推測するしかない。ドライランがあれば、情報に基づいた意思決定ができる。

ドライランを実行する場所

理想的な場所は、本番に近いデータを持つステージング環境だ。しかし、すべてのチームが現実的なデータ量のステージングデータベースを持っているわけではない。その場合は、ある時点の本番データのスナップショットを取得し、別のデータベースにロードする。このスナップショットを実際のトランザクションに使ってはならないが、マイグレーションをテストするには十分だ。

さらに進んで、ドライランを自動化するチームもある。プルリクエストでマイグレーションスクリプトが変更されるたびに、CIジョブが自動的にドライランを実行する。これにより、コードがマージされる前に問題を発見できる。小さな投資で、後々のデバッグ時間を大幅に節約できる。

ドライランの結果の読み方

エラーや警告のないクリーンな出力は安心できる。しかし、そこで止まってはいけない。ログを注意深く確認しよう。

新しいカラムの型にデータが合わずに投入に失敗した行がないか。インデックスの競合がないか。外部キー違反がないか。ドライランが技術的には成功しても、論理的に失敗することがある。例えば、WHERE句が間違っていて、移動したデータが空だった場合だ。スクリプトは正常に実行され、エラーもない。しかし結果は無意味だ。

これを防ぐには、ドライラン完了後、あたかもマイグレーションが実際に行われたかのように、変更したテーブルの内容を確認するSELECTクエリを実行する。ロールバックする前の同じトランザクション内でこれを行える。別のセッションを開き、トランザクション内でマイグレーションを実行し、ロールバックする前に変更したテーブルにクエリを実行してデータが正しいことを確認する。この追加のステップにより、ドライランが構文チェックから論理チェックへと昇格する。

ドライランで保証できないこと

ドライランは、マイグレーションが本番でスムーズに実行されることを保証するものではない。完全に再現できない要因がある。同時実行クエリの負荷は異なる。データ量がはるかに大きい可能性がある。ロックのタイミングもずれる。しかし、ドライランは驚きを減らす。本番に近い環境でマイグレーションをテストし、結果が期待通りであれば、実際のマイグレーションをより高い確信を持って実行できる。

マイグレーション実行前の実用的チェックリスト

本番データに影響を与えるマイグレーションを実行する前に、以下の短いリストを確認しよう。

  • マイグレーションをトランザクションでラップし、ROLLBACKで実行する
  • 実行時間とロック期間を確認する
  • 影響を受ける行数が期待通りかを確認する
  • トランザクション内で変更したテーブルにクエリを実行し、データの正確性を確認する
  • 制約違反、インデックス競合、型の不一致がないかログを確認する
  • マイグレーションに時間がかかりすぎる、またはロックが重すぎる場合は、代替戦略を計画する

具体的な教訓

ドライランは余分なステップではない。自信を持ってデプロイできるか、ストレスフルなデプロイになるかを分けるステップだ。トランザクション内でマイグレーションを実行し、ロールバックし、結果を確認する。そして、コミットする準備ができているかを判断する。本番データが感謝するだろう。