WordPress・CMS設計

WP-CLIカスタムコマンドによる1万件のデータ移行:タイムアウトを防いだ実装記録

WordPressで1万件規模のデータを移行するとき、最初に問題になるのは処理内容よりも実行経路だ。管理画面のボタンや独自のPOSTエンドポイントからPHPを動かす構成は、少量のデータなら簡単に動く。しかし、投稿の更新、カスタムフィールドの再計算、関連データの変換を1件ずつ実行すると、Webリクエストの制限が先に来る。…

WP-CLIカスタムコマンドによる1万件のデータ移行:タイムアウトを防いだ実装記録

ブラウザの画面が固まり、最終的にゲートウェイエラーが返る。PHPのログには、実行時間の上限に達したことを示すエラーが残る。厄介なのは、画面上で失敗したように見えても、すでに一部の書き込みが完了している場合があることだ。再実行すれば二重変換が起きる可能性があり、単純に最初からやり直すわけにはいかない。

この問題を避けるため、移行処理をブラウザから切り離し、WP-CLIのカスタムコマンドとして実行する構成に変更した。さらに、全件を一度に読み込まず、一定件数ごとのチャンクに分割する。これでWebリクエストの時間制限と、長時間処理に伴うメモリ使用量の増加を別々に扱えるようになる。

WP-CLIへ移せば時間の制約は軽くなる。だが、メモリと再実行の問題まで自動的に消えるわけではない。

ブラウザ経由の移行が抱えるタイムアウトとメモリ制限

Web経由の処理には、複数の制限が重なっている。PHPのmax_execution_time、PHP-FPMの処理待ち時間、nginxやApacheなどWebサーバー側のタイムアウト、さらにロードバランサーやCDNの制限だ。

どの設定が先に効くかは環境によって異なる。PHPの実行時間を延ばしても、Webサーバーが先に接続を切れば処理結果をブラウザへ返せない。逆にWebサーバー側の待ち時間を延ばしても、PHPのメモリ制限やプロセス管理の制約は残る。

1万件の移行で問題になるのは、1件の処理が極端に重い場合だけではない。1件あたりの処理が軽くても、それを連続して実行すれば合計時間は伸びる。投稿の取得、メタ情報の読み込み、値の変換、update_post_meta()などの書き込み、関連キャッシュの更新が重なると、処理時間は対象件数に応じて増えていく。

さらに、ブラウザ経由では接続の維持そのものが不安定だ。ユーザーがタブを閉じる、VPNやネットワークが一時的に切れる、プロキシが応答をタイムアウトさせるといった状況が起こりうる。PHPプロセスがその時点で停止するかどうかは、WebサーバーやPHP-FPMの設定に左右される。つまり、画面が切れたことと処理が確実に停止したことは同じではない。

この状態で最も避けたいのは、処理の進捗を判断できないことだ。何件まで完了したのか、最後に書き込まれた対象はどれか、失敗したデータがあるのかが分からなければ、復旧の方針を立てられない。

Webリクエストに処理を詰め込まない

管理画面から移行を開始する設計自体が悪いわけではない。少量のデータであれば、ユーザーに実行結果を返せるため扱いやすい。しかし、処理時間が読みにくいバッチを1リクエストに詰め込むと、次の問題が同時に発生する。

  • Webサーバーのタイムアウトに達する
  • PHP-FPMのワーカーを長時間占有する
  • 途中まで書き込まれた状態で再実行が必要になる
  • ブラウザ側では失敗理由や進捗を確認できない
  • 大量のオブジェクトやクエリ結果がプロセス内に残る

移行処理をCLIへ移す判断は、単に「ブラウザでは時間が足りないから」という理由だけで行うものではない。実行状態を端末やログで確認し、終了コードを取得し、必要なら同じコマンドを再実行できるようにするための設計変更でもある。

WP-CLIに移したときに解消される制約、残る制約

WP-CLIはWebリクエストとは別のPHPプロセスとして動く。そのため、ブラウザの接続維持やWebサーバーの応答待ち時間は、通常の実行経路には入らない。ターミナルからコマンドを開始した後、ブラウザのタブを開いておく必要もない。

これは大きな違いだが、CLIだから無制限になるわけではない。CLIでもmemory_limitは基本的に有効であり、OSやサービスマネージャーによるプロセス制限が設定されている場合もある。Dockerコンテナ内で実行するなら、PHPの設定だけでなくコンテナのメモリ上限も確認が必要だ。

