こんばんは、ウチイダです。

結構前なのですが、しょうもないヤラカシをしたので、自戒を込めて記事にしておきます。

冒頭、まずはお詫びを。エックスサーバーさん、その節は大変失礼いたしました🙇

何があったのか

それは、エックスサーバー上でLaravelのアプリケーションを運用するお仕事でした。

そこそこ本格的なアプリケーションで、外部サービスのAPIへのリクエストなどの重い処理が走るので、ジョブ・キューを使うことにしました。

本来ならドキュメントにもある通り、supervisor などを使ってワーカープロセスをデーモン化したいところです。

しかし、さしものエックスサーバといえども、supervisor は利用できないようでした。

そこで、cron を使ってワーカープロセスを定期的に起動するようにしました。

実際に記述していたcron エントリは以下のような感じです。ディレクトリのパスだけ、ダミーのものに変えています。

* * * * * cd path/to/laravel_project && /opt/php-8.1/bin/php artisan queue:work --max-time=36000 --timeout=36000 --env=production >> /dev/null 2>&1

設定後しばらくしたら、エックスサーバーのサポートからメールで連絡がありました。

要旨としては、契約しているサーバーが高負荷になっており、cron で実行されてるプロセスが原因なので早急に対策せよというものでした。

慌ててエラーログを確認したところ、DBとのコネクションリソースも使い果たしていました…

[2022-07-31 12:40:51] production.ERROR: SQLSTATE[HY000] [1226] User 'my_user' has exceeded the 'max_user_connections' resource (current value: 100) // ...以下略

サポートから指摘があった通り、CPU負荷も高くなっていただろうと思います。

何がいけなかったのか

冷静にcron設定を見直してみたところ、いくつか問題点に気づきました。

  • 重い処理が渡されたときのために –max-time と –timeout を 36000秒(10時間!)というめちゃ大きな値にしている
  • –stop-when-empty オプションをつけていないので、–max-time オプションで指定している秒数、ワーカープロセスが残り続ける
  • cron の起動は最短間隔(1分ごと)にしてあるため、新しいワーカープロセスが頻繁に生成される

つまり、古いプロセスが消えることなく、新しいプロセスを次々に作ってしまう設定だったということです。

ちょっと落ち着いて考えればわかりますね…あほすぎる…

cron エントリの修正

今回の原因は、古いワーカープロセスが終了せず、時間の経過とともに増え続けてしまうことでした。

というわけで、以下のようにcronエントリを修正しました。

*/5 * * * * cd path/to/laravel_project && /opt/php-8.1/bin/php artisan queue:work --max-time=300 --timeout=36000 --env=production >> /dev/null 2>&1

修正したのは以下の点です。

  • cron の実行周期を、1分から5分に変更
  • –max-time=300 に指定して、ワーカープロセスの生存時間を5分に設定
  • –timeout は、日に1回のバッチ処理が数時間かかるものがあるので、36000のままにしておく

5分毎にワーカープロセスがcronによって生成され、次の生成までワーカープロセスが少なくとも1つ存在するようにしました。

処理に5分以上かかるジョブが送られると、一時的に2つ以上のワーカープロセスが存在することになります。

そんなに重い処理が頻繁に発生するアプリケーションではなかったので、問題にはならないはずです。

これでも負荷が上がってしまうようなら、VPSとかクラウド環境に移行することをクライアントに提案するつもりです。

ちなみに、Laravel の日本語ドキュメントにも、queue コマンドの使い方は詳しく書いてあります。RTFM!!

https://readouble.com/laravel/9.x/ja/queues.html#running-the-queue-worker

まとめ

あらためまして、エックスサーバーさん、および同一サーバーを利用していた方、ご迷惑おかけしました。

ドキュメントをちゃんと読むことと、ローカルでの動作テストをきちんと行うことを改めて心に刻みました。

この記事を見ているみなさんはこんなアホなミスはしないと思いますが、ヤラカシ事例としてお知りおきください。

以上、あなたのお役に立てれば幸いです。