Metabase の H2 DB が破損した時に修復せず作り直した話
こんにちは、BABY JOB 開発部のミヤギです。
今回は、技術的な内容を中心にお届けします。
はじめに
Metabase のデフォルトデータベース ( H2 ) が破損し、RDS( PostgreSQL )へ移行しようとして失敗した際の技術的な記録を共有します。
最終的に「移行断念・新規構築」という判断に至りましたが、その過程で行った調査手順やエラー原因の特定方法を技術ログとして残します。
前提知識:Metabase と H2 データベース
本題に入る前に、Metabase の構成と今回問題となったデータベースについて簡単に触れておきます。
Metabase とは
過去の記事で紹介しているので抜粋します。
Metabase社がOSSとして開発している、さまざまなデータソースに対応したデータ可視化ツール(Business Intelligence tool:BIツール)です。
サービスで利用しているDBの接続文字列等を Metabase に登録することで、Metabase 経由でDBクエリの実行や、データのグラフ化、ダッシュボード化などが行うことができます。
H2 データベースとの関係
Metabase 上で作成したユーザー情報や設定情報、「質問(クエリ)」や「ダッシュボード」などのアプリケーションデータを保存するためのデータベースです。
デフォルトで H2 データベース ( ファイルベースの組み込み DB ) を使用します。
ただし、Metabase 公式ドキュメントでは、H2 はあくまで試用や開発用とされており、信頼性の観点から本番環境では速やかに PostgreSQL や MySQL などの外部データベースへ移行することが強く推奨されています。
今回は、この「デフォルトの H2 データベース」を長く運用してしまった結果、データ破損に遭遇した事例となります。
環境
- Metabase: AWS EC2 上で jar ファイル実行
- Database: H2 ( デフォルト ) → PostgreSQL ( RDS ) へ移行
- Version: v0.52.2
1. 発生していた事象と事前の復旧措置
ある日突然、Metabase が起動しなくなるクラッシュが発生。再起動でも解消しなかったため、H2 データベースの修復(ダンプファイルからの再生成)を実施しました。
具体的には、Metabase の jar に含まれる H2 ツールを使用し、以下のようなコマンドでデータベースファイルの再作成を行いました。
# H2 データベースの復旧コマンド ( RunScript )
# SQL ダンプ ( metabase.db.h2.sql ) から新しい DB ファイル ( metabase_recovered ) を作成
sudo java -cp metabase.jar org.h2.tools.RunScript \
-url "jdbc:h2:./metabase_recovered" \
-user sa \
-script metabase.db.h2.sql
これにより Metabase 自体は起動するようになりましたが、動作が不安定でした。
そのため、恒久対応として RDS への移行を実施することにしました。
2. 移行作業とエラー発生
RDS への移行は、Metabase 標準の load-from-h2 コマンドを使用します。
実行した手順
- Metabase サービスを停止。
- 移行先の PostgreSQL 接続情報を環境変数にセットしてコマンド実行。
# 環境変数の設定
export MB_DB_TYPE=postgres
export MB_DB_DBNAME=metabase
export MB_DB_PORT=5432
export MB_DB_USER=<db_user>
export MB_DB_PASS=<db_password>
export MB_DB_HOST=<rds_endpoint>
# 移行コマンドの実行
java -jar metabase.jar load-from-h2 metabase.db
発生したエラー
処理の途中で以下の例外が発生し、移行プロセスが中断 ( ロールバック ) されました。
ERROR: insert or update on table "metabase_fieldvalues" violates foreign key constraint "fk_fieldvalues_ref_field_id"
Detail: Key (field_id)=(1) is not present in table "metabase_field".
3. 原因調査 ( H2 Shell による内部確認 )
エラー内容は「metabase_fieldvalues テーブルが参照している field_id=1 が、親テーブル metabase_field に存在しない」という 外部キー制約違反 です。
実際にデータの状態を確認するため、Metabase に同梱されている H2 Shell を使用して直接データを参照しました。
3-1. H2 Shell への接続
# H2 Shell の起動
# -url: データベースファイルのパス ( 拡張子.mv.dbは含めない )
# -user: デフォルトは sa
# -password: デフォルトは空文字 ( 設定している場合は指定 )
java -cp metabase.jar org.h2.tools.Shell -url "jdbc:h2:./metabase.db" -user sa -password ""
3-2. 不整合データの確認
Shell に入った後、エラーログに出ていた field_id=1 の状態を確認しました。
① 親テーブル ( metabase_field ) の確認
まず、本来あるはずのカラム定義データを確認します。
sql> SELECT * FROM metabase_field WHERE id = 1;
(No rows selected)
結果は0件。親データが存在しない ことが確定しました。
② 子テーブル ( metabase_fieldvalues ) の確認
次に、エラーの原因となっている参照元データを確認します。
sql> SELECT count(*) FROM metabase_fieldvalues WHERE field_id = 1;
COUNT(*)
5
親定義が存在しないにも関わらず、それを参照する子データが存在する ことが確認できました。
さらに、他にも同様の「孤児レコード」がないか確認するため、以下のクエリを実行しました。
sql> SELECT count(distinct field_id) FROM metabase_fieldvalues
WHERE field_id NOT IN (SELECT id FROM metabase_field);
COUNT(DISTINCT FIELD_ID)
211
調査の結果、id=1 だけでなく、合計で 211 種類 ものフィールド定義が欠損したまま孤児レコードだけが残っている、深刻な状態であることが判明しました。
3-3. 結論
以前の破損時、または修復プロセスの過程で、「テーブル定義データ ( 親 ) はロストしたが、キャッシュ的な値データ ( 子 ) だけが生き残った」状態になっていました。
H2 ( Metabase のデフォルト設定 ) ではこの不整合が許容されていましたが、PostgreSQL は参照整合性を厳格にチェックするため、このデータをインポートできずエラーとなっていました。
4. 対応の検討と決定
案 A:孤児レコードを削除して再トライ
H2 Shell から DELETE 文を発行し、整合性が取れていないデータを削除してから移行する案です。
-- 例: 親が存在しない子レコードを削除
DELETE FROM metabase_fieldvalues WHERE field_id NOT IN (SELECT id FROM metabase_field);
- 判定 : 却下
- 移行エラーは回避できるかもしれないが、
id=1(おそらく初期に作成された重要なカラム定義)自体が消えている事実は変わらない。 - 定義が消えた状態で、それを利用していた Metabase 上の「質問( Question )」やダッシュボードが正しく動く保証がない。
案 B:新規構築(採用)
不整合なDBを引き継ぐリスクを避け、環境をリフレッシュする案です。
[ 作業の流れ ]
- 新規構築 : EC2 インスタンスを作り直し、RDS ( PostgreSQL ) を接続して Metabase をクリーンインストール。
- データ移行 : 移行ツールは使わず、必要なダッシュボードやSQLクエリを手動で移植 ( または SQL ファイルから復元 ) 。
- 切り替え : ユーザーへ新環境を案内。
- 判定 : 採用
- 潜在的なデータの不整合リスクを完全に排除し、クリーンな環境で再出発できる。
- 手動移行の工数はかかるが、将来的なトラブル対応コストや運用リスクと比較すると、作り直す方が安全かつ確実であると判断した。
5. まとめ
Metabase の load-from-h2 コマンドで外部キー制約エラーが出る場合、移行元の H2 データベースですでに論理的なデータ欠損 ( 親レコードの消失 ) が起きている可能性が高いです。
その場合、無理にデータを修復して移行するよりも、「諦めて作り直す」方が、将来的な運用リスクを排除できる最善手となることがあります。
H2 Shell を使えば内部データの不整合を特定できるため、同様のエラーに遭遇した際はまず SELECT ... WHERE id = ... 等で、エラーに出ている id の生存確認をすることをお勧めします。
また、本番運用において H2 データベースはあくまで試用・小規模向けであり、早めに RDS 等のマネージド DB へ移行することを強く推奨します。
私たち BABY JOB は、子育てを取り巻く社会のあり方を変え、「すべての人が子育てを楽しいと思える社会」の実現を目指すスタートアップ企業です。圧倒的なぬくもりと当事者意識をもって、子どもと向き合う時間、そして心のゆとりが生まれるサービスを創出します。baby-job.co.jp/
Discussion