データベースのロールバックがアプリケーションのロールバックと全く異なる理由
アプリケーションの新しいバージョンで問題が発生した場合、通常は簡単に修正できます。デプロイパイプラインでロールバックボタンを押せば、古いバージョンが戻ってきます。サーバーは問題のあるコードの実行を停止し、以前のコードを実行し始めます。システム依存関係や設定を変更していなければ、アプリケーションは数分以内に正常に戻ります。
これが機能するのは、アプリケーションがステートレスに設計されているからです。アプリケーションは、永続的なものを失うことなく、停止、再起動、バージョン切り替えができます。アプリケーションが処理するデータは、ユーザーまたはデータベースから取得されます。アプリケーションが数秒間ダウンしても、ユーザーは気付くかもしれませんが、データは失われません。古いバージョンが再び実行されると、何事もなかったかのようにすべてが再開されます。
データベースはそうはいきません。
データベースのステートフルな現実
データベースはステートフルなコンポーネントです。データベースは、アプリケーションのバージョン間で存続しなければならないデータを保持します。ユーザープロファイル、トランザクション、注文記録、構成設定など、すべてがデータベース内に存在し、そのまま維持されなければなりません。データベーススキーマを変更(カラムの追加、データ型の変更、テーブルの削除など)すると、データベースはその変更を永続的に記録します。アプリケーションを古いバージョンに戻したからといって、既に保存されているデータが自動的に以前の状態に戻るわけではありません。
これが、多くのチームを不意を突く根本的な違いです。彼らはデータベースのロールバックもアプリケーションのロールバックと同じように機能すると想定しています。つまり、逆のコマンドを実行すれば、マイグレーション前の状態にすべてが戻ると考えています。実際には、スキーマ変更を元に戻すことははるかに複雑でリスクが伴います。
問題の具体的な例
あなたのチームがデータベースマイグレーションを通じて users テーブルに phone_number カラムを追加したとします。マイグレーションは正常に実行され、その後数時間のうちにユーザーが電話番号を入力し始めます。その後、誰かがそのカラムを読み取るアプリケーションコードにバグを発見しました。チームはアプリケーションを以前のバージョンにロールバックすることにしました。アプリケーションは復元され、バグは解消されました。
しかし、phone_number カラムはまだデータベースに残っています。ユーザーが入力したデータもまだそこにあります。ここでロールバックマイグレーションを実行してそのカラムを削除すると、それらの電話番号はすべて失われます。完全に、です。そして、そのデータはすでに他の機能で使用されたり、他のユーザーに表示されたりしている可能性があります。結果を考慮せずに単純に削除することはできません。
このデータ損失のリスクこそが、データベースのロールバックを軽視できない理由です。マイグレーションがスキーマを変更するたびに、データが変換、移動、または削除される可能性があります。古いスキーマを復元するということは、古いデータ構造を復元することを意味します。データを正確に以前の状態に戻せることを保証するメカニズムがなければ、スキーマのロールバックはデータの不整合や欠落を引き起こす可能性があります。
コードとスキーマの不一致問題
データ損失以外にも、2つ目の問題があります。それは、アプリケーションコードとデータベーススキーマの間の非互換性です。
アプリケーションを古いバージョンにロールバックすると、その古いコードはデータベースに存在する新しいカラムやテーブルを認識しない可能性があります。予期しないカラムを読み取ろうとしてクラッシュしたり、知らない制約のためにデータの挿入に失敗したりするかもしれません。
逆に、現在のアプリケーションコードでまだ使用されているカラムを削除するロールバックマイグレーションを実行すると、アプリケーションは即座に壊れます。アプリケーションのデプロイとデータベースマイグレーションがまったく同じ瞬間に実行されることはほとんどないため、ロールバック中に両側の同期を保証することはできません。
この不一致は、本番環境では特に危険です。ほんの数秒の不整合でも、エラー、トランザクションの失敗、または回復が困難なデータ破損を引き起こす可能性があります。
同じロールバック戦略が適用できない理由
アプリケーションのロールバックが機能するのは、古いバージョンが既知の正常な状態だからです。コードはデプロイ前と同じです。環境も同じです。変更されるのは、実行中のバイナリだけです。
データベースのロールバックには、同じ意味での「既知の正常な状態」がありません。スキーマとデータは前に進んでいます。逆マイグレーションを実行しても、以前と同じデータベースが得られるわけではありません。見た目は古いスキーマと同じでも、データは変換、追加、削除されている可能性があり、慎重な計画なしには元に戻せない場合があります。
この問題に対処するために、一部のチームはすべてのマイグレーションの前にデータベースの完全バックアップを取得しようとします。このアプローチにも問題があります。
- バックアップの復元には時間がかかります(多くの場合、数秒ではなく数分から数時間)。
- バックアップ取得後に書き込まれたデータは失われます。
- 同じデータベースに依存する他のサービスが、復元中に壊れる可能性があります。
- 復元自体が失敗し、さらに悪い状態になる可能性があります。
より安全な道:ロールフォワード
これらのリスクのため、経験豊富なチームの多くは、データベースのロールバックを最後の手段として扱います。失敗したマイグレーションを元に戻そうとする代わりに、彼らはロールフォワードを好みます。つまり、スキーマを戻さずに問題を修正する新しいマイグレーションを書くのです。
例えば、マイグレーションが誤ってまだ必要なカラムを削除してしまった場合、ロールフォワードのアプローチは、削除を元に戻そうとするのではなく、新しいマイグレーションでカラムを再度追加することです。これにより、スキーマは一方向に進み続け、データ変換を元に戻す複雑さを回避できます。
ロールフォワードが常に可能であるとは限りません。損害が大きすぎる場合や、修正にフォワードマイグレーションとして表現できないスキーマ変更が必要な場合もあります。しかし、ほとんどの場合、データ損失やアプリケーションの破損を引き起こす可能性のあるロールバックを試みるよりも安全です。
データベース変更の安全性に関する実践的なチェックリスト
本番環境でデータベースマイグレーションを実行する前に、このチェックリストを確認してください。
- マイグレーションはデータを失わずに元に戻せますか? そうでない場合は、代わりにロールフォワード戦略を計画してください。
- マイグレーションの直前に取得したデータベースのバックアップはありますか? そのバックアップが実際に復元可能かどうかをテストしてください。
- マイグレーションは現在のアプリケーションコードと後方互換性がありますか? マイグレーションの実行中も古いコードが動作する必要があります。
- 変更するスキーマに依存する他のサービスやデータベースはありますか? それらのチームと調整してください。
- ユーザーが報告する前に、マイグレーションが問題を引き起こしたことを検出する方法はありますか? 監視とアラートを設定しておく必要があります。
まとめ
データベースのロールバックは、単純な元に戻すボタンではありません。データ損失、アプリケーションエラー、長期ダウンタイムを引き起こす可能性のある高リスクな操作です。手術と同じ注意をもって扱ってください。事前に計画し、バックアップを用意し、可能な限りフォワードフィックスを優先してください。最も安全なロールバックは、実行する必要がないロールバックです。