`Configuration#clamp` の二重呼び出しを修正し、冪等性を保証

puma/puma

LauncherConfiguration#clampinitializerun の両方で呼び出していたバグを修正し、single {} / cluster {} ブロックのコールバックが二重実行される問題を解消しました。あわせて clamp に冪等性ガードを追加し、重複呼び出しをdeprecation warningとして検出できるようにしています。

背景

#3935 で報告されたこの問題の本質は、clamp が副作用を持つ処理を内包していることにあります。Configuration#clamp は内部で run_mode_hooks を呼び出しており、single {}cluster {} ブロックに登録されたコールバックを実行します。Launcher#initialize でのクランプ後、Launcher#run でも再びクランプが呼ばれていたため、これらのコールバックが2回実行されていました。

initialize 側のクランプは必須です。直後の @config.options 参照が NotClampedError を発生させないようにするために必要であり、run 側の呼び出しは完全に冗長でした。initialize から run の間に行われる設定変更はすべて同一のミュータブルなオプションハッシュへの書き込みであり、クランプ前のDSL状態には影響しないため、run 側を削除しても安全です。

Launcher を生成する3つの呼び出し元(CLIRack::Handler::Puma、テストヘルパー)はいずれも initialize を経由するため、この修正でカバーされないパスは存在しません。

技術的な変更

変更は launcher.rb からの冗長コード削除と、configuration.rb への冪等性ガード追加の2点に集約されます。

Launcher#run から clamp 呼び出しを削除:

 def run
   previous_env = get_env

-  @config.clamp
-
   @config.plugins.fire_starts self

Configuration#clamp に二重呼び出し検出ガードを追加:

def clamp
  if @clamped
    warn "Configuration already clamped, calling clamp multiple times is deprecated and will raise in Puma v9"
    return options
  end

  load unless @loaded
  run_mode_hooks
  set_conditional_default_options
  # ...
end

@clamped フラグが立っている場合は warn を出力して即座に options を返すことで、Puma v9での raise への移行を段階的に案内します。この挙動を保証するテスト test_clamp_warns_if_called_twice と、v9でdeprecation warningをraiseに置き換えることを自動検出する test_double_clamp_is_removed_in_v9Gem::Version を使ったバージョンアサーション)も追加されています。

あわせて、run_mode_hooks 内のエラーメッセージ先頭が小文字だった点を大文字に統一しています("cannot change...""Cannot change...")。

テストコードでは test_cli.rb の4箇所から手動 conf.clamp 呼び出しが削除されました。これらは Puma::CLI.new の内部で既にクランプが完了しているため不要だったものです。

設計判断

「削除」と「ガード」を組み合わせたアプローチが採用されました。根本的な修正は run からの削除のみで完結しますが、今後同様のコードが混入した際にサイレントに副作用が二重実行されるリスクに備え、clamp 自体を防御的にしています。

deprecation warning → Puma v9での raise という段階的な移行戦略は、既存のコードベースに手動 clamp 呼び出しが残っている場合でも互換性を保ちながら問題を顕在化させます。test_double_clamp_is_removed_in_v9 でバージョン条件チェックをテストとして記述しておくことで、v9リリース時にwarningをraiseへ変更する作業の見落としを防ぐ仕組みになっています。

まとめ

この修正は、clamp の冗長呼び出しという1行の削除に留まらず、設定の冪等性を @clamped フラグとdeprecation mechanismで制度として保証した変更です。Puma v9への移行パスを見据えたテストと段階的警告により、今後の重複呼び出しも安全に検出・排除できる基盤が整いました。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
a30214b8

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)という理想的な構成で書かれており、各セクションの役割も明確です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```diff:lib/puma/launcher.rb```)、Issue番号のリンク記法([#3935](...))など、カスタムMarkdown構文が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Pumaの内部実装に関する専門的な内容であり、冗長な説明がなく、対象読者であるエンジニアに適切です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論から始まる構成になっており、各段落もトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

`launcher.rb`からのコード削除や`configuration.rb`へのガード節追加など、記事で引用されているコードは提供されたDiff内容と正確に一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`clamp`, `NotClampedError`, `冪等性`など、PRで使われている技術用語や一般用語が文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

二重呼び出しの原因、`initialize`側の呼び出しの必要性、`run`側が冗長である理由など、技術的な説明はPR情報と整合しており、論理的で正確です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(二重呼び出しの発生、冪等性ガードの追加、v9での変更予定など)は、PRのDescriptionやDiff内容で裏付けられており、ハルシネーションは検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#3939)、関連Issue番号(#3935)、バージョン(Puma v9)など、すべての数値・固有名詞が正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルはPRの主題である「二重呼び出しの修正」を正確に反映しつつ、「冪等性の保証」という技術的意義を付け加えており、内容と完全に一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事で言及されている`Puma v9`に関する情報は、PRのDescriptionやDiff内の警告文に記載されているものであり、PRに基づかない外部知識の追加はありません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

Puma v9で`raise`に移行するという未来の予定について、PRの「will become」という表現と一致しており、時間表現は正確です。