puma はメモリ食い
puma にして喜んでたけど二時間後くらいに NewRelic で様子を見てみたらメモリ 90% 以上消費してて swap ファイルもめっちゃでかいのができてて暴発一歩手前になってた。
一旦 puma をシングルモードからクラスターモードに変えて puma_worker_killer を導入し、 worker を定期的に再起動するようにした。ただ restart 後のメモリ増加傾向は変わらない。
引き続き NewRelic で様子を見ていると Python2 が一番メモリを食っている。このブログは Syntax Highlighting に Python の Pygments を pygments.rb 越しに使っている。これまで unicorn の worker プロセス 2 個で動かしていたときには最大でも Python のプロセスは 2 個で事足りてたけど、 puma にして 16 スレッド( puma のデフォルト)が起ち上がるので、 16 個の Python プロセスが起動していたっぽい。
これは意味ないなということで以前利用したことがあった Pygments の Ruby 移植版である rouge に変えてみることにした。こちらは Ruby なのでスレッドが別プロセスを起動してメモリ爆発ということにはならないと思われる。
puma_worker_killer と Pygments 置き換え後半日様子を見たが、これでメモリ消費量爆発ということはなくなった。
sidekiq でスレッドを増やしたときにも似たようなことを経験したけど、 Ruby から外部のプロセスを起動するようなプログラムを書いているときにマルチスレッドで処理をさせると、 Ruby のプロセスは増えなくても外部のプロセスがめっちゃ増えてそのせいでメモリあっぷあっぷになったり CPU の使用率が高まったりする。マルチスレッドプログラミングのご利用は計画的に。
異なるワーカー間でセッションを継続できない?
puma をクラスターモードにしたことで別の問題も発生した。なんとセッション情報が異なるプロセス間で共有されていないっぽい。なのでログインしてすぐに再ログインを求められるようになった。これは不便。原因を調査するには時間がかかりそうだったのでシングルモードに戻してみることにする。
まとめ
- デフォルトの 16 スレッドでは puma は unicorn に比べてメモリを沢山消費する可能性がある
- Ruby から外部のプロセスを起動するような処理に注意
- puma のクラスターモード( master worker モデル)でセッション情報がリセットされる問題に遭遇
追記
セッション情報が異なるプロセス間で共有されていない
これはちゃんと設定があって、 puma.rb に preload_app!
を記述すればよかった。
If you're running in Clustered Mode you can optionally choose to preload your application before starting up the workers. This is necessary in order to take advantage of the Copy on Write feature introduced in MRI Ruby 2.0. To do this simply specify the --preload flag in invocation:
# CLI invocation $ puma -t 8:32 -w 3 --preload
If you're using a configuration file, use the preload_app! method, and be sure to specify your config file's location with the -C flag:
$ puma -C config/puma.rb # config/puma.rb threads 8,32 workers 3 preload_app!
これでプロセス間でセッション情報が共有されるようになった。