定数の Ractor 共有化のための freeze と shareable ヘルパー追加

rails/rails

Rails は Ractor 対応を進める中で、定数に保持された可変オブジェクトが Ractor 間で安全に共有できない問題を解消するため、数箇所で freeze と shareable ヘルパーを導入しました。これにより、挙動を変えることなく Ractor‑safe な実装が可能になります。

背景

Ractor へ移行する際の最重要課題は、共有メモリ上のオブジェクトが不変であることを保証することです。フレームワーク内部で定数として保持されているハッシュや Proc はデフォルトでミュータブルであり、Ractor 間で共有すると例外が発生します。今回の PR は、テストアプリの開発で見つかった数件の可変定数を対象に、freeze または ActiveSupport::Ractors 系のヘルパーで不変化させる作業です。変更は動作を変更しないことが前提で、既存コードへの影響を最小限に抑える設計になっています。

技術的な変更

actionpack/lib/action_controller/metal/allow_browser.rb

**SETS** 定数はブラウザバージョンの許容リストを保持していますが、内部ハッシュがミュータブルな状態でした。変更前は modern: { safari: 17.2, chrome: 120, firefox: 121, opera: 106, ie: false } というリテラルがそのまま格納されていました。

@@ -72,7 +72,7 @@ def allow_browser(versions:, block:)

         class BrowserBlocker # :nodoc:
           SETS = {
-          modern: { safari: 17.2, chrome: 120, firefox: 121, opera: 106, ie: false }
+          modern: { safari: 17.2, chrome: 120, firefox: 121, opera: 106, ie: false }.freeze
           }.freeze

           attr_reader :request, :versions

このようにハッシュ自体に .freeze を付与し、SETS 全体が完全に不変になるようにしました。結果として、Ractor で SETS を共有しても安全性が保証されます。

actionview/lib/action_view/helpers/tag_helper.rb

**PRE_CONTENT_STRINGS** はフォーム要素の前置文字列を管理するハッシュで、デフォルト値を Proc で生成していました。元の実装は Hash.new { "" } であり、内部の Proc がミュータブルでした。

@@ -41,7 +41,7 @@ module TagHelper
       TAG_TYPES.merge! CLASS_PREFIXES.index_with(:class)
       TAG_TYPES.freeze

-      PRE_CONTENT_STRINGS             = Hash.new { "" }
+      PRE_CONTENT_STRINGS             = Hash.new(&ActiveSupport::Ractors.shareable_proc { "" })
       PRE_CONTENT_STRINGS[:textarea]  = "\n"
       PRE_CONTENT_STRINGS["textarea"] = "\n"
       PRE_CONTENT_STRINGS.freeze

Hash.newActiveSupport::Ractors.shareable_proc を渡すことで、生成される Proc が Ractor‑shareable かつ不変となります。さらに定数全体を freeze しているため、他のスレッドや Ractor が参照しても状態が保護されます。

activesupport/lib/active_support/log_subscriber.rb

**LEVEL_CHECKS** はロガーのログレベル判定をラムダで保持しており、各ラムダがミュータブルでした。変更により、ActiveSupport::Ractors.shareable_lambda を使用して不変ラムダに置き換えました。

@@ -67,9 +67,9 @@ class LogSubscriber < Subscriber
     class_attribute :log_levels, instance_accessor: false, default: {} # :nodoc:

     LEVEL_CHECKS = {
-      debug: -> (logger) { !logger.debug? },
-      info: -> (logger) { !logger.info? },
-      error: -> (logger) { !logger.error? },
+      debug: ActiveSupport::Ractors.shareable_lambda(&-> (logger) { !logger.debug? }),
+      info: ActiveSupport::Ractors.shareable_lambda(&-> (logger) { !logger.info? }),
+      error: ActiveSupport::Ractors.shareable_lambda(&-> (logger) { !logger.error? }),
     }.freeze

     class << self

shareable_lambda により各ラムダが Ractor で安全に共有でき、LEVEL_CHECKS 全体が freeze されているため不変性が保証されます。

設計判断

今回の変更は freezeActiveSupport::Ractors.shareable_* 系ヘルパーを組み合わせて、定数の不変化と Ractor 共有可能性を同時に満たす方針です。freeze は最もシンプルで既存コードへの影響が最小ですが、Proc や Lambda の場合は単に freeze できないため、shareable ヘルパーが採用されました。これにより、可変オブジェクトを安全に Ractor 間で参照でき、将来的な Ractor 対応拡張が容易になります。また、定数自体を freeze したまま残すことで、意図しない再代入や変更を防止し、コードベース全体の不変性を強化しています。

まとめ

本 PR は、Rails の定数に対し freezeActiveSupport::Ractors.shareable_proc / shareable_lambda を導入し、Ractor で安全に共有できるようにした変更です。挙動は変えずに不変性を保証することで、フレームワーク全体の Ractor‑safe 化に寄与しています。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
ce80e5e3

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文→背景→技術的な変更→設計判断→まとめの3部構成が明確に存在し、各要素が充実しています。

カスタムMarkdown構文 ⚠ WARNING

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

コードブロックのファイル名付きシンタックスハイライトは正しく使用されていますが、GitHubリンクが '[PR #57752](URL)' の形式であり、要求された '[#57752](URL)' の形式になっていません。

対象読者への適合性 ✓ PASS

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

専門的なエンジニア向けの内容で、余計な初心者向け解説はなく適切です。

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

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

各セクションは総論→各論→結論の流れで構成され、段落はトピックセンテンスで始まり、1段落1トピック、適切な長さで空行で区切られています。

Diff内容との照合 ✓ PASS

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

記事中のコードブロックは提供されたDiffと完全に一致しており、変更点が正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

freeze、shareable_proc、shareable_lambda、Ractor などの用語は正しく使用され、PRの記述と一致しています。

説明の技術的正確性 ✓ PASS

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

技術的な説明はDiffの変更内容と合致し、因果関係も論理的です。

事実の突合 ✓ PASS

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

記事の主張はすべてPR情報とDiffで裏付けられており、根拠のない推測はありません。

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

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

コード内の数値(例: Safari 17.2, Chrome 120 等)はDiffと一致し、誤りはありません。

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

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

記事タイトルはPRの内容(定数の shareable 化)を適切に表現しており、矛盾はありません。

外部知識の正確性 ✓ PASS

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

記事はPRに記載されている範囲外のバージョンサポート情報やベンチマーク等を含んでいません。

時間表現の正確性 ✓ PASS

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

時間表現は使用されておらず、PRの記述と食い違いはありません。