データベースマイグレーション成功後に本当に確認すべきこと

データベースマイグレーションがエラーなく完了した。パイプラインはグリーン表示。チームはひと安心する。しかし1時間後、ユーザーからページの読み込みが遅いと報告が入り始める。一部のクエリがタイムアウトし、APIコールが500エラーを返す。マイグレーションは技術的に成功したが、水面下で何かが起こっていた。

このシナリオは、ほとんどのチームが想定するよりも頻繁に発生する。例外をスローせずに完了したマイグレーションと、システムを健全な状態に保ったまま完了したマイグレーションは同じではない。この2つの結果の差を捉えるのが、マイグレーション後の検証(ポストマイグレーションベリフィケーション)の目的である。

成功コードだけでは不十分な理由

ほとんどのマイグレーションツールは、エラーなく完了すると終了コード0を返す。これはSQLが実行され、ツールの内部追跡テーブルが更新されたことを示す。しかし、新しいスキーマがアプリケーションと適切に動作するか、クエリのパフォーマンスが変化したか、マイグレーションがロックを残したかどうかは教えてくれない。

マイグレーションは技術的に成功しても、実際に損害を引き起こす可能性がある。デフォルト値を持つカラムを追加すると、大きなテーブルが数分間ロックされることがある。カラムの型を変更すると、データベースが行を書き換える必要が生じ、同時実行クエリが遅くなる。インデックスを追加すると、あるクエリには役立つが、別のクエリの実行計画を壊すことがある。これらの問題はいずれもマイグレーションツールの終了コードには現れない。

ポストマイグレーションベリフィケーションとは、マイグレーション実行後にデータベースとアプリケーションの実際の状態を確認するプラクティスである。これにより、盲目的なデプロイから情報に基づいたデプロイへと変わる。

マイグレーションステータスを適切に確認する

最初に確認すべきことは、マイグレーションが完全に完了したのか、途中で停止したのかである。一部のツールはマイグレーションをバッチで適用する。3番目のバッチで失敗した場合、最初の2つのバッチはすでにデータベースを変更している。終了コードは0以外かもしれないが、損害はすでに部分的に発生している。

マイグレーションツールの追跡テーブルまたは詳細ログを確認する。どのステートメントが適用され、どこで処理が停止したのかを正確に把握する。この情報は、データベースが一貫した状態にあるのか、再試行前に手動でのクリーンアップが必要なのかを教えてくれる。

終了コードだけに依存してはならない。一部のマイグレーションは、致命的ではないが潜在的な問題を示す警告(非推奨の構文や暗黙の型変換など)を生成する。これらの警告をログに記録し、パイプラインのレポートに含める。

クエリレイテンシをマイグレーション前後で比較する

スキーマ変更により、データベースがクエリを実行する方法が変わることがある。テーブルに追加されたカラムが原因で、クエリプランナーが異なるインデックスやフルテーブルスキャンを選択する可能性がある。データ型の変更により、特定の比較でインデックスの使用が無効になることがある。

パイプラインでは、マイグレーションの前後でデータベースに対して代表的なクエリセットを実行する必要がある。各クエリのレイテンシを比較する。いずれかのクエリで大幅な増加が見られた場合、マイグレーションがパフォーマンスを損なう方法で実行計画を変更したことを示すシグナルである。

アプリケーションが最も頻繁に使用するクエリ、またはパフォーマンスに敏感であることがわかっているクエリに焦点を当てる。このチェックのために重い分析クエリを実行してはならない。デプロイウィンドウ中にデータベースに負荷をかけないよう、検証クエリは軽量に保つ。

解放されなかったロックを確認する

スキーマを変更するマイグレーションは、多くの場合、テーブルや行に対するロックを取得する必要がある。ほとんどのロックはマイグレーション終了時に解放されるが、常にそうとは限らない。長時間実行トランザクション、適切にクローズされなかった接続、タイムアウトしたマイグレーションなどにより、ロックが残ることがある。

マイグレーション完了後、データベースのアクティブなロックを確認する。ロックがまだ保持されている場合、アプリケーションが影響を受けるテーブルにアクセスしようとすると、タイムアウトやキューイングの蓄積が発生する。パイプラインは、マイグレーション中にロックがどのくらいの時間保持されたかもログに記録する必要がある。本番テーブルでロックが数秒以上保持された場合、最終的に解放されたとしても調査に値する。