WordPressのロード処理も省略されない。WP-CLIはWordPress本体を読み込み、通常は有効化されているプラグインやテーマのコードも読み込む。そこにフック、オートローダー、外部ライブラリ、オブジェクトキャッシュが加わる。長時間同じプロセスを動かす以上、1回のクエリが解放されていても、別の場所に状態が蓄積することがある。

CLI移行後に見るべき制約は、次のように分けると整理しやすい。

制約ブラウザ経由WP-CLI
Webサーバーの応答待ち時間影響する通常は影響しない
PHPのメモリ制限影響する影響する
PHP-FPMワーカー占有する通常は占有しない
標準出力の扱い画面表示が中心端末、リダイレクト、ログへ出力
再実行状態確認が難しい引数や終了コードで管理しやすい
プラグインやテーマの影響受ける通常は受ける

CLIの利点は、時間制限がなくなることではなく、時間の長い処理をWebの応答モデルから切り離せることにある。実行中のメモリ使用量や処理件数を出力できるため、問題がどこにあるかを観測しやすくなる。

WP-CLIカスタムコマンドとwp eval-fileの使い分け

WP-CLIで独自の移行処理を実行する方法として、よく使うのがカスタムコマンドとwp eval-fileだ。

カスタムコマンドでは、PHPクラスを定義し、WP_CLI::add_command()でコマンドを登録する。WordPressプラグインやmu-pluginsに組み込めば、対象サイトの環境でいつでも同じコマンドを呼び出せる。コマンドの引数やオプションは、コマンド登録時の$args$assoc_argsとして受け取る設計にする。

たとえば、移行対象の投稿タイプを位置引数で受け取り、バッチサイズやドライランを連想配列のオプションで受け取る構成が考えられる。コマンドの概要や引数の形式は、WP_CLI::add_command()の第三引数に渡す$argsで定義できる。ここでsynopsisを設定しておけば、wp helpから使い方を確認できる。

注意したいのは、WP-CLIに存在しない汎用的な取得メソッドを想定しないことだ。カスタムコマンドの引数は、コールバックの引数として渡される。フラグの値は$assoc_args['batch']のように取得し、未指定の場合は自分でデフォルト値を設定する。文字列で渡される値を整数へ変換し、許容範囲を検証する処理も必要になる。

一方、wp eval-file path/to/script.phpは、PHPファイルをそのまま実行する方法だ。コマンドを登録するためのクラスや、常設のプラグイン構成を用意しなくてもよい。短期間の検証や一度だけ行う移行では、こちらのほうが早く試せる。

eval-fileでもWordPressのロード後に実行されるため、get_posts()get_post_meta()update_post_meta()などのWordPress関数を利用できる。ただし、引数処理、ヘルプ、ログの形式、再実行のルールはスクリプト側で設計しなければならない。処理が長く使われる見込みなら、最初からカスタムコマンドとして作ったほうが保守しやすい。

比較軸カスタムコマンドeval-file
実行方法登録したコマンドを呼び出すPHPファイルを直接指定する
引数処理$args$assoc_argsで受け取るスクリプト側で処理する
ヘルプwp helpに組み込める自分で説明を用意する
再利用性高い一時利用向き
テストクラスやメソッド単位で分離しやすいスクリプト単位になりやすい
導入の速さ登録や配置が必要ファイルを置けば試せる
運用向き定期処理や本番運用に向く検証、調査、単発処理に向く

1万件規模の移行では、対象投稿タイプ、対象ID、バッチサイズ、ドライラン、再実行方法を明示できることが重要だ。したがって、常設する処理はカスタムコマンド、移行ロジックの試作や局所的な確認はeval-fileという分け方が現実的になる。

コマンド引数は実行時に検証する

バッチサイズをコマンドラインから変更できるようにすると、環境に応じた調整がしやすい。ただし、指定値をそのままposts_per_pageへ渡すのは危険だ。

まず文字列を整数に変換する。次に、1未満の値を拒否し、必要なら上限も設ける。上限はサイトや処理内容によって違うため、固定の正解があるわけではない。重要なのは、試験時に大きな値を指定しても、意図せず全件取得へ切り替わらないことだ。

--dry-runも単なる表示用フラグではない。書き込み関数を呼ばず、対象件数、対象ID、変換後の値の一部だけを確認するモードとして実装する。ドライランで本番と同じ検索条件を通すことで、対象範囲の間違いを早い段階で発見できる。

