Heroku Postgresで誤って特定のテーブルデータを削除してしまった、なんてことはありませんか?データベース全体を巻き戻すのではなく、特定のテーブルだけを復旧したいケースは少なくありません。
Herokuには heroku pg:backups:restore という便利なコマンドがありますが、これは復旧前にデータベース全体を一度クリアしてしまうため、テーブル単位の復旧には使えません。
この記事では、pg_restore コマンドを使い、Heroku Postgresで特定のテーブルのみを安全に復旧する手順を、テストから本番作業まで詳しく解説します。
目次
- 必要なバックアップの準備
- ローカル環境での復旧リハーサル
- Heroku環境での最終テスト
- 本番環境での復旧作業
- まとめ
1. 必要なバックアップの準備
まず、復旧作業に必要となる2種類のバックアップファイルを用意します。
① データ破損後のバックアップ(リストア対象環境の再現用)
作業のテストに使うため、現在の(データが破損した状態の)データベースのバックアップを新たに取得します。
# 本番アプリの新規バックアップを作成
heroku pg:backups:capture -a your_production_app
# 作成したバックアップをダウンロード
heroku pg:backups:download -a your_production_app
# 分かりやすい名前に変更
mv latest.dump corrupted_db_for_test.dump
② 正常だった頃のバックアップ(復元データ本体)
次に、復元したいデータが含まれている、破損前の正常なバックアップを探してダウンロードします。
# バックアップ一覧を確認し、破損前のバックアップID(例: b005)を特定
heroku pg:backups -a your_production_app
# 特定した正常なバックアップをダウンロード
heroku pg:backups:download b005 -a your_production_app
# 分かりやすい名前に変更
mv latest.dump restore_source.dump
2. ローカル環境での復旧リハーサル
本番環境でいきなり作業するのは危険です。まずはご自身のPCなど、失敗しても問題ないローカル環境でリハーサルを行いましょう。
ローカルDBを破損状態に復元
まず、先ほど取得した「①データ破損後のバックアップ」を使い、ローカルのデータベースを本番と同じ破損状態にします。
# ローカルDBに破損状態をリストア
# --clean: リストア前にDBオブジェクトを削除します
# --no-owner: 所有権エラーを回避します
pg_restore -d your_local_dbname --host=localhost -U your_username --clean --no-owner corrupted_db_for_test.dump
特定テーブルを正常なデータで復元
次に、「②正常だった頃のバックアップ」から、目的のテーブルだけをリストアします。
--data-only オプションの使い分けが重要です。
- テーブルの構造は存在し、データのみ復元する場合 (TRUNCATEした場合など) :
--data-onlyを付けます。 - テーブル構造ごと存在しない場合 (DROP TABLEした場合など) :
--data-onlyは付けません。
# 正常なバックアップから特定のテーブル(your_table_name)のデータのみを復元
pg_restore -v -d your_local_dbname --host=localhost -U your_username -t your_table_name --data-only restore_source.dump
リストアが成功したら、psql などでDBに接続し、データが正しく復旧したかを確認します。この時のレコード数などをメモしておくと、後の工程で役立ちます。
# SQL
-- テーブル定義の確認
\d your_table_name
-- レコード数の確認
SELECT COUNT(*) FROM your_table_name;
最後に、ローカルでアプリケーションを起動し、関連機能が正常に動作するかを必ず確認してください。
3. Heroku環境での最終テスト
ローカルでのリハーサルが成功したら、次は本番環境と構成が近いHeroku環境で最終テストを行います。このテストの目的は、「本番と全く同じ状況を再現し、その上でリストア手順が成功すること」を確認することです。そのために、以下のいずれかの方法で、データ破損時点の本番DBの完全なクローンを作成します。
方法1:ステージングDBを一時的に上書き
この方法は、Herokuのステージング環境を使ってテストを行います。
1. 現在のステージングDBのバックアップ
テスト後にステージング環境を元の状態に戻せるように、現在のステージングDBのバックアップを取得しておきます。
heroku pg:backups:capture -a your_staging_app
2. ステージングDBを「データ破損後の本番環境」と同一にする
最初に取得した「データ破損後の本番バックアップ (corrupted_db_for_test.dump)」のURLを取得し、pg:backups:restore コマンドでステージングDBにデータベース全体をリストアします。
# ① 破損後の本番バックアップのURLを取得
heroku pg:backups:url -a your_production_app # 最新のバックアップ(破損後)のIDを指定
# ② 取得したURLを使って、ステージングDBに全体リストアを実行
# 警告: このコマンドはステージングDBの全データを上書きします!
heroku pg:backups:restore '取得したバックアップのURL' DATABASE_URL -a your_staging_app --confirm your_staging_app
この手順により、ステージングDBは外部キーの関連も含めて、これから作業する本番環境と全く同じ状態になります。
3. 改めて、単一テーブルのリストアをテストする
本番と全く同じになったステージング環境に対して、リモート接続での単一テーブルリストアを実行します。
# 接続情報を取得
heroku pg:credentials:url DATABASE -a your_staging_app
# Heroku PostgresはSSL接続が必須
export PGSSLMODE="require"
# 「正常だった頃のバックアップ」から単一テーブルをリストア
pg_restore -v -d <dbname> --host=<host> -U <user> -t your_table_name --data-only restore_source.dump
このテストが成功すれば、本番環境でも同じ手順が成功する確証を得られます。
方法2:本番環境のDBをフォーク
もしステージング環境を上書きしたくない場合、Herokuのfork機能を使うのが最も安全で優れた方法です。
# 本番DBのクローン(フォーク)を一時的に作成
heroku addons:create heroku-postgresql:standard-0 --fork DATABASE_URL -a your_production_app
# 作成されたフォークDB(例: HEROKU_POSTGRESQL_ORANGE_URL)の接続情報を取得
heroku pg:credentials:url HEROKU_POSTGRESQL_ORANGE_URL -a your_production_app
# pg_restore をフォークDBに対して実行
pg_restore -v -d <dbname> --host=<host> -U <user> -t your_table_name --data-only restore_source.dump
# テスト完了後、フォークDBを破棄
heroku addons:destroy heroku-postgresql-orange -a your_production_app --confirm your_production_app
この方法なら、稼働中のどの環境にも一切影響を与えずに、本番と全く同じデータセットでリストアのリハーサルが可能です。
4. 本番環境での復旧作業
いよいよ本番環境で復旧作業を行います。慎重に進めましょう。
1. メンテナンスモードを有効化
万が一の事態に備え、ユーザーからのアクセスと、データの書き込みを停止します。
heroku maintenance:on -a your_production_app
heroku ps:scale web=0 worker=0 -a your_production_app
2. 作業直前のバックアップを取得
復旧作業に失敗した際に、この時点の状態にすぐ戻せるように、必ずバックアップを取得します。
heroku pg:backups:capture -a your_production_app
3.テーブルを復元
ステージング環境と同様に、本番DBの接続情報を取得し、pg_restore を実行します。
# 本番DBの接続情報を取得
heroku pg:credentials:url DATABASE -a your_production_app
# 接続情報を元に、本番環境へリストアを実行
export PGSSLMODE="require"
pg_restore -v -d <dbname> --host=<host> -U <user> -t your_table_name --data-only restore_source.dump
4. データの確認
heroku psql で本番DBに接続し、テーブルの定義やレコード数が、ローカルでのリハーサル時に確認した状態と一致するかを検証します。
heroku psql -a your_production_app
-- レコード数を確認
SELECT COUNT(*) FROM your_table_name;
5. メンテナンスモードを解除
データに問題がないことを確認できたら、dynoを元の数に戻し、メンテナンスモードを解除します。
# dyno数は元の構成に合わせてください
heroku ps:scale web=1 worker=1 -a your_production_app
heroku maintenance:off -a your_production_app
最後にアプリケーションの動作を実際に確認し、すべてが正常であれば復旧作業は完了です。
5. まとめ
pg_restore を使ってHeroku Postgresにリモート接続することで、データベース全体に影響を与えることなく、テーブル単位での柔軟なデータ復旧が可能です。
ただし、この方法も万能ではありません。正常なバックアップが古すぎると、失われるデータが多くなったり、スキーマの変更が原因でリストアに失敗したりするリスクがあります。
万が一の事態に備え、Heroku Schedulerアドオンなどを活用して定期的にバックアップを取得しておくことが、なによりも重要な対策と言えるでしょう。