マイグレーションを実行したテーブルにロックがまだ残っていないか確認するには、次のクエリを実行する:

SELECT
    pg_locks.pid,
    pg_locks.mode,
    pg_locks.granted,
    pg_class.relname,
    pg_stat_activity.query,
    pg_stat_activity.state,
    pg_stat_activity.wait_event_type || ': ' || pg_stat_activity.wait_event AS wait
FROM pg_locks
JOIN pg_class ON pg_locks.relation = pg_class.oid
JOIN pg_stat_activity ON pg_locks.pid = pg_stat_activity.pid
WHERE pg_class.relname = 'your_table_name'
  AND pg_locks.granted = true;

行が返された場合、マイグレーションがロックを残している。query列とstate列を調査して理由を特定する。

データ変更を伴うマイグレーションの行数を検証する

一部のマイグレーションはスキーマ変更以上のことを行う。新しいカラムにデフォルト値を設定したり、テーブル間でデータを移動したり、重複をクリーンアップしたりする。これらの操作は、マイグレーションロジックにエッジケースがあったり、データが期待される形式と一致しない場合に、静かに行を見逃す可能性がある。

このようなマイグレーションの後、実際の行数と期待される行数を比較する。たとえば、マイグレーションが既存のすべての行に対して新しいカラムを埋めることを想定していた場合、そのカラムがNULLでない行数が総行数と一致することを確認する。不一致がある場合、マイグレーションはすべての行に適用されていない。

これらのチェックはシンプルなカウントクエリで実行する。データベースに不要な負荷をかける可能性のあるJOINや集計は避ける。

アプリケーションログでデータベースエラーを監視する

最も重要な検証ステップは、マイグレーション後もアプリケーションがデータベースと連携して動作できるかどうかを確認することである。現在実行中のアプリケーションコードは、古いスキーマ用に書かれている。マイグレーションが実行中のコードを壊す方法でスキーマを変更した場合、アプリケーションログにエラーが表示される。

カラムが見つからない、型が一致しない、クエリが失敗したなどのエラーを探す。これらのエラーは、アプリケーションとデータベースの同期が取れていないことを意味する。チームは、マイグレーションをロールバックするか、新しいスキーマに一致するコード修正をデプロイするかを迅速に決定する必要がある。

ユーザーがこれらのエラーを報告するのを待ってはならない。パイプラインは監視システムからアプリケーションログを取得し、データベース関連のエラーを自動的にスキャンする必要がある。

実践的なマイグレーション後チェックリスト

初めてポストマイグレーションベリフィケーションを設定する場合は、パイプラインに以下のチェックから組み込むこと:

次のフローチャートは、推奨される検証手順の順序を示している:

flowchart TD A[マイグレーションが正常に完了] --> B[マイグレーションステータスを確認] B --> C[クエリレイテンシを比較] C --> D[ロックを確認] D --> E[行数を検証] E --> F[アプリケーションログを監視] F --> G{すべてのチェックが合格?} G -- Yes --> H[そのまま維持して問題なし] G -- No --> I[チームにアラート]
  • マイグレーションステータス:完全に完了したか、失敗した場合はどこで停止したか
  • クエリレイテンシ:重要な上位5つのクエリが許容範囲内か
  • ロック:マイグレーション後にアクティブなロックが残っていないか
  • 行数:データ変更を伴うマイグレーションで数値が期待と一致するか
  • アプリケーションエラー:ログに新しいデータベース関連エラーが発生していないか

これらのチェックを毎回のマイグレーション後に自動的に実行する。結果をレポートとしてチームに送信する。すべてのチェックに合格すれば、マイグレーションはそのまま維持して安全である。いずれかのチェックに失敗した場合、チームは次のステップを決定するための十分な情報を得られる。

まとめ

マイグレーションのステータスがグリーンであることは、安全性の保証にはならない。本当のテストは、変更後もデータベースとアプリケーションが適切に連携して動作するかどうかである。ポストマイグレーションベリフィケーションは、「マイグレーションが実行された」と「システムが健全である」の間のギャップを埋める。これなしでは、盲目的にデプロイして運を天に任せることになる。