はじめに
株式会社オークファンの佐藤です。
Truly Ergonomic社が新しいキーボードのファンディングを始めたことに動揺している今日このごろです。
https://www.trulyergonomic.com/store/index.php
本記事では、私が半年前にハマった、PHP7環境対応のプロファイラ選びの話をお届けします。
前提
高いユーザビリティを保つためには、レスポンス速度は重要です。サイトが遅ければ遅いほど離脱率が上がるのは、もはや周知の事実です。
しかし、それを定量的に測定するのはなかなか大変なこと。そして、原因を追求するのも大変なことです。そこで、弊社のメインサービスである オークファン の開発環境にプロファイラを入れることにしました。
これにより、エンジニア側で(だいたい、プログラムを遅くするのはエンジニアの仕業です)問題を早期把握できるようにしています。
オークファンはPHP7 + Nginx環境で稼働しています。プロファイラもPHP7対応版を入れなければいけません。
今回はFacebookが作成したことで有名なXHProfの、PHP7非公式版を利用しました。
非公式とはいえ、手順通りにインストールして終わりかと思ったら……
502エラーが頻発
ここで大きな問題が発生しました。
プロファイラを入れた途端、リクエストが不定期に、しかし高頻度で502エラーを返すのです。
有料プロファイラとして有名なTidewaysのバージョンならもうちょっと信頼できるかと思ったら、今度は100%の確率で502エラーを返すようになりました。
ぐぐっても似たような現象は出てこないため、PHP7環境一般というより、オークファン固有の問題と考えざるを得ません。
そのあたりを想像しながら、ライブラリを入れ替えたり、コアダンプを取ったりして調査しました。
解決
特定のプロファイラを入れたら解決しました。
https://github.com/longxinH/xhprof
(´-`).。oO(なんか納得行かない……)
なので、もうちょっと調べてみました。
なぜ?
異常終了したコアダンプを見ると、メモリ解放系の処理のところで落ちています。
Core was generated by `php-fpm: pool www '.
Program terminated with signal 11, Segmentation fault.
# 0 hp_fast_alloc_hprof_entry () at /phpng-xhprof/phpng_xhprof.c:1163
1163 XHG(entry_free_list) = p->prev_hprof;
Missing separate debuginfos, use: debuginfo-install php-fpm-7.1.11-1.el6.remi.x86_64
この段階でマルチプロセス系の問題かな、と予想しました。
実質マルチプロセス
実は、オークファンのサイト上では、内部で自分にcurlを送っているコードがいくつかあるのです。
自分にcurlするということは、最低2つのプロセスが同時に存在しているということです。
そのときに、
- プログラムAが起動し、同時にプロファイラA'が起動する
- プログラムAが同一サーバーの別プログラムBにCurlする
- プログラムBが起動し、同時にプロファイラB'が起動する
- プログラムBが終了し、同時にプロファイラB'が終了する
- プログラムBの終了時点で、プロファイラB'の終了処理が呼び出される
- プロファイラB'の終了処理が、プロファイラ共有のメモリ領域を破壊する
- プログラムAが再開するが、プロファイラA'のメモリ領域は破壊されているので異常終了する
といった現象が発生しているのでは?
つまり、うまくいくプロファイラは、関連するリクエストが終了するまでメモリ領域を保管できているのでは?
実際、うまくいくソースコードとうまくいかないソースコードを見ると、うまくいく方はメモリの解放位置が違いました。
PHP_MSHUTDOWN_FUNCTION()とPHP_RSHUTDOWN_FUNCTION()を使い分けることで、マルチプロセスに対応できたのかなと思いました。
↑現時点でソースコードを見る限り、原因はこれじゃないかもしれません……
結論
(´-`).。oO(ライブラリを使う際は幅広く検討しましょう……)
最後に
株式会社オークファンでは、予想外の要求数百億件のデータと戦う、PHPエンジニア・Javaエンジニアを募集中です。
データは使ってこそ価値を持ちます。価値を生むのはあなたのコードです!