| @技術/プログラミング

puma はメモリ食い

puma にして喜んでたけど二時間後くらいに NewRelic で様子を見てみたらメモリ 90% 以上消費してて swap ファイルもめっちゃでかいのができてて暴発一歩手前になってた。

一旦 puma をシングルモードからクラスターモードに変えて puma_worker_killer を導入し、 worker を定期的に再起動するようにした。ただ restart 後のメモリ増加傾向は変わらない。

引き続き NewRelic で様子を見ていると Python2 が一番メモリを食っている。このブログは Syntax Highlighting に Python の Pygmentspygments.rb 越しに使っている。これまで unicorn の worker プロセス 2 個で動かしていたときには最大でも Python のプロセスは 2 個で事足りてたけど、 puma にして 16 スレッド( puma のデフォルト)が起ち上がるので、 16 個の Python プロセスが起動していたっぽい。

これは意味ないなということで以前利用したことがあった Pygments の Ruby 移植版である rouge に変えてみることにした。こちらは Ruby なのでスレッドが別プロセスを起動してメモリ爆発ということにはならないと思われる。

puma_worker_killer と Pygments 置き換え後半日様子を見たが、これでメモリ消費量爆発ということはなくなった。

sidekiq でスレッドを増やしたときにも似たようなことを経験したけど、 Ruby から外部のプロセスを起動するようなプログラムを書いているときにマルチスレッドで処理をさせると、 Ruby のプロセスは増えなくても外部のプロセスがめっちゃ増えてそのせいでメモリあっぷあっぷになったり CPU の使用率が高まったりする。マルチスレッドプログラミングのご利用は計画的に。

異なるワーカー間でセッションを継続できない?

puma をクラスターモードにしたことで別の問題も発生した。なんとセッション情報が異なるプロセス間で共有されていないっぽい。なのでログインしてすぐに再ログインを求められるようになった。これは不便。原因を調査するには時間がかかりそうだったのでシングルモードに戻してみることにする。

まとめ

  1. デフォルトの 16 スレッドでは puma は unicorn に比べてメモリを沢山消費する可能性がある
  2. Ruby から外部のプロセスを起動するような処理に注意
  3. 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!

-- https://github.com/puma/puma#clustered-mode

これでプロセス間でセッション情報が共有されるようになった。

| @技術/プログラミング

以前、Unicorn が暴走して困ってるという記事を書いていた。しかしよくよく調べてみると暴走してるのは Unicorn ではなく、Syntax Highlight に使っている Python の Pygments を呼び出している Ruby のプロセスだった(pygments.rb)。こいつが CPU をバカ食いしてしまい、アプリケーションのレスポンスがすこぶる悪くなっていた。

少し前、Pure Ruby の Pygments 互換 Syntax Highlighter で Rouge というのを発見していたので、Python を spawn するプロセスがなくなれば CPU バカ食いも止まるはず、と思って pygments.rb から移行してみた。しかし…

なんと逆にサイトが重くなってしまった。やはり Pure Ruby のプログラムは重いみたいである。pygments.rb は C のネイティブコードも含んでいて、速さがボトルネックになるところは C で処理しているみたい。

というわけで残念ながら EC2 のマイクロインスタンスで Rouge を常用するのは無理そうなので Pygments + pygments.rb に戻します。あとはキャッシュして誤魔化すしかなさそう。

| @技術/プログラミング

外タレのJekyllブログを見てると、シャレオツな感じでコードがシンタックスハイライトされてる。どうもPygmentsというやつを使うらしい。公式Wikiでも触れられていて、コードをハイライトさせたいときは jekyll --pygments しろや、みたいなことが書いてあるんだけど(Liquid Extensions - jekyll - GitHub)、そういうオプションつけてもコードは全然色つきにならず、「サギやんけ」とか思ってた。

しかしマニュアルをよく読むと、PygmentsってのはPython製のソフトで、こいつを別途インストールする必要があるらしい。なるほどそういうことだったのか。そういうわけで

$ port search pygments

してみたところ、MacPortsでは三つヒットした。

py-pygments @1.0 (python, devel)
    Python syntax highlighter

py25-pygments @1.0 (python, devel)
    Python syntax highlighter

py26-pygments @1.3.1 (python, devel)
    Python syntax highlighter

Found 3 ports.

しかしSnow LeopardにはPython 2.6.1が入ってるので、

$ easy_install Pygments

でもオッケーかも知れない。僕は職場に持ち込んでMacBookにはMacPortsから py26-pygments を入れた。そしたらdependencyが発動してPythonとかXorg何とかというもののダウンロードも始まり、 /opt/local/bin/python-2.6.5 が入ったが、長々と時間がかかった。※1

インストール完了後、これでシャレオツシンタックスハイライティングできるようになったと思い、意気揚々と

$ jekyll --pygments

してみたのだが、一向にハイライトされない。なんか「pygmentizeとか見つからないし」みたいなエラーが出る。なんでじゃ〜とイライラしながら公式Wikiを読んでると、次のような記述があった。

the pygmentize binary must be in your path

そうなのよ。 pygmentize-1.3.1 っていうバイナリファイルはあってパスは通ってるんだけど、 pygmentize そのものがないのよ。そういうわけで

$ sudo ln -s /opt/local/bin/pygmentize-1.3.1 /opt/local/bin/pygmentize

してやった。すると pygmentize というバイナリファイルにパスが通るので、めでたくシャレオツシンッタクスハイライティング環境が手に入った。ちなみにCSSはGithub互換のものを拾ってきて入れといた。