Rails アプリケーションを Puma Web サーバーでデプロイする
最終更新日 2024年04月26日(金)
Table of Contents
並列リクエストを処理する Web アプリケーションのほうが、一度に 1 つのリクエストしか処理しない Web アプリケーションより dyno リソースをより効率的に使用します。Puma は Unicorn と競合する Web サーバーで、並列リクエストの操作ができます。
Puma は、Worker プロセスだけでなくスレッドを使用して、使用可能 CPU をより活用します。Puma でスレッドを使用できるのは、コードベース全体がスレッドセーフな場合だけです。安全でない場合は、Puma を使用できても、ワーカープロセスを通してスケールアウトしなければなりません。
このガイドでは、Puma Web サーバーを使用して新しい Rails アプリケーションを Heroku にデプロイする方法について説明します。基本的な Rails のセットアップについては、Rails スターターガイドを参照してください。
必ず新しいデプロイをステージング環境でテストした後に、本番環境へデプロイしてください。
Puma をアプリケーションに追加する
Gemfile
最初に、Puma をアプリの Gemfile に追加します。
gem 'puma'
Procfile
Puma をアプリケーションの Procfile
で Web プロセスのサーバーとして設定します。ほとんどの値をインラインで設定できます。
web: bundle exec puma -t 5:5 -p ${PORT:-3000} -e ${RACK_ENV:-development}
しかし、設定ファイルを生成することをお勧めします。
web: bundle exec puma -C config/puma.rb
Procfile
で大文字が正しく使われて Git にチェックインされるようにしてください。
設定
Puma の設定ファイルを config/puma.rb
、または好きなパスに作成します。単純な Rails アプリケーションでは、次の基本設定を推奨します。
workers Integer(ENV['WEB_CONCURRENCY'] || 2)
threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)
threads threads_count, threads_count
preload_app!
rackup DefaultRackup if defined?(DefaultRackup)
port ENV['PORT'] || 3000
environment ENV['RACK_ENV'] || 'development'
on_worker_boot do
# Worker-specific setup for Rails 4.1 to 5.2, after 5.2 it's not needed
# See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot
ActiveRecord::Base.establish_connection
end
また、Rails アプリケーションのデータベース接続数を、すべてのスレッドと Worker のプールで不足なく利用できる数にする必要があります(これについては後述します)。
Worker
workers Integer(ENV['WEB_CONCURRENCY'] || 2)
この値を手動で設定するには、
heroku config:set WEB_CONCURRENCY
を使用します。アプリケーションの負荷テストを実行して、アプリに適切な値を見つけます。
Puma が各 dyno 内で複数の OS プロセスを分岐するので、Rails アプリが複数の並列リクエストをサポートできるようになります。Puma 用語で、これらは Worker プロセスと呼ばれます (Heroku Worker プロセスがそれぞれの dyno で実行されるのと混同しないようにしてください)。Worker プロセスは OS レベルでは互いに分離されているため、スレッドセーフにする必要がありません。
マルチプロセスモードは、JRuby または Windows を使用していると機能しません。理由は、JVM および Windows が複数のプロセスをサポートしないためです。JRuby または Windows を使用している場合はこの行を設定から省略してください。
Worker プロセスを使用するごとに新たなメモリが使われます。この動作によって、1 つの dyno で実行できるプロセスの数が制限されます。一般的な Rails メモリフットプリントでは、2 ~ 4 の Puma Worker プロセスを eco
、basic
または standard-1x
dyno で実行できます。ご使用のアプリケーションでは、具体的なメモリフットプリントに応じて、おおよそ同等数が可能になります。アプリケーションの高速チューニングを可能にするため、この数を環境設定で指定することをお勧めします。アプリケーションログの R14 エラー (メモリ割り当てを超過) について、当社のロギングアドオンまたは Heroku ログで調べてください。
スレッド
threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)
threads threads_count, threads_count
Puma は各リクエストを、内部スレッドプールのスレッドで処理できます。この動作により、Puma が Web アプリケーションにさらなる同時性を提供します。大まかに言えば Worker が使う RAM とスレッドが使う CPU が増えることで、同時性が向上します。
MRI では、Global Interpreter Lock (GIL) によっていつでも 1 つのスレッドだけが実行されます。データベース呼び出し、ファイルシステムとの相互作用、あるいは外部 http 呼び出しなどの IO 操作によって GIL がロックされなくなります。ほとんどの Rails アプリケーションが IO を大量に使用します。そこで、さらなるスレッドを追加することで Puma は複数のスレッドを処理できるようになり、スループットが向上します。JRuby と Rubinius も Puma を使用することでメリットが得られます。これらの Ruby 実装には GIL がなく、すべてのスレッドを、中で何が起こっているかにかかわらず、並列実行します。
Puma ではスレッドプールを min
および max
設定で設定して、各 Puma インスタンスが使用するスレッドの数をコントロールできます。最小スレッド数の設定では、負荷がかかっていないときのリソースをアプリケーションが減少できます。Heroku ではアプリケーションがすべてのリソースを所定の dyno で消費できるため、この機能は必要ありません。最小が最大と等しくなるよう設定することをお勧めします。
各 Puma Worker が、指定する最大数のスレッドまで生成できるようになります。
アプリの事前ロード
preload_app!
アプリケーションを事前ロードすると個々の Puma Worker プロセスの起動時間が減り、on_worker_boot
呼び出しを使用して各 Worker の外部接続を管理できるようになります。上の設定では、これらの呼び出しは、各 Worker プロセスの Postgres 接続を正しく確立するために使用します。
Worker ブート時
on_worker_boot
の使用は Rails 5.2+ アプリでは不要になりました。これは、フォークされた接続は自動的に再接続されるためです。
on_worker_boot
ブロックは Worker 発生の後、リクエストの受諾を開始する前に実行されます。このブロックは、接続を複数のプロセス間で共有できないために別のサービスへ接続する場合に特に便利です。この動作は、Unicorn の after_fork
ブロックと同じです。必要となるのはマルチプロセスモードを使用している場合だけです (つまり workers
が指定されている)。
Rails 4.1+ を使用している場合、以下のように database.yml を使用して接続プールサイズを設定するだけで済みます。
on_worker_boot do
# Worker-specific setup for Rails 4.1 to 5.2, after 5.2 it's not needed.
# For apps using the `config/database.yml` method of setting `pool` size
ActiveRecord::Base.establish_connection
end
使用していない場合、以下のように再接続コードを具体的に指定する必要があります。
on_worker_boot do
# Valid on Rails up to 4.1 the initializer method of setting `pool` size
ActiveSupport.on_load(:active_record) do
config = ActiveRecord::Base.configurations[Rails.env] ||
Rails.application.config.database_configuration[Rails.env]
config['pool'] = ENV['RAILS_MAX_THREADS'] || 5
ActiveRecord::Base.establish_connection(config)
end
end
すでにイニシャライザを使用している場合は、
database.yml
方式へできるだけ早く切り替える必要があります。イニシャライザを使用するためには、ハイブリッドモードを Puma で使用しているときにコードを複製する必要があります。イニシャライザ方式は、発生している内容に関する混乱が生じることがあり、サポートチケットが大量に消費される原因となります。
デフォルト設定では、データベースプールサイズを設定しています。詳細については、Concurrency and Database Connections in Ruby with ActiveRecord (ActiveRecord による Ruby での同時性とデータベース接続) を参照してください。データベースへの新しい接続もここで作成します。
Postgres、Redis、memcache などのデータストアへの再接続が必要になります。事前ロードセクションで、Active Record を再接続する方法を紹介します。Resque を使用しているために Redis へ接続される場合は、再接続が必要になります。
on_worker_boot do
# ...
if defined?(Resque)
Resque.redis = ENV["<redis-uri>"] || "redis://127.0.0.1:6379"
end
end
アプリケーションの起動中に接続エラーが発生する場合は、コミュニケーションしようとしているサービスを gem ドキュメントで調べて、このブロックで再接続できる方法を確認してください。
ラックアップ
rackup DefaultRackup
rackup
コマンドを使用して Puma にラックアプリを起動する方法を伝えます。この設定が指し示すアプリケーション config.ru
は、新しいプロジェクトを作成すると Rails により自動的に生成されます。
この行は、新しいバージョンの Puma では必要ないかもしれません。
ポート
port ENV['PORT'] || 3000
Heroku は、Web プロセスが起動するとき ENV['PORT']
を設定します。ローカルで、これを 3000
にデフォルト設定して Rails のデフォルトに合わせます。
環境
environment ENV['RACK_ENV'] || 'development'
Puma の環境を設定します。Heroku で ENV['RACK_ENV']
がデフォルトで 'production'
に設定されます。
タイムアウト
Puma 内にはリクエストタイムアウトのメカニズムがありません。Heroku ルーターは 30 秒を超えたすべてのリクエストをタイムアウトにします。エラーがクライアントに返されますが、ルーターが Puma にリクエストが早く終了したことを知らせる方法がないので、Puma はリクエストに対する動作を継続します。処理能力の停滞を防ぐため、Rack::Timeout
を使用して時間がかかっているリクエストを終了してそのリクエスト元を特定することをお勧めします。
ラックタイムアウト gem をプロジェクトに追加してから、タイムアウト値を環境変数から設定します。
$ heroku config:set RACK_TIMEOUT_SERVICE_TIMEOUT=20
20 秒間継続するリクエストが終了するようになり、スタックトレースがログに出力されます。スタックトレースでアプリケーションのどの部分が原因でタイムアウトになるかを調べて、問題を修正できます。
注意: この環境変数サポートには、rack-timeout 0.5.0 以上が必要です。
様々なラックタイムアウト設定を理解するため、双方向型ラックタイムアウトデモのセットでこれらの設定がどのように動作するかを調べることができます。
ご使用のアプリケーションで Heroku - H12 からタイムアウトエラーが大量に発生する場合は、アプリケーションが回復不可能な状態になるリスクが潜んでいます。当社はラックタイムアウトを使用することをお勧めしますが、ピークロードに対処できるだけの十分な dyno がアプリケーションにあること、暴走パフォーマンスの問題がないことを確認する必要があります。ラックタイムアウトが使用する Thread.raise によって、アプリケーションがリソース (接続プール内の接続など) を除去できなくなる場合があります。
ご使用のアプリケーションに dyno が十分にあるか把握するため、Puma Pool Usage (Puma プール使用) を有効にして監視できます。Puma の Worker とスレッドが正しく設定されているのであれば、安定使用値が 20% を下回るとアプリの dyno 数が多すぎることを示します。80% を超えると、dyno をさらに追加することでアプリケーションにメリットが得られる可能性があります。
アプリケーションの dyno 数が十分なのにタイムアウトが発生している場合は、アプリケーションパフォーマンスを調査する必要があります。お勧めする APM アドオンは Scout や New Relic などです。アプリケーションを理解してチューニングするために、Complete Guide to Rails Performance (Rails パフォーマンスの完全ガイド) を参照することをお勧めします。
スロークライアント
スロークライアントとは、データの送受信が遅いクライアントです。たとえば、ユーザーが携帯電話からアップロードした画像を受信するアプリが WiFi や 4G、あるいは他の高速ネットワーク上にない場合です。このタイプの接続は、Unicorn など一部のサーバーにとってサービス拒否が発生する原因となります。なぜなら Worker はリクエストが終了するのを待つ間アイドル状態でいなければならないからです。アプリケーションを保護するため、Puma のようにスロークライアント保護が内蔵されたサーバーへ移行するか、スロークライアントに対処する NGINX などのプロキシサーバーを使って実行する必要があります。Unicorn Web サーバーは NGINX を使って実行しなければなりません。そうしないと、スロークライアント攻撃の標的になってしまいます。
Puma では、リクエストトランザクションでの Worker のブロックを必要とせず、複数のスロークライアントが接続できます。これが理由で、Puma はスロークライアントに余裕を持って対処します。Heroku では、Puma をスロークライアントが予想されるシナリオで使うことをお勧めします。
Puma がスロークライアントを緩和する方法についての詳細は、Puma 4: New I/O 4 Your Server (Puma 4: 新しい I/O をご使用のサーバーに) を参照してください。
データベース接続
同時性をアプリケーションに追加すればするほど、必要となるデータベースへの接続が増えます。各アプリケーションで必要となる接続の数を求めるための式は、RAILS_MAX_THREADS
に WEB_CONCURRENCY
を掛けます。この組み合わせによって、各 dyno が使う接続の数が求められます。
Rails が維持するデータベース接続プールでは、Worker プロセスごとに新しいプールが作成されます。Worker 内のスレッドは同じプールで動作します。Rails データベース接続プール内の接続数が十分にあり RAILS_MAX_THREADS
の接続数を使用できるようにしてください。次のエラーが発生する場合:
ActiveRecord::ConnectionTimeoutError - could not obtain a database connection within 5 seconds
このエラーは、Rails 接続プールが少なすぎることを示します。これらのトピックの詳細は、Dev Center の記事「Concurrency and Database Connections」(並列性とデータベース接続) を参照してください。
バックログ
Puma の「backlog」値を設定することが可能です。この設定は、Puma が HTTP リクエストの拒否を開始するまでソケットでキューに入れられるリクエストの数です。デフォルト値は 1024 までです。この値を変更したり小さくしないことをお勧めします。この値を小さくして、dyno がビジーなときに、あまりビジーでない dyno へリクエストが送られるようにしたほうがよいと思われるかもしれません。Heroku は送り返されたリクエストを再ルーティングするとき、使用しているアプリ全体が飽和していると想定します。接続毎に 5 秒間遅れるので、自動的にリクエスト毎に 5 秒間のペナルティが課せられます。ルーティングの動作に関する詳細を参照してください。さらに、dyno の 1 つがリクエストのバウンシングを開始した場合、負荷の増大が原因である可能性が高く、すべての dyno がリクエストをバウンシングしてしまいます。同じリクエストが何度もバウンシングされると、お客様のエラー率が高くなります。
バックログ値を任意に高く設定することで、dyno がリクエスト数の急騰に対処できるようになります。この値を小さくしてもアプリの速度はほとんど速くならず、むしろお客様にとってリクエストのエラーが増えます。Heroku では、バックログ値を設定しないでデフォルト値を使用することをお勧めします。
スレッドセーフティ
スレッドセーフなコードを複数のスレッド間で、エラーを起こさずに実行できます。Ruby コードがすべてスレッドセーフというわけではありません。しかも、ご使用のコードとすべてのライブラリを複数のスレッド間で実行できるかどうかを調べるのは難しいかもしれません。
Rails 4 までは、スレッドセーフな互換性モードに切り替えることができました。しかし、Rails がスレッドセーフというだけで、ご使用のコードがスレッドセーフになるという保証はありません。
アプリケーションを以前に sidekiq や Puma のようなスレッド化した環境で実行したことがなければ、最初に Puma を使用して Rack::Lock ミドルウェアを追加し、各リクエストをミューテックスでラップしてすべてのリクエストが効果的に同期実行されるようにしてみます。
# config/initializers/rack_lock.rb
Rails.application.config.middleware.insert_before 0, Rack::Lock
Rack::Lock
によってスレッドセーフティの問題がアプリケーションで起きなくなりますが、このミドルウェアの同期的性質はアプリケーションの応答速度がスレッドを使用している場合より遅くなることを意味しません。複数の Worker と Worker 当たり 3 つのスレッドを使用して本番環境にデプロイしたアプリケーションの例をここに示します。Rack::Lock は、説明目的のため、8:30 pm よりわずか前に導入しています。「ミドルウェア」で紫色のリクエスト時間が増大しています。
Rack::Lock
によって複数のスレッドを利用できなくなりますが、Worker を追加することで同時性を得ることができます。Worker は異なるプロセスで実行され、メモリを共有しないので、スレッドセーフでないコードが複数の Worker プロセスで実行される可能性があります。しかし、効率を最大にするため、プロセスとスレッドの両方で実行できるようにすることをお勧めします。
スレッドセーフティのヒント
スレッド化された Worker または Web サーバー (Puma など) を初めて使う方は、どうしたら自分のアプリがスレッドセーフかどうかわかるでしょうか ? 残念ながら、スレッドセーフティについて正確に調べられるリトマス試験紙はありませんが、調べることができるいくつかの領域があります。
スレッドセーフな依存関係にする。ご使用の gem がすべてスレッドセーフであり、比較的よく使われており過去 1 年内のリリースがあるほとんど (すべてでなくても) の gem がスレッドセーフであることを確認します。
グローバルを変化させない。一般に、グローバルアクセス可能な値は変化しないようにしたいと考えます。たとえば Kernel.const_set
をリクエストで使用していれば、それが現在のリクエストだけでなく、すべてのスレッドのすべてのリクエストに影響を与えます。このスタックオーバーフローの回答からスレッドセーフでない他のいくつかの領域についてアイデアを得ることができます。
rack-freeze の使用。この gem によって、ご使用のミドルウェアを誤って変異させることがなくなります。ラックフリーズは Rack::Lock とは別のもので、アプリの速度を遅くしません。自分のラックミドルウェアを記述した場合は、スレッディングバグを容易に発生させることができます。たとえば各インスタンスが複数のリクエストに接触して、このミドルウェアがスレッドセーフでなくなるからです。
# Your Custom middleware
class StatusMessage
def initialize(app)
@app = app
end
def call(env)
status, headers, response = @app.call(env)
if status == 200
@message = response
else
@message = "Bad response"
end
# Value of @message could change here
# if another thread has executed,
# this is called a "race condition"
[status, headers, @message]
end
end
# in config/application.rb
config.middleware.use StatusMessage
この場合、@message
がグローバルな理由は同じオブジェクトがすべての応答で使用されるからです。ローカル変数 message
を使用することで修正できます。インスタンス変数 @message
を使う必要はありません。ラックフリーズを使用している場合、gem は freeze
メソッドをミドルウェアで呼び出すので、内部状態が変わった場合は例外が変更されます。この gem により、本番環境にデプロイする前に、開発環境であらゆるスレッドセーフティ関連の問題を見つけ出せるようになります。
ステージとデプロイ。先へ進む準備ができたら、Rack::Lock
をプロジェクトから削除して、以下を実行して進んだことを確認します。
$ RAILS_ENV=production rake middleware
最初に、ステージングアプリまたはレビューアプリにデプロイします。スレッド数を 1 より上に増やします。デフォルトのスレッド数を Worker 毎に 5 にすることをお勧めします。ただし、最初はそれより少ない数にして、徐々に増やすこともできます。
$ Heroku config:set MIN_THREADS=2 RAILS_MAX_THREADS=2
アプリケーションがステージング環境で実行されるようになったら、複数の同僚にサイトへ同時にアクセスしてもらいます。
例外をモニタリングして、「**deadlock detected (fatal)(デッドロックが検出されました(致命的)) 」などのエラーがないか調べる必要があります。同時性バグを見つけ出して修正するのは難しい可能性があるため、アプリケーションを徹底的にテストした後、本番環境にデプロイします。アプリケーションをスレッドセーフにできると、Puma スレッドと Worker を使用したスケールアウトは Worker だけを使用した場合よりスループットが増大するため、そのメリットは相当なものです。
アプリケーションが予想通りに動作すると確信できたら、本番環境にデプロイしてスレッド数を増やすことができます。
Puma のプロセスとスレッドの推奨されるデフォルト設定
Puma のプロセスとスレッドの推奨されるデフォルト値を以下に示します。
dyno タイプ | 推奨される Web プロセスカウント (WEB_CONCURRENCY) | 推奨される Web スレッドカウント (RAILS_MAX_THREADS) |
---|---|---|
eco | 1 (メモリが十分な場合は 2) | 5 |
basic | 1 (メモリが十分な場合は 2) | 5 |
Standard-1x | 1 (メモリが十分な場合は 2) | 5 |
Standard-2x | 2 | 5 |
Performance-m | 2 | 5 |
Performance-l | 8 | 5 |
performance-l-ram | 4 | 5 |
performance-xl | 8 | 5 |
performance-2xl | 16 | 5 |
ここでの推奨値に関係なく、アプリケーションに合わせて値を調整する必要があります。アプリケーションで R14 - Memory Quota Exceeded (メモリ割り当てを超過しました) エラーが発生した場合、プロセスカウントを削減することを検討してください。
以下の資料は包括的なものではなく、変更される可能性があります。アプリケーションのパフォーマンスの調整について完全に理解するには、『The Complete Guide to Rails Performance』(Rails パフォーマンスの完全ガイド) をお勧めします。
プロセスカウント値
プロセスカウントを増やすと RAM の使用率が増加し、制限要因になることがあります。この値を設定するための別の要因は、システムの物理コア数です。GVL のために、Ruby インタープリタ (MRI) は Ruby コードを実行する 1 つのスレッドのみ一度に実行できます。この制限のため、複数のコアを完全に活用するには、システム上の物理コア数に一致するプロセスカウントをアプリケーションで持つようにする必要があります。
物理コア数を超えると、プロセスでは限られたリソースを求めて競合が発生します。この競合により、コードの実行に使用できたはずの余計な時間を、コンテキストの切り替えに消費することになります。
dyno 上の vCPU の数は、システムで heroku run bash
を使用して nproc
を実行して見つけることができます。次に例を示します。
$ heroku run bash --size=performance-l
$ nproc
8
nproc
によって返される値には、物理コアに加えて「ハイパースレッド」が含まれており、これらの 2 つを組み合わせたものが vCPU カウントと呼ばれています。Heroku で使用されるすべての物理コアにはハイパースレッドがあるため、「本当の」物理コア数を求めるには、2 で割ってください。たとえば、Performance-l の dyno の場合、4 つの物理コアと 4 つのハイパースレッドがあります。この dyno は一度に 4 つのプロセッサのみから物理的に命令を実行できます。
eco
、basic
、standard-1x
、および standard-2x
の dyno についての nproc
の値は正しいですが、これらのコアはコンテナ内で実行中の複数のアプリケーションで共有されています。これらの dyno について nproc
ではすべて 8
が返されますが、一度に 1 つのプロセスしか実行できないと推定するのが最適です。
特定の時間に実行できる最大のプロセス数は、物理コア数によって決まりますが、物理コア数を超えるようにプロセスカウントを調整する場合もあります。複数のプロセスがあると、1 つのプロセスがクラッシュした場合の冗長性を提供することができます。Puma Worker プロセスがクラッシュすると、再起動しますが、このプロセスは瞬時には動作しません。マスタープロセスが Worker プロセスを置換するとき、冗長性があれば、2 番目のプロセスによって要求を処理できます。このため、可能な場合は最低 2
つのプロセスを持つことを通常はお勧めしています。
物理コア数を超える複数のプロセスを持つことのもう 1 つの理由として、アプリケーションがスレッドセーフではなく、複数のスレッドを実行できない状況があります。このシナリオで 1 つのプロセスのみを実行している場合、アプリケーションで IO 呼び出し (ネットワークリクエストやデータベースクエリなど) を行っている間は、コアがアイドル状態となります。このシナリオで、余分のプロセスがあれば、IO を待機している間に別のリクエストを処理することができます。
プロセスタイプを設定するときの最後の検討事項はメモリの使用です。プロセスによるスケールアウトでは、通常の場合、スレッドよりもメモリを多く使用します。このことについての詳細は、「what is a thread」(スレッドとは) を参照してください。アプリケーションで使用するメモリが多すぎてディスクへのスワップが開始された場合、アプリケーションのパフォーマンスが劇的に低下します。プロセスカウントを調整して、R14 - Memory Quota Exceeded (メモリ割り当てを超過しました) エラーが発生しないようにすることを強くお勧めします。
スレッドカウント値
プロセスカウントの最適な値が見つかったら、今度はシステムのスレッドカウントを調整することができます。Ruby (MRI) プロセス内の複数のスレッドにより、IO (データベース呼び出しやネットワーク呼び出しなど) が関与するときにアプリが一度に複数のリクエストを処理できるようになります。オペレーティングシステムではスレッド間でコンテキストの切り替えを行う方が「低コスト」であるほか、メモリが共有されるため、一般的にプロセスよりも全体としてメモリの消費量が少なくなります。システムにスレッドを追加すると Ruby アプリのメモリ使用量が経時的に増加しますが、これはたいていの場合スレッド数を調整する際の主な懸念事項ではありません。
各プロセスタイプに対してスレッド数を同じにすることをお勧めします。5 を採用した理由の 1 つは、これが Active Record 接続プールのデフォルト値であることですが、もう 1 つの理由は、この値で「十分である」ことが研究によって示されているためです。
I/O が多いワークロードで 5 から 10 のスレッドが最適であるということは、CRuby では極めて典型的です。Appfolio: スレッド、プロセス、およびファイバー
スレッドカウントを調整するための dyno の負荷 (Performance dyno に関する) - Heroku では、CPU の使用率を理解するために Dyno Load メトリクス が提供されています。理想的には、スレッドカウント全体 (プロセス数×スレッド数) を調整するとき、アプリケーションで利用可能なすべての CPU 時間を使用する必要があります。このメトリクスの負荷の値は、ある特定の時点で待機または実行中のタスクまたはスレッドの数を表しています。この数が多い場合、スレッドの発生が多すぎて、アプリケーションに競合が発生していることを示しています。この数が物理コア数を上回ることがほとんどない場合、アプリケーションはスレッドの追加によるメリットを受ける可能性があります。
詳細については、「What is an acceptable amount of Dyno load?」(許容可能な Dyno の負荷の量) を参照してください。
サンプルコード
オープンソースの CodeTriage プロジェクトは Puma を使用するため、Puma 設定ファイルを repo で見ることができます。