事例

本番だけ落ちる移行処理――冪等でないDDLが招く再現性の罠

ステージングでは通ったDDLが空の本番DBで失敗。原因は『既存前提』の書き方にあった。冪等化で再現性を取り戻した実例から学ぶ環境差対策。

Papapapapa
データベースマイグレーションDevOps本番障害冪等性
本番だけ落ちる移行処理――冪等でないDDLが招く再現性の罠

本番だけ、爆死した。

ステージング環境では何度も流していたはずのDDL(データベースの構造を変更する処理)が、本番リリース当日、まっさらな本番DBに適用した瞬間にエラーで止まりました。コードは一切変えていません。手順も同じです。それなのに、片方では通り、片方では落ちる——この事実に、現場は凍りつきました。

「動作確認済みのはずなのに、なぜ」という戸惑いは、実は多くの現場が一度は通る道です。原因を先に言ってしまうと、DDLの書き方そのものに「差分ありき」の前提が紛れ込んでいたことにありました。

「すでにある」を前提にしたDDLの罠

ステージング環境というのは、多くの場合、過去に何度もDDLを適用してきた履歴の積み重ねの上に存在します。テーブルが存在する、カラムが存在する、インデックスが存在する——そうした「すでにある状態」が、知らないうちに前提として組み込まれてしまうのです。

一方、本番リリースの初回適用や、災害復旧・環境再構築のために作る空のDBでは、その前提は一切成り立ちません。テーブルを追加しようとしてもテーブル自体がない、カラムを変更しようとしてもカラム自体がない。存在するはずのものが存在しないため、DDLはそこで例外を吐いて止まります。

つまり「動いた」という実績は、コードの正しさを証明していたのではなく、たまたまその環境に積み上がっていた履歴の正しさを証明していたに過ぎなかった、ということです。これは恐ろしい事実です。テストで検証していたのは処理ではなく、環境の方だったのですから。

何度流しても同じ結果に着地させる

この問題を解決するために行った修正が、DDLの冪等化です。冪等というのは、同じ処理を何度実行しても、最終的に同じ結果に収束するという性質を指します。

具体的には、あるテーブル定義まわりの2件のDDLファイルを対象に、テーブルやカラムを操作する前に「すでに存在するかどうか」を確認する処理を追加しました。存在するなら変更をスキップし、存在しないなら新規に作成する——この分岐を組み込むことで、履歴が積み上がった既存環境で流しても、何もない空のDBに初めて流しても、結果的に同じ最終状態にたどり着くようになります。変更量としては2ファイルで41行の追加、14行の削除という、決して大きくはない修正でした。しかし、この小さな修正こそが「本番だけ落ちる」という再現性ギャップを根本から塞ぐ一手です。

重要なのは、これが単なるエラー回避のパッチではないという点です。存在チェックを入れるという行為は、「このDDLがどんな状態のDBに対しても、同じ結果を保証する」という設計思想への転換を意味します。差分を前提にした書き方から、状態を前提にした書き方への転換、と言い換えてもいいかもしれません。

新設テーブルと初期データにも同じ影が差す

同じ落とし穴は、テーブルを新設する場面や、初期データを投入する場面にも潜んでいます。実際に、テナントごとの外部サービス連携設定を保存するテーブルを新設した際には、初期データ投入処理に「初回ロードかどうか」を示すフラグを追加する修正を行いました。2ファイルで105行の追加という規模の変更でしたが、狙いは同じです。初めてデータを投入する場合と、すでにデータが入っている環境に再投入する場合とで、処理が同じ結果に収束するようにする、という発想です。

また、社員の所属店舗を突き合わせるロジックの修正でも、似た教訓がありました。完全一致だけを前提にしていた突合処理に、接尾一致でも解決できる仕組みを26行追加したのですが、これも「特定の命名パターンが存在する」という暗黙の前提に依存していた処理を、より緩やかな条件でも成立するように直したという点で、根は同じです。前提条件がわずかにでも崩れる環境が存在する限り、そこに事故は潜んでいます。

「動いた」を証拠にしないための備え

ここから得られる判断は、シンプルです。ステージングや本番の「動いた」という結果は、コードの正しさの証拠にはならない、ということです。証拠になるのは、履歴のない空のDBに対しても同じ処理が同じ結果で通ることだけです。

実務的な備えとしては、定期的に空のDBへゼロから移行処理一式を適用し直す検証を、リリース前の恒常的な工程に組み込むことが有効です。手間はかかりますが、本番リリース当日に初めて空の状態でDDLを流す、という一発勝負を避けられます。

環境をまたぐ限り、「動いた」は証拠になりません。動いた環境と、動かなかった環境の違いがどこにあるのかを問い続ける姿勢こそが、次の「本番だけ爆死した」を防ぐ最も確実な備えです。

この記事をシェア

関連記事