0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Laravel Migrationの--pretendは注意!本番データ移行を安全に行う方法

Posted at

本番データの移行は、一度実行すると取り返しがつかないことが多く、非常にリスクが高い作業です。
「php artisan migrate --pretend」で安全に確認できると思っていませんか?

実は --pretend はテーブル構造変更しか表示されず、データ移行系では役に立ちません。
この記事では Laravelで本番データ移行を安全に行うための実装パターン を紹介します。

Laravel Migration の --pretend の落とし穴

Laravelには php artisan migrate --pretend というオプションがあり、実際にSQLを実行せずに「どんなSQLが発行されるか」を確認できます。

php artisan migrate --pretend

ただし、この方法には大きな制限があります。

  • Schemaビルダーによるテーブル変更は表示される
  • クエリビルダやDBファサードで書かれた insert, update, delete は表示されない

例えば以下のような「データ移行系のMigration」ではSQLが出力されません👇

public function up()
{
    $users = DB::table('users')->where('status', 'inactive')->get();

    foreach ($users as $user) {
        DB::table('archived_users')->insert([
            'id' => $user->id,
            'name' => $user->name,
        ]);
    }
}

--pretend をつけても何も表示されないので、実際にどんなデータが更新されるか事前確認できない という問題があるわけです。

そこで必要なのが「Dry Run実装」

本番実行の前に「実際には更新せず、対象件数や対象データを確認できる」仕組みを入れておくことが重要です。
以下では Laravel を例に、Dry Run実装のパターンを共有します。

1. Dry Run の実装方法

(1) トランザクション + ロールバック

DB::beginTransaction();

try {
    $targets = User::where('status', 'inactive')->get();
    echo "対象件数: " . $targets->count() . PHP_EOL;
    echo "対象ID: " . implode(',', $targets->pluck('id')->toArray()) . PHP_EOL;

    // 実際の更新(Dry Runではコメントアウト)
    // User::where('status', 'inactive')->update(['status' => 'archived']);

    DB::rollBack(); // Dry Runなので反映しない
} catch (\Exception $e) {
    DB::rollBack();
    throw $e;
}

Dry Run では最後に必ず DB::rollBack() を呼び出して変更を反映しないようにします。
本番処理と混在させると、ロールバックを入れ忘れて本当に更新してしまう危険があるため、Dry Run 用のコードとして切り分けて書くのが安全です。

(2) DB::listen() でSQLログを出力

DB::listen() を使うと、実際に発行されるSQLをリアルタイムで確認できます。
Dry Runの確認用に使うと便利です。

DB::listen(function ($query) {
    // 発行されたSQLをそのまま表示
    echo $query->sql . PHP_EOL;
});

例えば、対象ユーザーを取得するクエリの場合はこんな出力がされます。

select * from `users` where `status` = 'inactive'

これにより、「このDry Runでどのレコードが対象になるのか」を事前に確認できます。
本番処理を走らせる前に、発行されるSQLをチェックして安全性を確かめましょう。

(3) 独自 --dry-run オプションを作る

Laravel の Artisan コマンドでは、自分でオプションを定義して切り替えることができます。
例えば --dry-run をつけたら「実際には更新せず、対象データだけ表示する」といった動作にできます。

public function handle()
{
    $dryRun = $this->option('dry-run');
    $targets = User::where('status', 'inactive');

    if ($dryRun) {
        $this->info('対象件数: ' . $targets->count());
        $this->info('対象ID: ' . implode(',', $targets->pluck('id')->toArray()));
    } else {
        $targets->update(['status' => 'archived']);
    }
}

ポイント

  • --dry-run オプションをつけると「確認モード」として動作する
php artisan user:archive --dry-run

➝ 対象件数や対象IDだけが表示される

  • オプションを外すと「本番モード」として実際の更新が走る
php artisan user:archive
  • 本番処理とDry Run処理を一つのコマンドにまとめつつ、安全に切り替えられる

👉 つまり、この方法を使えば「Dry Runコードと本番コードが同居しても、明示的に --dry-run をつけない限り更新されない」仕組みになるので安心です。

2. バックアップ・リストアの仕組み

mysqldump でバックアップ

mysqldump -u user -p database_name target_table > target_table_backup.sql

3. 本番環境での安全性を高める工夫

  • 件数チェック
$expected = 500;
$actual = User::where('status', 'inactive')->count();
if ($expected !== $actual) {
    throw new \Exception("件数不一致: expected=$expected, actual=$actual");
}
  • 分割処理(チャンク更新)
User::where('status', 'inactive')
    ->chunkById(1000, function ($users) {
        foreach ($users as $user) {
            $user->update(['status' => 'archived']);
        }
    });

まとめ:安全な本番移行フロー

✅ Dry Runを実行して対象件数・対象データを確認
✅ バックアップを取得
✅ 件数チェックで期待値と一致しているか検証
✅ 大量データはチャンク更新
✅ 実行前にチームレビュー

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?