taiki-t's diary

React Native, Rails そして雑多な記録: The world is waiting for you to give it a meaning.

メモ: puma on heroku, worker と thread

web serverとしてpumaを用い、Railsアプリケーションをherokuで運用するにあたり workerやthreadという話が出てきたのでメモ。

ネタ元は: Deploying Rails Applications with the Puma Web Server | Heroku Dev Center

Workers

Workerとは

pumaの文脈でいうworkerは、herokuでいうworkerとは別の概念であることに注意。 herokuのworkerはそれ用のdynoでバックグランド処理などを行うプロセス。一方、pumaのworkerは、pumaがforkする OSのプロセス。これによりRailsは並行してリクエストを処理できるようになる。

Worker processはOSレベルで独立なので、thread safeである必要はない。 自身のアプリがthread safeかどうかわからない、もしくはthread safeでない場合とりあえずworker processだけ増やせば安全にパフォーマンスの向上を図れるのだろうか。

設定

workers Integer(ENV['WEB_CONCURRENCY'] || 2)

のように環境変数でWEB_CONCURRENCYを設定する。ここら辺の詳細はProcfileの設定にて: Deploying Rails Applications with the Puma Web Server | Heroku Dev Center

Worker数について

workerはプロセスなので増やすごとにメモリ使用量が増える。なのでdyno一つにつきどれぐらいworkerを生やせるかはメモリ使用量による。 free, hobby , standard-1x dynoでは、典型的なRailsアプリだとだいたい2-4のworkerが目安らしい。個々の環境においては、dynoで利用可能なメモリの上限を超えた時に生じるR14 errorsを監視して設定する。

詳しくは: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#workers

Threads

Pumaはそれぞれのリクエストをthreadを使い処理できる。なのでworker数がリクエストの並行処理数を決め、thread数がそのそれぞれのリクエストの並行処理数を決める感じだろうか。

MRIにおいてはGILがあるので実行されるthreadは常に1つ。しかしIOの処理時にはGILはロックされない。そしてほとんどのRailsアプリは大量のIOを伴う。そのため、thread数を増やすことでPumaは複数のthreadを処理できるようになり、その結果スループットは上がる。

Pumaはthread数の上限と下限を設定できるが、heroku的には同じ値を設定するのがお勧めとのこと。

詳しくは: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#threads

参考リンク

Deploying Rails Applications with the Puma Web Server | Heroku Dev Center

マルチスレッド/プロセスまとめ(Ruby編) - Qiita

余談

thread safeなRailsとかそういう情報って少ない

How do I know whether my Rails app is thread-safe or not? - by Jarkko of Bear Metal