何があったか
APIの性能測定を行うべく数十万件のデータの全件取得を行った結果、pumaがハング。
「pumaダメじゃーん」とかろくに原因も調べずにpumaのせいにしていました。
その後、productionと同じnginx + unicornの組み合わせに変えて測定しなおした結果、同じくハング。
メモリ使用量を確認したら下記のような値が。
$ ps aux
hoge 31672 12.5 49.0 7306084 3926988 ? Dl 16:30 22:44 unicorn worker[1] -c /home/hoge/my_project/rails_app/config/unicorn.
7GB使ってるっていう。8GBしかないんですけど。
原因
ggってみた結果、どうもActiveRecordの問題らしいです。
参考: Qiita - ActiveRecordがメモリを食って解放しない問題
知らなかった。。ただ、該当のgithubとかのissueを見つけきれませんでした。
unicorn-worker-killerを入れて解決した
install
gem 'unicorn-worker-killer'
$ bundle install
config
自分の場合はproduction環境でのみunicornを使っているので、unicorn-worker-killerも同様にproduction環境でのみ動くように記述しました(環境指定しないと怒られました)。
# This file is used by Rack-based servers to start the application.
require_relative 'config/environment'
# 下記のif文を記述
if ENV['RAILS_ENV'] == 'production'
# Unicorn self-process killer
require 'unicorn/worker_killer'
# Max requests per worker
use Unicorn::WorkerKiller::MaxRequests, 3072, 4096
# Max memory size (RSS) per worker
use Unicorn::WorkerKiller::Oom, (192*(1024**2)), (256*(1024**2))
end
run Rails.application
無事unicorn_worerを再起動する動きをしてくれて何回リクエストをしてもハングすることは無くなりました。
でも落としちゃうってワイルド過ぎない。
参考
- Github - unicorn-worker-killer
- zephiransasのチラシの裏 - unicorn-worker-killerが便利だった件
- tabosque - unicorn-worker-killerのエラーがdevelopment環境で出る時
まとめ
- ActiveRecord要注意
- 本番でunicorn使っているならunicorn-worker-killerがおすすめ
- pumaは悪くなかった
- ActiveRecordのメモリリークに関してご存知の方情報募集中です!
結構ヤバめな問題だと思うんですが、無料で利用させてもらっているのであまり偉そうなこと言えないですね。
追記
どういったリクエストを行っていたかについてもう少しだけ具体的に書くと、数十万件のデータを抱えるXXX APIに全件取得リクエストが成功した後、その他の数百件程度の比較的件数が少ない別のAPIたちに続けてリクエストを行っていると、いずれ動かなくなるというものです。
変なコードを書いてメモリリークさせちゃっている可能性も無くは無いです。
ただ、レスポンスが終わった後は必ずGCが働くんじゃないのと思っていて、もしそこらへんの自分の認識が間違っていると結構ヤバヤバです。