メインコンテンツまでスキップ

トラブルシューティング: データベースとマイグレーション

マイグレーションは前方向のみであり、起動時に自動実行されることはなく、明示的な運用手順(make migrate)で適用する。ここで起きる問題のほとんどは、マイグレーションランナーが処理を拒否したものであり、それは意図された動作である。説明のつかないスキーマ変更を適用するのではなく、停止する。

このページで合っているか分からない場合はトラブルシューティングから始めること。

マイグレーションロールが指定されていない

MERLON_MIGRATION_DATABASE_URL is required in production

本番以外では次の警告になる。

using MERLON_DATABASE_URL as migration role; production must use a separate role

スキーマ変更とリクエスト処理は意図的に異なるロールで行う。サービング用ロールは DDL 権限を持たず、audit_logs を変更できない。マイグレーションをサービング用ロールで実行すると、監査統制がまさに与えまいとしている権限をそのロールに与えることになる。

同じデータベースを異なるロールで指すよう、両方を設定する。

export MERLON_DATABASE_URL='postgres://merlon_app:...@host:5432/merlon'
export MERLON_MIGRATION_DATABASE_URL='postgres://merlon_migrate:...@host:5432/merlon'
make migrate

本番以外ではこの警告は致命的ではないため開発作業は止まらない。ただしそれは、本番が強制している権限分離を開発環境が検証できていないことを意味する。

マイグレーションのチェックサムが一致しない

migration 021 checksum mismatch: ledger=<sha256> file=<sha256>

ランナーは適用したすべてのマイグレーションの SHA-256 を記録する。このメッセージは、ディスク上のファイルがこのデータベースに適用されたファイルと異なっていることを示す。

これはロールアウトを停止すべき状態であり、ランナーの判断は正しい。データベースの実際のスキーマがリポジトリ上のマイグレーション履歴と対応しなくなっており、以降のマイグレーションについて何も推論できない。

原因は、可能性の高い順に次のとおり。

  1. 適用済みのマイグレーションが編集された。 適用済みマイグレーションは不変である。ファイルを適用時の内容に戻し、変更は新しいマイグレーションとして追加する。
  2. 別のブランチや環境のデータベースを指している。 MERLON_MIGRATION_DATABASE_URL が実際にどのデータベースへ解決されるか確認する。
  3. 改行コードが変わった。 チェックサムはバイト列を対象とする。同じ SQL でも CRLF で書き直されればハッシュは変わる。

これを回避するために schema_migrations の行を削除してはならない。それはスキーマの不整合を解消せず、不整合が起きた証跡だけを消す。

既存データベースに台帳が無い

MERLON_MIGRATION_BASELINE "0xx_name.sql" does not match a migration filename

あるいは、台帳導入前のデータベースに対して、ランナーが既に存在するマイグレーションを適用しようとする。

ランナーはテーブルの内容からベースラインを推測しない。どのマイグレーションが「適用済みに見えるか」を推測することこそ、スキーマが気づかぬうちに中途半端に適用される原因である。ベースラインは明示的に指定する。

MERLON_MIGRATION_BASELINE=017_retention.sql make migrate

そのファイル名までのマイグレーションは、実行されずに適用済みとして記録される。それ以降は通常どおり適用される。値は migrations/ に実在するファイル名と正確に一致する必要がある。テーブル名から名前を組み立てるのではなく、ls migrations/ の出力からコピーすること。

実施前にバックアップを取ること。誤ったベースラインは本来必要なマイグレーションを飛ばし、台帳はそれらが実行されたと主張することになる。

復元したデータを復号できない

直接的な PII 顧客属性は MERLON_ENCRYPTION_KEY_RING によって保存時に暗号化される。鍵はデータベース内には無い。

対応する鍵リングを伴わないデータベースバックアップは、恒久的に読み取れない。 復旧経路は存在しない。サポート経路も、ベンダー経路も無い。データは失われる。

復元したデータベースで顧客属性がエラーになる、あるいは読めない値を返す場合、鍵リングにそれらを暗号化した鍵が含まれていない。データベースバックアップと同時点の鍵リングを復元すること。

鍵ローテーションはバッチで再暗号化を行うため、バックアップがローテーションより前の時点であることは容易に起こる。退避した鍵は、その鍵で書かれたバックアップを保持する期間以上、保持すること。バックアップと復元を参照。

上記いずれかの対応後の確認

# 2回適用しても no-op であること。2回目は適用対象なしと報告する。
make migrate && make migrate

# 追記専用の監査ログが引き続き検証できること。
cd api && go run ./cmd/merlon-audit verify

# readiness に異常が無いこと。
curl -s http://localhost:8080/healthz/ready