Passengerがメモリを食いすぎるとき

Passengerを動かしているサーバのメモリ使用量が突然跳ね上がってスワップをガリガリ発生させることしばしばだったので最近いろいろ調整していた。
結論としては二つ原因があった。

Railsインスタンスプロセスの立ち上がりすぎ

PassengerMaxPoolSizeを適切に設定してないとそうなることがある。

PassengerMaxPoolSizeのデフォルトは6なのでRailsインスタンスが一個につき400MBのメモリを食っていたら最大で2.4GBのメモリを食うことになる。
というわけでメモリが2GBのサーバでも撃沈する。(まあ400MB消費すること自体がおかしいけど)

インスタンスひとつあたりのメモリ使用量を把握するにはしばらく動かしてみるしかないと思うので(何か方法あるかな?)最初は小さめに設定しておくのが無難かもしれない。
この値が1とか2くらいでも小さなサービスでは全く問題ないし。
今回のケースでもこれを4以下にしておけばスワップ発生はなかっただろう。

とはいえ2GBのメモリを搭載しているサーバでPassengerMaxPoolSizeが6で破綻するというのは残念すぎる。
今回の場合はこれは主原因ではなく副原因と言うべきだろう。

一気に大量のレコードを取得しすぎ

そこでもうひとつの問題。

たまにPassengerのRailsインスタンスプロセスのサイズが300MBとか400MBくらいに肥大化しているときがあるので何でかと思って調べてみた。
何となくカンでアタリをつけて動作を確認していると、案の定サイトマップのために数千レコード取得してるのが原因だった。

ビューテンプレートで以下のようにして一度に取得するレコード数を制限したら90MB台をキープするようになった。

Item.paginated_each(:per_page => 100) do |item|
  xml.url do
    xml.loc(url_for(:controller => 'item', :action => 'show', :id => item.id, :only_path => false))
  end
end

善哉。
本当はなるべくビュー側でモデルを直接取得しない方がいいのだろうけど、背に腹は代えられない。
なお、こういう場合はカンじゃなくてログとか調べて時間のかかってる処理からアタリをつけるのがほんとはいいと思う。

ちなみにPassengerじゃなくてthinとかでも同じ問題は起きる。
アプリの実装次第でメモリ使用量が大分変わるという例でもあるということで。