1万件を処理するチャンク分割とメモリ管理

全件を一度に取得し、配列へ詰めてから処理する構成は避ける。投稿オブジェクト、メタ情報、関連データを保持したままにすると、対象件数に比例してメモリが増えるからだ。

基本は、一定数だけ取得して処理し、そのチャンクを捨ててから次へ進む。WP_Queryを使う場合は、posts_per_pageとページ番号を明示する。取得結果を使い終わったら、wp_reset_postdata()でグローバルな投稿データを戻す。必要に応じて、クエリオブジェクトや一時配列への参照も解放する。

ただし、チャンクサイズに一般的な正解はない。投稿の本文を扱うのか、複数のメタフィールドを扱うのか、外部ライブラリを呼ぶのか、プラグインがどの程度のフックを追加しているのかでメモリ消費は変わる。

少ない件数から始め、実行中のメモリを観測しながら増減させるのが安全だ。最初から大きなチャンクを選び、途中でFatal errorが出てから戻すより、検証環境で小さな値から段階的に試したほうが、失敗時の切り分けもしやすい。

paged方式とIDの固定

pagedを増やしながらページを移動する方式は、実装が分かりやすい。一方で、処理中に対象データが追加・削除される環境では注意が必要だ。検索結果の並び順が変わると、同じ投稿を二度処理したり、別の投稿を飛ばしたりする可能性がある。

移行中に対象データが変更されないと保証できるなら、安定した並び順を指定したページングで十分な場合もある。対象IDを先に確定できるなら、IDの一覧をスナップショットとして保存し、その一覧を一定件数ごとに分ける方法がより明確だ。

ただし、1万件分のIDをPHPの配列へ保持すること自体が問題になる環境もある。IDだけなら投稿オブジェクトより軽いが、常に無条件で全件をメモリへ載せるべきではない。IDを一時ファイルや専用の状態へ保存する方法、あるいは最後に処理したIDを記録して次回の検索条件に使う方法も候補になる。

重要なのは、ページ番号だけを進捗の根拠にしないことだ。処理済みの対象を再実行時に判定できるよう、データの状態や一意な識別子を組み合わせておく。

1万件を一度に書き込むのではなく、失敗しても再開できる単位へ分ける。

キャッシュクリアは定期実行ではなく検証して決める

長時間のCLI処理では、WordPressのキャッシュがメモリや処理時間へ影響することがある。しかし、キャッシュを頻繁に消せば必ず安定するわけではない。

wp_cache_flush()は、永続オブジェクトキャッシュを利用している環境ではサイト全体のキャッシュへ影響する可能性がある。移行中に無条件で呼ぶと、同時に動いている通常のリクエストや別のバッチに負荷を与えることもある。キャッシュの種類や実装によって挙動が異なるため、対象サイトでの確認なしに定期実行へ組み込むのは避けたい。

まずは、処理前後のメモリ使用量とピーク値を記録する。チャンクを進めるたびにメモリが増え続けるのか、特定の処理だけで一時的に跳ねるのかを確認する。そのうえで、必要な場合だけキャッシュの破棄を検討する。

投稿単位の処理であれば、clean_post_cache()など、対象を限定したキャッシュ更新で足りることもある。これも処理内容や利用しているキャッシュ実装によって適切な方法が変わる。全体キャッシュを消す設定を経験則だけで決めるのではなく、キャッシュを消す前後でメモリ、実行時間、データベース負荷を比較するべきだ。

WP-CLI実行時に発生するFatal errorの回避とデバッグ

CLIへ移行した後に、次に遭遇しやすいのがメモリ不足だ。表示されるエラーは、PHPが許可されたメモリを使い切ったことを示すものになる。原因はチャンクサイズとは限らない。プラグインのフック、外部ライブラリ、特定データの異常な大きさ、解放されない参照など、複数の要因が考えられる。

まず実行条件を小さくする

最初の切り分けでは、対象件数とチャンクサイズを小さくする。対象IDを指定できるようにしておけば、1件だけを処理するテストも可能だ。

1件だけでメモリが大きく増えるなら、データそのものか、1件の処理で呼び出しているライブラリを疑う。1件では問題がなく、チャンクサイズを増やすと急激に増えるなら、取得件数や一時配列の持ち方が原因かもしれない。チャンクを繰り返すほど少しずつ増えるなら、ループ外に残っている参照や、フックが保持する状態を調べる。

