Less is Best

rubyが好き。技術の話とスタートアップに興味があります。

Sidekiqのワーカープロセスが増殖してメモリを食い殺す

スクリーンショット 2014-02-09 12.59.54.png

現在開発を進めていたアプリで、スクレイピングをするためにワーカーに処理を投げるということを行なっていました。その際にSidekiqを使用していたのですが、Sidekiqのワーカープロセスが増殖してサーバーのメモリを食い殺す。という自体が起きてしまい、エラーが頻発するようになってしまったのでその原因を探ってみました。

tmux-7.png

当初の段階で怪しいと思ったのはSidekiqのconfig/initializers/sidekiq.rbで設定ファイルに不備があるかconfig/unicorn/production.rbUnicornの起動時にRedisとコネクション結ばせている設定がおかしいかの部分ではないかと見込みを付けてみました。

元の設定はこんな感じ

config/initializers/sidekiq.rb

  Sidekiq.configure_server do |config|
    config.redis = { url: ENV['REDIS_URL'], namespace: "sidekiq_#{Rails.env}" }
  end

  Sidekiq.configure_client do |config|
      config.redis = { url: ENV['REDIS_URL'], namespace: "sidekiq_#{Rails.env}" }
  end

config/unicorn/production.rb

*
*
*

after_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection

  # When in Unicorn, this block needs to go in unicorn's `after_fork` callback:
  Sidekiq.configure_client do |config|
    config.redis = { :url => ENV["REDIS_URL"] , :namespace => "sidekiq_#{Rails.env}"}
  end
end

んで、調査を進めて行った所あまり、参考に出来そうな記事を見つけられず。MRIのバグやら,Sidekiqのメモリリークだなんやらという話がでてくる。解決出来なそうで不安になる。

Sidekiq not deallocating memory after workers have finished

Memory leak?

これも問題は問題だが、今回の状況とはちょっと異なる気がする。よくわからなくなって来たのでSidekiqのソースコードを読み始める。

途中、可愛いメソッド見つけてなごんだ。こんなメソッドも動くんだー。

スクリーンショット 2014-02-09 12.59.54.png

とふと気がついて、Capistranoでのデプロイ時になんかおかしいことが起こってるのではないかと主行ったって調べた所、見事Sidekiqのpidファイルを見つけられずにプロセスを終了させずに新たにプロセスを生成していたことを発見。

バグは、capistranoのsidekiqのpidを指定する部分でフルパスを渡していたせいでした。

config/deploy/production.rb

set :sidekiq_pid, "#{current_path}/tmp/pids/sidekiq.pid"

変更後

config/deploy/production.rb

set :sidekiq_pid, "tmp/pids/sidekiq.pid"

これで、しっかりとSidekiqの再起動時にプロセスがkillされるようになったはずなのでメモリを異常にくうことは無くなったはず。Capistranoがkillできなかった場合に止まってくれなかったこと、それに気付かなかったこと。そしてpidファイルのパス指定が賢くなかったことが重なった悲劇でした。ずっと悩んでいたのですがようやく解決。

にしてもSidekiqのメソッド可愛いですね。

広告を非表示にする