LoginSignup
12
15

More than 3 years have passed since last update.

Laravelでメモリリーク??? 実はそんなわけなかったというしょうもない話

Last updated at Posted at 2020-05-31

タイトルで若干ネタバレしてますが、ほんとしょうもない話です。ただ、おそらく同様のことで悩まれている方が何人かいるようなので、記事にしてみます。最後の方にちょっとした知見もありますが、ほぼ読み物です。

ちなみにSQSキューワーカーでメモリリークが起きるという記事がQiitaにありますが、今回の記事はこの件と直接関係はありません。

参考)
https://qiita.com/sh-ogawa/items/d64cafce2a2646b7abc6

単純な処理でなぜか延々と膨れ上がるメモリ使用量

Laravel 6.xでデータベース(今回はMySQLですがこの話に関係はない)に数万行のデータをインポートしようと試みたある日のことでした。CSVファイルから1000行ずつデータを読み出して1行ずつインサートするという手抜き超簡単なプログラムです(Laravel Excelを使用)。インポートプログラムが完成して数万行あるCSVファイルを食わせてバッチを実行したところ、数千行まで処理が走った瞬間に

PHP Fatal error: Allowed memory size of xxx bytes exhausted.

「ああん? いやいや、たかが数千行でメモリ食い潰すほどのことはなかろう。てか、1000行ずつデータとってBULK INSERTすらもせずゆっくりインサートしてるのにどこでこんなにメモリなんか使ってるんだ??? ははーん・・・・、これはなんかメモリリーク的なことが起きてるのでは????」

そもそもPHPでは意図的にメモリを確保したり解放したりなんてまずしませんし、メモリリーク的なことが起きるとしても循環参照が起きるなどでかなりレアケースではありますが…。以下を参考。

【PHP7.4新機能】弱参照(WeakReference)とGCとメモリリークについて整理したよ!
https://qiita.com/miracle-FJSW/items/f35c3e90a5d14eb6eba3

なんて変に勘ぐってしまったのが事の始まりでした。

プロセスのメモリ使用量を監視してみると、確かに徐々にメモリ使用量が膨れ上がり、1分間くらいで数十MBずつくらい物理メモリ(RES)が盛り盛りと増えていくのです。これは何事か!?
image.png
4000行くらい入れただけで436MBも使っちゃってる・・・

さらっと調べてみてもどうも同様のケースは見つけられない

データを読み込んで、そのデータをDBにインサートするだけという普通のことをしているだけなので、さすがに調べたら見つかるだろうと思ったのですが、どうにも見つかりません。でてくるのは「クエリログがメモリを食い潰しているのでそれを切れ!(\DB::connection()->disableQueryLog();)」的な記事がちらほら出てくるのですが、どうもLaravel5.5時代の話のようです。
参考
Memory leak on database insert function
https://github.com/laravel/framework/issues/30012

今はデフォルト設定でそのようなことは起きない模様。

ただ、DBのインサートをする行だけコメントアウトしてプログラムを実行すると、たしかにメモリ使用量は増えないので、Eloquentあたりになにかバグでもあってそれを踏んでいるのだろうか?なんてこの時点では思ってしまいました。

Laravel Telescope の存在に気づく

こまったなぁ、と思いつつ解決できないこと数か月・・・(一時的にphp.iniの設定をいじり、メモリがっつり増やせばデータは全件入るのでめちゃくちゃ困ってるわけではなかった)。

ある日、ぼんやりとプロジェクトファイル全体を眺めていたら、Laravel Telescopeを入れていることにふと気づきました。今までまったく使ってないのになんでこんなもん入れてるんだ? なんて思いつつ、すごく嫌な予感がしました。Laravel Telescopeをご存じでない方に、説明ページの冒頭を引用させていただきます。

Laravel TelescopeはLaravelフレームワークのエレガントなデバッグアシスタントです。Telescopeはアプリケーションへ送信されたリクエスト、例外、ログエンティティ、データクエリ、キュージョブ、メール、通知、キャッシュ操作、スケジュールされたタスク、さまざまなダンプなどを提示します。TelescopeはLaravelローカル開発環境における、素晴らしいコンパニオンです。
image.png

あー、これは危険な香りがプンプンする!!!ということで、これを無効にする設定(.envにTELESCOPE_ENABLED=false)をいれてphp artisan config:cacheしたあとにスクリプトを実行すると・・・ はいビンゴでした。

君か・・・ 数か月も見つけられなかったよ・・・

Laravel Telescope の一部機能を無効にする方法は色々ある模様

上の例では機能全体を無効にしましたが、一部機能だけ無効にすることもできます。詳しくはLaravel Telescopeのドキュメントを見ていただくといいと思いますが、今回の場合だと、Queryワッチャーをオフにすればメモリの使用量は大幅に減ります。ただ、Queryワッチャー以外でもメモリは使用しているので、ちょろちょろとメモリ使用量が増えてはいきます。

コード内の一部のみLaravel Telescopeを無効にしたい場合は

use Laravel\Telescope\Telescope;

//〜略〜

Telescope::stopRecording();
//Laravel Telescopeでメモリ食いの処理
Telescope::startRecording();

という感じで書けばメモリ問題も上手く捌けるようです。

参考)
Query builder memory leak on large insert
https://github.com/laravel/framework/issues/27539

この記事はLaravel Telescopeの件で気づいた後に見つけたけど、やっぱりメモリリークって勘違いするよなぁw

結論

Laravel Telescopeはメモリ食うから気をつけろ。

そして、入れたことを忘れるな(普通は忘れないよねw)

12
15
2

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
12
15