Pumaプラグインでのnil pid処理を追加
PumaのSolid Queueプラグインにおいて、コンテナ環境でのfork失敗時に発生する TypeError を防ぐため、プロセスID(pid)が nil の場合の早期リターンが追加されました。これにより、制約のあるコンテナ環境でもプラグインが安全に動作するようになります。
背景
Render.comなどの制約のあるコンテナ環境では、fork システムコールが暗黙的に失敗することがあります。#617 では、Pumaの起動時に no implicit conversion from nil to integer という TypeError が発生していました。fork が失敗すると solid_queue_pid が nil のまま残り、Pumaのシャットダウン時に stop_solid_queue_fork メソッドが呼ばれた際、Process.waitpid(nil) に nil が渡されてエラーが発生していました。
macOS環境でも同様の問題が報告されており、#628 では objc[37819]: +[__NSCFConstantString initialize] may have been in progress in another thread when fork() was called というメッセージとともにクラッシュが発生していました。これらは fork の失敗がSolid Queueのライフサイクル管理に影響を与えていたことを示しています。
技術的な変更
lib/puma/plugin/solid_queue.rb の stop_solid_queue_fork メソッドに、pidが nil の場合の早期リターンが追加されました。
変更前:
def stop_solid_queue_fork
Process.waitpid(solid_queue_pid, Process::WNOHANG)
log "Stopping Solid Queue..."
Process.kill(:INT, solid_queue_pid) if solid_queue_pid
Process.wait(solid_queue_pid)
rescue Errno::ECHILD, Errno::ESRCH
end
変更後:
def stop_solid_queue_fork
return unless solid_queue_pid
Process.waitpid(solid_queue_pid, Process::WNOHANG)
log "Stopping Solid Queue..."
Process.kill(:INT, solid_queue_pid)
Process.wait(solid_queue_pid)
rescue Errno::ECHILD, Errno::ESRCH
end
メソッドの先頭に return unless solid_queue_pid を配置することで、pidが nil の場合は以降の処理をスキップします。これにより、Process.waitpid に nil が渡されることがなくなり、TypeError が防止されます。また、Process.kill の条件分岐 if solid_queue_pid が削除されました。これは早期リターンによって pid が必ず有効な値であることが保証されるため、冗長な条件チェックとなったためです。
設計判断
メソッド先頭での早期リターンパターン が採用されました。
この変更により、メソッド本体のロジックはすべて「pidが有効な場合」という前提で記述できるようになり、各処理ステップでの条件分岐が不要になります。Process.kill の条件チェックを削除できたことは、この設計判断の効果を示しています。ガード節によってメソッドの前提条件を明示し、正常系のコードをネストなく記述できる点で、可読性と保守性の向上が図られています。
また、rescue 節で Errno::ECHILD と Errno::ESRCH をキャッチする既存の設計は維持されています。これは、プロセスが既に終了している場合や存在しない場合のエラーをすでに考慮していたことを示しており、今回の変更は「プロセスが起動していない場合」というより上流の状態を扱うものといえます。
まとめ
本PRは、コンテナ環境での fork 失敗に対するロバスト性を向上させる変更です。pidが nil の場合の早期リターンを追加することで、制約のある環境でもPumaプラグインが安全に動作し、シャットダウン時のクラッシュを防ぎます。冗長な条件分岐の削除により、コードの意図もより明確になりました。