Ruby
Rails
ActiveRecord
RubyOnRails

Rails:AvtiveRecordのバグ?でメモリが解放されない問題をunicorn-worker-killerを入れて解決した

何があったか

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

Gemfile
gem 'unicorn-worker-killer'
$ bundle install

config

自分の場合はproduction環境でのみunicornを使っているので、unicorn-worker-killerも同様にproduction環境でのみ動くように記述しました(環境指定しないと怒られました)。

my_app/config.ru
# 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を再起動する動きをしてくれて何回リクエストをしてもハングすることは無くなりました。
でも落としちゃうってワイルド過ぎない。

参考

まとめ

  • ActiveRecord要注意
  • 本番でunicorn使っているならunicorn-worker-killerがおすすめ
  • pumaは悪くなかった
  • ActiveRecordのメモリリークに関してご存知の方情報募集中です!

結構ヤバめな問題だと思うんですが、無料で利用させてもらっているのであまり偉そうなこと言えないですね。

追記

どういったリクエストを行っていたかについてもう少しだけ具体的に書くと、数十万件のデータを抱えるXXX APIに全件取得リクエストが成功した後、その他の数百件程度の比較的件数が少ない別のAPIたちに続けてリクエストを行っていると、いずれ動かなくなるというものです。

変なコードを書いてメモリリークさせちゃっている可能性も無くは無いです。
ただ、レスポンスが終わった後は必ずGCが働くんじゃないのと思っていて、もしそこらへんの自分の認識が間違っていると結構ヤバヤバです。