メモリは、処理開始時、各チャンクの開始時と終了時、処理終了時に記録すると比較しやすい。memory_get_usage(true)は現在確保されているメモリの目安、memory_get_peak_usage(true)は実行中のピークを確認するために使える。ログへ残すと、どのチャンクで値が変化したかを後から追跡できる。

数値は絶対的な安全基準ではない。同じメモリ使用量でも、サーバーのmemory_limit、同時実行プロセス、データベースの負荷によって余裕は変わる。計測値は「この環境で、この処理条件ならどうだったか」を判断する材料として扱う。

プラグインとテーマの影響を分離する

WP-CLIには、プラグインやテーマを読み込まずにコマンドを実行するためのグローバルオプションがある。--skip-plugins--skip-themesを使うと、移行処理へ影響するコードを一時的に除外できる。

ただし、これらを付ければ常に正しい結果になるわけではない。移行対象のカスタム投稿タイプやメタフィールドがプラグインによって登録されている場合、そのプラグインを除外すると、必要な処理やデータ定義まで失われることがある。

使い方は、原因の切り分けとして考えるべきだ。通常の環境でメモリが増え、特定のプラグインを除外すると増え方が変わるなら、そのプラグインのフックや内部キャッシュを確認する。最終的な本番実行では、除外した状態がデータの整合性を保てるかを確認してから判断する。

Fatal errorの場所を絞り込む

デバッグ時には、処理をまとめて実行しない。次のように段階を分けると原因を追いやすい。

1. 対象IDの取得だけを行い、検索条件が正しいか確認する

2. 変換前のデータを読み込み、特定のレコードで異常がないか調べる

3. 書き込みを無効にしたドライランで、変換ロジックだけを動かす

4. 1件の書き込みを含めて実行する

5. 小さなチャンクで複数件を処理する

6. 実際のバッチサイズへ近づける

この順番なら、クエリ、変換処理、書き込み、ループのどこで問題が起きたかを分離しやすい。

エラーが出た対象IDは必ず記録する。対象IDが分かれば、そのレコードだけを再現条件にできる。逆に、エラーの位置を記録せずに処理を続けると、最後に大量の失敗データを調べることになる。

ログ出力と進捗管理でバッチを観測可能にする

長時間のコマンドで、画面に何も表示されない時間を作らない。処理が止まっているのか、単に1件の変換に時間がかかっているのかが分からないからだ。

ログは、少なくとも次の情報を含めると復旧に使いやすい。

  • コマンドの開始時刻と実行条件
  • 対象件数または対象範囲
  • 現在のチャンク番号
  • そのチャンクで取得した件数
  • 成功件数と失敗件数
  • 最後に処理した対象ID
  • メモリ使用量とピーク値
  • エラーが起きた対象IDと理由

通常の情報はWP_CLI::log()で出力できる。警告にはWP_CLI::warning()、処理を継続できないエラーにはWP_CLI::error()を使う。WP_CLI::error()はメッセージを出力したうえでコマンドを失敗終了させるため、1件の不正データで全体を止めるかどうかをあらかじめ決めておく必要がある。

1件の失敗で全体を止めない場合は、対象IDと例外の内容をエラー用の出力へ記録し、次の対象へ進む構成にする。ただし、最後に失敗件数がゼロでなければ、コマンド自体を成功扱いにしない設計も考えられる。終了コードだけで運用結果を判断する場合、部分的な失敗を見逃さないことが重要だ。

ログを人間が読むだけなら、読みやすい文章でもよい。一方、後から集計したり監視へ渡したりするなら、項目の並びと形式を固定する。JSON LinesやTSVのような形式を選ぶ場合でも、最初から過剰に複雑なスキーマを作る必要はない。チャンク番号、対象ID、結果、エラー内容といった復旧に必要な項目から始めればよい。

進捗バーの正しい使い方

WP-CLIの進捗表示には、WP_CLI\Utils\make_progress_bar()を使える。処理対象の総数が分かっている場合は、開始時に総数を渡し、1件処理するごと、または一定のまとまりを処理するごとに返り値のバーへtick()を送る。

進捗バーを出すこと自体が目的になると、別の問題が出る。1万件を1件ずつ表示すれば、端末への出力が増え、ログへリダイレクトした場合にも扱いにくい。処理単位や利用環境に応じて、チャンク単位で更新する方法もある。

