WordPress 4.3でcronタスクが凄まじい量に肥大化、メモリを使いすぎる件の対応
こんにちは。はじめての寄稿になります。タロスカイで開発を行っている高橋です。
WordPress 4.3へのアップグレードに関する注意点です。弊社運営サイトのTENNIS.jp(マルチサイト)で、Internal Server Errorが頻発し、ほとんど機能しなくなるという問題が発生しました。
ソースコードはいじっていないので、当初はサーバを疑ったのですが、ログを見たり色々としているうちに、アプリケーション側の問題とわかりました。さくらインターネットさんには何度も問い合わせをしてしまい、申し訳なかったです。
バグの症状
さて、肝心の症状ですが、基本的には「サイトが重い」ということです。ロードアベレージも18ぐらいになって、まともにアクセスすらできません。memory_limitのエラーが出るときもあれば、そのままInternal Server Errorになることもあります。
Google Analyticsのリアルタイム解析を確認すると、見ている人は何人かいたので、サイト全体が完全に落ちている状態ではありませんでした。
解決までの道筋
ローカルの環境では再現しなかったため、データベース周りだろうと当たりをつけて色々調査しました。そもそも管理画面にアクセスできないのでWP CLIコマンドでプラグインをオン・オフしてみようかと wp help plugin と打ってみたところ、シリアライズされた配列がドワーっと表示されました。
なんじゃこりゃ! と驚いて、wp_optionsの cron テーブルを確認してみると、cronタスクが4MBぐらい登録されていました……
wp_optionsのcronはautoload=yes、つまり、毎回読み込まれるデータなので、すべてのアクセスにおいて4MBのデータを引き回していたことになります。このため、メモリの使用量が増大したわけですね。
そのcronデータが8/25から増えているので、社内を確認したところ、4.3へのアップデートをそれぐらいの日に行ったとのこと。タバコを吸いに行って戻ってくると、五十嵐くんが「WordPress 4.3 cron バグ」でのグーグリングを完了していました。
WordPress 4.3でメモリ消費量増大・CPU高負荷になる重大なバグあり。直し方を一応解説。 – enjoypclife.net
これで解決! いやー、焦りました。
バグの詳細
一応、バグの詳細です。WordPress 4.3へのアップグレードにおいて、以下のパッチが適用されました。おそらく、WordPressのタクソノミーの取り扱いが変更したことによるバッチ処理の登録ですね。
wp-includes/taxonomy.php
/**
* In order to avoid the wp_batch_split_terms() job being accidentally removed,
* check that it's still scheduled while we haven't finished splitting terms.
*
* @ignore
* @since 4.3.0
*/
function _wp_check_for_scheduled_split_terms() {
if ( ! get_option( 'finished_splitting_shared_terms' ) && ! wp_next_scheduled( 'wp_batch_split_terms' ) ) {
wp_schedule_single_event( 'wp_batch_split_terms', time() + MINUTE_IN_SECONDS );
}
}
上記はwp_schedule_single_event
の呼び出しにバグがあります。引数の順番が違うので、wp_next_scheduled
の確認がtrueにならず、延々とタスクが登録されていきます。
このフックは管理画面へのアクセスで毎回生成されるので、Ajax(WordPressのAjaxエンドポイントは管理画面扱い)でなにかしていたりすると、すごいことになります。
add_action( 'admin_init', '_wp_check_for_scheduled_split_terms' );
この結果、アクセス数に応じてすごい数のCronが登録されていました。で、上記で紹介したブログ記事の修正方法は
- 引数の順番を正しく戻す
- すでに登録してしまったゴミデータを消去するためのパッチをmu-pluginsに登録する
というものです。mu-pluginsのファイルは必要なくなったら消去してしまってもいいでしょう。このバグに対する修正はすでに行われているようですが、リリースは4.3.1まで待たないといけませんね。高トラフィックサイト、投稿者が多いサイト、更新が頻繁なサイトは4.3.1までアップデートを見送った方がよいと思われます。
このバグへの感想
なぜこのバグが混入したかというと、色々理由はあると思いますが以下の2つですかね。
- PHPは7以上じゃないと引数の型付けでintやstringなどを指定できない(もし指定できていれば、今回のミスは防げたはず)
- cronの登録それ自体は成功してしまうので、量がかなり多くならないと問題が顕在化しない
WordPressのコアはユニットテストを適用しているのですが、「データが極度に多い」というケースに対応するのはユニットテストだと難しいですね。データベースがからむと結合テストが難しいとはよく言いますが、まさにこの問題が該当するケースなのではないでしょうか。
ちなみに、私の個人ブログは4.3にしても特に問題なかった(ゴミデータは存在していましたが)ので、レアケースともいえるでしょう。
というわけで、WordPressご利用の皆さん、お互い気を付けましょう。