パラメータエンコーディングをデフォルトでRactorセーフにする
ActionController の parameter_encodings を Ractor 環境でも安全に共有できるようにし、既存挙動を保ちつつ内部実装を不変オブジェクトに置き換えました。
背景
この変更は ActionController::Base.parameter_encodings を Ractor で安全に扱えるようにすることを目的としています。Rails ではこのハッシュがアクションごとにパラメータのエンコーディングを保持し、未宣言のキーに対してはデフォルトプロックで自動生成していました。Ractor は共有オブジェクトのミュータブルな状態を許容しないため、ハッシュが凍結されているとデフォルトプロックが機能しなくなります。そこでハッシュ自体を不変にし、必要な更新は新しいハッシュを作成して置き換える方針としました。
技術的な変更
_parameter_encodings へのリーダー追加
モジュールに attr_reader :_parameter_encodings を導入し、テストから内部ハッシュを参照できるようにしました。リーダーは外部公開 API ではなくテスト専用で、既存の動作に影響を与えません。これにより shareability の検証が容易になります。
初期化処理の変更
setup_param_encode では @_parameter_encodings を ActiveSupport::Ractors.make_shareable(Hash.new({})) で初期化します。空ハッシュを凍結かつ Ractor 共有可能にすることで、デフォルトプロックは不要となりハッシュは不変になります。結果としてハッシュへの直接ミューテーションが排除されます。
skip_parameter_encoding の実装
このメソッドは従来 @_parameter_encodings[action] = Hash.new { Encoding::ASCII_8BIT } の形でミューテートしていましたが、変更後は ActiveSupport::Ractors.make_shareable を使って新しいハッシュをマージし、再代入しています。具体的には Hash.new(Encoding::ASCII_8BIT) を作成し、対象アクションに紐付けた上で全体を shareable にします。ミューテーションが排除され、Ractor 間で安全に共有できます。
param_encoding の実装
個別パラメータのエンコーディング設定も同様に不変オブジェクトの置き換えで行います。引数を文字列に変換した後、対象アクションのハッシュを merge(param => encoding) で更新し、全体ハッシュを make_shareable で再構築します。これにより従来と同等の API を保ちつつ、内部データは常に凍結状態です。
テストの強化
テストファイルに ActiveSupport::Testing::RactorsAssertions を導入し、assert_ractor_shareable ParameterEncodingController._parameter_encodings で共有可能性を検証するケースを追加しました。従来のエンコーディング動作テストはそのまま残し、Ractor セーフティを新たに保証します。
設計判断
不変オブジェクトと make_shareable の組み合わせを採用したのは、Ractor が要求する共有オブジェクトの不変性を最小の侵入で満たすためです。ハッシュを凍結しつつ、必要な更新は新しいインスタンスを生成して置き換えることで、競合やデータ破壊のリスクを排除しました。これにより Rails 全体のスレッド/Ractor 安全性基準と合致します。
さらに公開 API(skip_parameter_encoding、param_encoding など)は変更せず、呼び出し側のコードはそのまま動作します。内部実装だけが immutable に変わるため、既存アプリケーションへの影響はありません。設計上のトレードオフは、ハッシュ生成のコストが若干増える点ですが、Ractor 環境での安全性向上という利益が上回ります。
まとめ
ActionController::Base.parameter_encodings を常に凍結かつ Ractor 共有可能な状態にし、ミューテーションを排除した実装に置き換えることで、Ractor 環境でも安全にパラメータエンコーディングを管理できるようになりました。API 互換性は維持され、テストにより shareability が保証されています。