ただし、チャンク単位の進捗表示では、1チャンクの処理中に長い空白が生じることがある。そこで、チャンク開始時と終了時に通常ログを出し、進捗バーは全体の目安として使う構成が扱いやすい。詳細が必要なときは、WP-CLIの--debugオプションとWP_CLI::debug()を組み合わせる。

進捗バーの表示だけでは、再開位置を保存できない。コマンドが途中で終了した場合にどこからやり直すかは、データの状態や最後に処理したIDを別途設計する必要がある。

冪等性を先に決める

移行処理は、途中で停止する前提で作る。サーバーの再起動、手動停止、データベースの一時的なエラー、予期しないFatal errorは避けられない。重要なのは、停止しないことではなく、停止後に安全に再実行できることだ。

そのためには、同じ対象を二度処理しても結果が壊れない、または処理済みかどうかを明確に判定できるようにする。

たとえば、移行後のメタフィールドが必ず特定の形式になるなら、その値を処理済み判定に使える。処理前の値と処理後の値が区別できるなら、別の管理テーブルを用意せずに再実行の判断ができる場合がある。

ただし、移行後の値が空になる処理や、元の値と移行後の値が同じになる処理では、この方法は使えない。状態だけでは判定できない場合、一意な対象IDと処理内容を記録する仕組みが必要になる。

ここで、管理用の別テーブルを作れば解決するとは限らない。対象テーブルへの書き込みと管理テーブルへの書き込みが別々に成功すると、片方だけ更新された状態が残る。トランザクションを使える処理なら、どこまでを同じ単位で確定するかを検討する。ただし、WordPressの各種APIやフックがすべてトランザクションと相性がよいとは限らないため、トランザクションを追加すれば安全になると単純化しないほうがよい。

実装では、次の点を確認しておく。

  • 移行済みデータを再び変換しない
  • 途中で停止しても、最後に処理した対象を特定できる
  • 失敗した対象だけを再実行できる
  • ドライランで対象範囲を確認できる
  • 書き込みの順番によって不整合が起きない
  • 実行条件をログから復元できる

処理済み判定は、移行ロジックの最後に付け足すものではない。検索条件、書き込み処理、ログ設計と同時に決めるべき要件だ。

バッチの完成度は、最後まで動いたときより、途中で止まった後に分かる。

実行前に確認する環境差分

ブラウザ経由とCLI経由では、実行される条件が同じとは限らない。is_admin()DOING_AJAXなどの条件分岐を使うプラグインがあると、CLIではWebと異なるコードパスへ入る可能性がある。

pre_get_postsなどのフックがCLIでどのクエリにも影響する場合もある。移行処理で使うクエリは、管理画面の表示用クエリとは目的が違う。対象投稿タイプ、ステータス、並び順、ページ番号を明示し、暗黙の条件に依存しないようにする。

また、WP_Queryで投稿を取得する場合は、不要なデータを読み込まない設定も検討する。投稿本文やサムネイル、関連情報を使わない処理なら、必要なフィールドだけを扱う方法がある。投稿オブジェクトを取得する必要があるなら、取得後に何を保持しているかを確認する。

データベース側では、検索条件にインデックスが使われているかを確認する。1万件という件数だけでなく、メタテーブルを条件にした検索、複数のメタ条件、ソート順の指定によっては、取得処理そのものがボトルネックになる。PHPのチャンクサイズを調整しても、毎回の検索が重ければ全体時間は短くならない。

検証は小さなデータから始める

本番データをいきなり全件処理しない。まず開発環境やステージング環境で、代表的なデータを含む小さな範囲を処理する。

単純な投稿だけでなく、次のようなデータを混ぜて確認する。

  • 対象フィールドが空の投稿
  • すでに移行済みの投稿
  • 想定外の形式が入った投稿
  • 関連データが欠落している投稿
  • 特にサイズの大きい本文やメタ値を持つ投稿
  • 権限やステータスが通常と異なる投稿

ドライランでは、対象件数と対象IDの範囲が想定どおりかを見る。次に少数件へ書き込みを行い、移行後の値だけでなく、表示側の処理や関連プラグインへの影響も確認する。

本番に近い環境で測るときも、チャンクサイズを複数試す。小さい値はメモリに余裕を持たせやすいが、クエリやループの回数が増える。大きい値は1回あたりの効率がよく見える一方、1チャンクの失敗時に再処理する範囲が広がる。処理時間だけでなく、ピークメモリ、データベース負荷、失敗時の復旧しやすさを合わせて判断する。

