スレッドプール設定の動的更新APIを追加
Pumaに、サーバー稼働中にスレッド数の最小値・最大値を動的に変更できる新しいAPIが追加されました。これにより、負荷状況に応じたスレッドプールの自動調整が可能になります。
背景
これまでPumaのスレッド数は起動時の設定で固定されており、稼働中に変更する公式な手段がありませんでした。#3658では、負荷に応じて最適なスレッド数を自動的に調整するアルゴリズムの実装に向けて、スレッド数を動的に更新するAPIが提案されています。
PR作成者の調査によると、Pumaの ThreadPool は既に @min と @max を参照してスレッドの生成・削減を行う仕組みを持っているため、この値を更新するだけで動的な調整が実現できることが判明しました。
既存のアーキテクチャを活用することで、大規模な変更なしに動的調整機能を実現できる設計判断となっています。
技術的な変更
Server#update_thread_pool_min_max メソッドが新たに追加され、スレッドプールの最小値・最大値を更新できるようになりました。
def update_thread_pool_min_max(min: @min_threads, max: @max_threads)
if min > max
@log_writer.log "`min' value cannot be greater than `max' value."
return
end
if min < 0
@log_writer.log "`min' value cannot be less than 0"
return
end
@thread_pool&.with_mutex do
@thread_pool.min, @thread_pool.max = min, max
@min_threads, @max_threads = min, max
end
end
このメソッドは以下の検証を行います:
-
minがmaxより大きい場合は警告を出力して更新を中止 -
minが0未満の場合は警告を出力して更新を中止 - 検証を通過した場合、mutexで保護された状態でスレッドプールと設定値の両方を更新
デフォルト引数として現在の値を使用することで、片方だけの更新も可能になっています。
プラグイン向けの制御インターフェース
ServerPluginControl クラスが新設され、プラグインがサーバーの内部状態に直接アクセスせずに設定を変更できるファサードパターンが採用されました。
class ServerPluginControl
def initialize(server)
@server = server
end
def max_threads
@server.max_threads
end
def min_threads
@server.min_threads
end
def update_thread_pool_min_max(min: max_threads, max: min_threads)
@server.update_thread_pool_min_max(min: min, max: max)
end
end
このクラスは before_thread_start フックのコールバックに渡されるようになり、プラグインから安全にスレッド設定を変更できます。
@before_thread_start.each do |b|
begin
b[:block].call(ServerPluginControl.new(@server))
rescue Exception => e
STDERR.puts "WARNING before_thread_start hook failed with exception (#{e.class}) #{e.message}"
end
end
ThreadPoolの変更
ThreadPool に min と max のアクセサが追加され、外部から値を更新できるようになりました。
attr_accessor :min, :max
同時に pool_capacity の計算ロジックが修正され、負の値にならないよう clamp メソッドで下限が0に設定されています。
変更前:
def pool_capacity
waiting + (@max - spawned)
end
変更後:
def pool_capacity
(waiting + (@max - spawned)).clamp(0, Float::INFINITY)
end
これは、@max が動的に縮小された際に spawned が一時的に @max を超える状況で、計算結果が負になることを防ぐための防御的な変更です。
設計判断
ThreadPoolの内部状態を直接変更する方式 が採用されました。
PRの説明では「少し怖いかもしれないが、動的更新とはそういうものなので問題ないと考えた」と述べられています。既存のスレッド管理ロジックが @min と @max を定期的に参照する設計になっているため、値を更新するだけで既存のスレッド生成・削減機能がそのまま機能します。
新たなスレッド管理ロジックを追加するのではなく、既存の仕組みを活用することで、変更を100行以内に抑えつつ機能を実現しています。一方で、with_mutex によるロック保護を導入することで、並行更新時の整合性を確保する配慮がされています。
まとめ
本PRは、Pumaのスレッドプール設定を稼働中に変更できるAPIを追加した変更です。既存のスレッド管理ロジックを活用し、最小限の変更で動的調整機能の基盤を提供しています。プラグイン向けの制御インターフェースを分離することで、将来的な負荷適応型スレッド管理の実装への道を開いています。