データマイグレーションがアプリケーションデプロイと根本的に異なる理由

CI/CDパイプラインが順調に動いています。アプリケーションのデプロイは日常業務です。ローリングアップデート、ブルーグリーンデプロイ、カナリアリリースも、チームは難なくこなしています。そんな時、誰かが「データベーススキーマを変更して、古いテーブルから新しいテーブルにデータを移行する必要がある」と言い出します。すると、突然、部屋の空気が変わります。皆が質問を始めます。誰かがバックアップが最近取られているか確認します。別の誰かがメンテナンス時間帯に実施できるか尋ねます。先ほどまでの自信はどこかへ消え去ります。

これはチームのスキルが不足しているからではありません。データマイグレーションがアプリケーションデプロイとは根本的に異なるからです。両者を同じように扱うことは、パイプラインのグリーンランプがいくつあっても防げない本番インシデントを招くレシピです。

ステートフルな問題

アプリケーションは設計上ステートレスです。新しいバージョンをデプロイするとき、メモリやディスク上で動作するコードを置き換えています。新しいバージョンが壊れた場合、以前のバージョンにロールバックします。古いコードはリポジトリに残っています。それを再実行するだけです。ユーザーは数分のダウンタイムを経験するかもしれませんが、データは影響を受けません。

データベースはその逆です。データベースは状態(ユーザーアカウント、トランザクション履歴、設定、注文記録)を保持します。カラムを削除するマイグレーションを実行すると、そのカラムのデータは失われます。何百万行もの日付形式を変更すると、その日付は自動的には元に戻りません。データ変更に対する「元に戻す」ボタンはありません。バックアップから復元することはできますが、それは最後のバックアップ以降に作成または変更されたデータを失うことを意味します。

この不可逆的な性質こそが、データマイグレーションを緊張させる理由です。アプリケーションデプロイの失敗は回復可能です。データマイグレーションの失敗は、開始前に正確な復旧計画を立てていない限り、永久的なものになります。

次の図は、2つのフローを並べて比較しています。

flowchart TD subgraph AppDeploy["アプリケーションデプロイ"] A1[コード] --> A2[ビルド] A2 --> A3[テスト] A3 --> A4[デプロイ] A4 --> A5{成功?} A5 -- Yes --> A6[完了] A5 -- No --> A7[旧バージョンにロールバック] A7 --> A6 end subgraph DataMigrate["データマイグレーション"] B1[スキーマ変更] --> B2[データ移動] B2 --> B3{成功?} B3 -- Yes --> B4[検証とリコンシリエーション] B3 -- No --> B5[バックアップから復元] B5 --> B6[データ損失の可能性] end AppDeploy -.->|可逆的| DataMigrate

ユーザーへの直接的な影響

アプリケーションがクラッシュすると、ユーザーはエラーページを目にします。リロードしたり、後で再試行したり、サポートに連絡したりするかもしれません。迷惑ではありますが、データは安全です。

データマイグレーションが失敗した場合、誰かが気づくまで被害は見えません。口座残高を誤計算したり、配送先住所を上書きしたり、注文履歴を削除したりするマイグレーションは、500エラーとして現れません。ユーザーがアカウントを確認して誤った情報を見つけたときに初めて明らかになります。その時点で、マイグレーションはすでに実行され、データは変更されています。ユーザーは単なる不便さではなく、実際の損害を被っています。

このユーザーデータへの直接的な影響は、異なるレベルの注意を要求します。ステージングでテストしてから本番に昇格させるコードデプロイと同じようにデータマイグレーションを扱うことはできません。リスクはより高く、障害モードは検出がより困難です。

実行時間と制約

アプリケーションデプロイは通常、数秒から数分で完了します。ローリングアップデートは、目立ったダウンタイムなしにインスタンスを1つずつ置き換えることができます。ユーザーはデプロイが行われたことすら気づかないかもしれません。

データマイグレーションには数時間かかることがあります。数百万レコードのテーブルのすべての行を更新するマイグレーションは、テーブルをロックし、データベースリソースを消費し、クエリを遅くします。その間、アプリケーションは縮退モードで動作する必要があるかもしれません。一部の機能は無効になり、一部のエンドポイントはエラーを返すかもしれません。アプリケーションを完全にオフラインにする必要すらあるかもしれません。

この長時間実行という性質は、調整の問題を引き起こします。誰がマイグレーションを監視するのか?途中で失敗したらどうするのか?ステータスをチームの他のメンバーにどのように伝えるのか?これらは、コードデプロイ中には通常尋ねない質問です。

データマイグレーションを安全にするもの

データマイグレーションはリスクが高いため、異なる種類の安全策が必要です。これらはオプションの追加機能ではありません。データ変更を真剣に扱うための最低限の要件です。

冪等性。 マイグレーションスクリプトは複数回実行しても安全であるべきです。途中で失敗した場合、問題を修正して再実行しても、データの重複や不整合な状態を引き起こさないようにする必要があります。これは、IF NOT EXISTSチェック、UPSERT操作、または変更がすでに適用されているかどうかを検出する条件ロジックを使用することを意味します。

ドライラン機能。 本番データに触れる前に、可能な限り本番を模した安全な環境でマイグレーションを実行する必要があります。これはステージングでテストすることと同じではありません。ドライランでは、何が変更されるのか、どれくらいの時間がかかるのか、制約に違反するものがないのかを正確に示す必要があります。

バックフィル戦略。 一部のデータマイグレーションでは、履歴レコードから欠落データを補完する作業が含まれます。これは一度限りの操作ではありません。バックフィルはインクリメンタルに行い、監視し、ロールバック可能であるべきです。一時停止して結果を確認し、すべてが正しければ再開できるようにする必要があります。

リコンシリエーション。 マイグレーション完了後、データが正しいことを証明する必要があります。これは、古い状態と新しい状態を比較するクエリを実行し、行数をチェックし、合計を検証し、異常を探すことを意味します。リコンシリエーションは「あると良い」ものではありません。マイグレーションが意図したとおりに動作したことを確認する唯一の方法です。

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

本番でデータマイグレーションを実行する前に、このリストを確認してください。

  • マイグレーションスクリプトは冪等ですか?2回実行しても問題ありませんか?
  • 本番データのコピーに対してドライランを実施しましたか?
  • マイグレーションにかかる時間は把握していますか?その時間枠を計画しましたか?
  • 「バックアップから復元するだけ」に依存しないロールバック計画はありますか?
  • 結果を検証するためのリコンシリエーションクエリを作成しましたか?
  • 誰がマイグレーションを監視し、問題が発生した場合に誰に連絡するか、チームで合意していますか?

まとめ

データマイグレーションは、別のスクリプトを使ったアプリケーションデプロイではありません。それは異なるカテゴリの作業であり、独自のプロセス、独自の安全策、独自の完了定義を必要とします。次回チームがスキーマ変更やデータ移動を計画するときは、立ち止まって自問してください。冪等性、ドライラン、バックフィル、リコンシリエーションはカバーされていますか?もし答えが「いいえ」なら、そのマイグレーションを実行する準備はできていません。