特定のチャンクだけ処理時間が長い場合は、その範囲に異常なデータが含まれているかもしれない。チャンク番号と最後に処理したIDを記録しておけば、対象を絞って調査できる。

残るトレードオフと運用の形

WP-CLIのカスタムコマンドへ移行し、チャンク単位で処理すれば、Webリクエストのタイムアウトを避けやすくなる。ただし、処理時間そのものが短くなるとは限らない。タイムアウトを回避することと、高速化することは別の課題だ。

SQLを使った一括更新、一時テーブルを使った変換、差分だけを対象にする検索、不要なフックを実行しない専用処理などは、高速化の候補になる。しかし、WordPressのAPIを経由しない処理では、キャッシュやフック、データ整合性への影響が大きくなる。速度だけを見て切り替えるのではなく、どの機能を意図的に省略するのかを把握しておく必要がある。

メモリについても、チャンクサイズを上げればよいわけではない。実行環境のmemory_limitが違えば、同じ設定でも結果は変わる。Dockerで実行する場合はコンテナの上限、CIで実行する場合はジョブ全体のリソース制限も確認する。

定期処理へ発展させる場合は、WP-Cronやアクションスケジューラなど別の実行基盤も候補になる。ただし、CLIで作ったログや進捗表示をそのままWeb画面のように扱うことはできない。ファイルへの出力、実行状態の保存、重複起動の防止、失敗時の通知を別に設計する必要がある。

単発の移行では、まずWP-CLIコマンドとして安全に完走できる形を作る。そのうえで、同じ処理を定期的に行う必要が出てきた段階で、キューやスケジューラーへ分離する。この順番のほうが、最初から大きな仕組みを作るより失敗箇所を限定しやすい。

1万件規模の移行で押さえるべきなのは、特別な高速化テクニックではない。ブラウザ経由の処理をCLIへ移し、取得と更新を小さな単位へ分け、実際のメモリ使用量を計測する。そして、途中で止まっても同じデータを壊さずに再実行できるようにする。

WP_CLI::add_command()で運用可能な入口を作り、引数はコールバックへ渡される$args$assoc_argsで検証する。進捗表示にはWP_CLI\Utils\make_progress_bar()を使い、ログにはWP_CLI::log()、警告にはWP_CLI::warning()、継続できない失敗にはWP_CLI::error()を使い分ける。こうした小さな正確さが、長時間バッチの安定性を支える。

タイムアウトを消すだけなら、CLIへ移すだけでも効果はある。しかし、本番で安心して使える移行処理にするには、時間、メモリ、ログ、進捗、冪等性を一つの運用設計として扱う必要がある。そこまで作り込んで初めて、1万件のデータを「処理できる」から「壊さずに何度でも処理できる」へ変えられる。

Related reading: WP-CLIによる1万記事の移行で直面したメモリリークと解決の記録 and WordPressのSQLite本番運用は現実的か?表示速度と保守性から見た検証.

よくある質問

WP-CLIに移行すればデータ移行のタイムアウトを防げますか?
WP-CLIではブラウザの接続維持やWebサーバーの応答待ち時間が通常の実行経路に入らないため、Webリクエスト由来のタイムアウトは避けやすくなります。ただし、PHPのメモリ制限やOS、コンテナなどによるプロセス制限は残ります。
WP-CLIのデータ移行でバッチサイズはどう決めればよいですか?
チャンクサイズに一般的な正解はないため、少ない件数から始め、実行中のメモリ使用量やピーク値を観測しながら調整します。処理時間だけでなく、データベース負荷や失敗時の再処理範囲も考慮します。
WP-CLIのカスタムコマンドとeval-fileはどう使い分けますか?
カスタムコマンドは再利用性が高く、定期処理や本番運用に向いています。eval-fileは導入が速いため、短期間の検証や一度だけ行う移行、局所的な確認に適しています。
データ移行を安全に再実行するにはどうすればよいですか?
同じ対象を二度処理しても結果が壊れないようにするか、移行済みかどうかを判定できる仕組みを用意します。最後に処理した対象IDや失敗した対象を記録し、失敗対象だけを再実行できるようにします。
WP-CLI実行中にFatal errorが出た場合、どう切り分けますか?
対象件数とチャンクサイズを小さくし、対象IDの取得、データ変換、1件の書き込み、複数件の処理という順に段階を分けて確認します。処理開始時や各チャンクの前後にメモリ使用量を記録し、プラグインやテーマを一時的に除外して影響を比較する方法もあります。

参考情報