シングルモードとクラスタモードの設定フックを追加
Pumaに、実行モードに応じて条件分岐できる single と cluster 設定フックが追加されました。これにより、フォーク非対応のライブラリを扱うアプリケーションでも、単一の設定ファイル内でモード別の初期化処理を記述できるようになります。
背景
JVMをRuby-Java Bridge(RJB)経由で読み込むアプリケーションでは、JVMのフォーク非対応という制約により初期化タイミングの制御が必要でした。#3571で報告されたように、クラスタモードでは on_worker_boot 内でJVMを読み込む必要がある一方、シングルモードでは別の初期化処理が必要になります。
従来は環境変数でモードを判定するか、設定ファイルを分離する必要がありました。Pumaは lib/puma/dsl.rb 内部で @options[:workers] の値を基にモード判定を行っていましたが、これは公開APIではなく、ユーザーコードから利用できませんでした。単一の config/puma.rb で両モードに対応する設定を記述する手段が求められていました。
技術的な変更
lib/puma/dsl.rb に single と cluster の2つのDSLメソッドが追加されました。これらはブロックを受け取り、実行モードに応じて評価されます。
def single(&block)
raise ArgumentError, "A block must be provided to `single`" unless block
@options[:single] ||= []
@options[:single] << block
end
def cluster(&block)
raise ArgumentError, "A block must be provided to `cluster`" unless block
@options[:cluster] ||= []
@options[:cluster] << block
end
両メソッドはブロックを配列に格納し、後で順次実行する仕組みです。ブロックが渡されない場合は ArgumentError を発生させます。
lib/puma/configuration.rb の clamp メソッドに、新しい run_mode_hooks メソッドの呼び出しが追加されました。
def clamp
load unless @loaded
run_mode_hooks
set_conditional_default_options
@_options.finalize_values
@clamped = true
end
def run_mode_hooks
workers_before = @_options[:workers]
key = workers_before > 0 ? :cluster : :single
@_options.all_of(key).each(&:call)
unless @_options[:workers] == workers_before
raise "cannot change the number of workers inside a #{key} configuration hook"
end
end
run_mode_hooks は、設定ファイル読み込み後かつオプション確定前のタイミングで実行されます。:workers の値でモードを判定し、対応するフックブロックを順次実行します。フック内で :workers の値が変更された場合はエラーを発生させ、モード判定の整合性を保ちます。
使用例は以下のようになります:
single do
# シングルモード時のみ実行
silence_fork_callback_warning
end
cluster do
# クラスタモード時のみ実行
prune_bundler
end
設計判断
設定ファイル内での条件分岐をサポートする方式が採用されました。
代替案として、環境変数による外部からの制御や、モード別の設定ファイル分離が考えられました。しかし、本実装では config/puma.rb 内で完結する条件分岐を選択しています。これにより、モード判定ロジックとそれに基づく設定を同一ファイル内で管理できます。
フックの実行タイミングは clamp メソッド内、具体的には set_conditional_default_options の前に配置されました。これは、フック内で設定した値がデフォルト値の条件判定に影響を与えられるようにするためです。例えば、cluster ブロック内で prune_bundler を設定した場合、その値が :preload_app のデフォルト値計算に反映されます。
:workers の値変更を禁止する制約も重要な設計判断です。フック実行前の :workers の値でモードが決定されるため、フック内での変更を許すとモード判定の前提が崩れます。テストでは、この制約違反時に適切なエラーが発生することが検証されています。
本PRは、Pumaの内部で既に使用されていたモード判定ロジックを公開APIとして整備した変更といえます。@options[:workers] > 0 という単純な条件判定を、ユーザーコードから安全に利用できる形でカプセル化しています。JVMのようなフォーク非対応ライブラリの初期化タイミング制御という具体的なユースケースに対し、環境変数や設定ファイル分離に頼らない解決策を提供しました。