[solid_queue] ワーカープロセス終了時のNoMethodError修正
背景
Solid QueueのワーカープロセスがSIGTERMやSIGKILLなどのシグナルで終了した際、status.exitstatusがnilを返すケースがあります。このとき、条件式の演算子優先順位の問題によりNoMethodError: undefined method '>' for nilが発生していました。
この問題は、フォークされたワーカープロセスの終了状態を監視するForkSupervisorクラスで発生しており、プロセス管理の信頼性に影響を与えていました。
技術的な変更内容
問題のあったコード
変更前のコードでは、演算子の優先順位により意図しない評価順序になっていました。
if (terminated_fork = process_instances.delete(pid)) && !status.exited? || status.exitstatus > 0
error = Processes::ProcessExitError.new(status)
release_claimed_jobs_by(terminated_fork, with_error: error)
end
このコードは以下のように評価されます:
if ((terminated_fork = process_instances.delete(pid)) && !status.exited?) || (status.exitstatus > 0)
右辺のstatus.exitstatus > 0が単独で評価されるため、exitstatusがnilの場合にNoMethodErrorが発生します。
修正後のコード
if (terminated_fork = process_instances.delete(pid)) && (!status.exited? || status.exitstatus.to_i > 0)
error = Processes::ProcessExitError.new(status)
release_claimed_jobs_by(terminated_fork, with_error: error)
end
修正のポイント:
-
括弧の追加:
(!status.exited? || status.exitstatus.to_i > 0)を括弧で囲むことで、terminated_forkが存在する場合のみこの条件が評価されるようにしました -
to_iの使用:status.exitstatus.to_iとすることで、nilの場合は安全に0に変換されます
動作の違い
シグナルによるプロセス終了時のProcess::Statusの挙動:
# 通常の終了 (exit 1)
status.exited? # => true
status.exitstatus # => 1
# SIGTERMによる終了
status.exited? # => false
status.exitstatus # => nil
status.signaled? # => true
status.termsig # => 15 (SIGTERM)
修正後は、exitstatusがnilの場合でもnil.to_iにより0となり、エラーを回避しつつ適切な処理フローを維持できます。
影響範囲
この修正により、以下のシナリオで安定性が向上します:
- ワーカープロセスがシグナルで強制終了された場合
- コンテナ環境でのグレースフルシャットダウン
- システムリソース不足によるプロセスキル
修正はForkSupervisorのreap_terminated_forksメソッドのみに限定されており、他のコンポーネントへの影響はありません。