Make parameter parsers assignment Ractor safe
デフォルトの parameter_parsers が Ractor 間で安全に共有できるよう、ハッシュの割り当て方法と不変化処理を追加しました。これにより、Ractor 環境でのパラメータ解析が安全に実行されます。
背景
デフォルトパーサ集合は以前のコミット f2be45a で Ractor 安全化されましたが、割り当て時にハッシュが変換されてしまい、freeze が行われていませんでした。Ractor では mutable なオブジェクトは共有できず、未凍結のハッシュはエラーの原因になります。
parameter_parsers= メソッドは受け取ったハッシュを変形した上で代入しますが、変形後のオブジェクトは凍結されず、他の Ractor から参照された場合に安全性が損なわれます。この問題はデフォルト設定だけでなく、ユーザーがカスタムパーサを設定する際にも潜在的に顕在化します。
したがって、ハッシュを割り当てる前に freeze し、さらにデフォルト設定は不必要なコピーを避けて直接インスタンス変数に格納する必要があります。
技術的な変更
ActionDispatch::Http::Parameters のクラス初期化部とセッターを修正し、ハッシュを freeze した上で直接 @parameter_parsers に代入するようにしました。これにより、ハッシュは Ractor 間で安全に共有できます。
変更前:
class << self
attr_reader :parameter_parsers
end
self.parameter_parsers = DEFAULT_PARSERS
変更後:
class << self
attr_reader :parameter_parsers
end
@parameter_parsers = DEFAULT_PARSERS
また、カスタム設定用のセッターも凍結処理を追加しました。
def parameter_parsers=(parsers)
@parameter_parsers = parsers.transform_keys { |key| key.respond_to?(:symbol) ? key.symbol : key }.freeze
end
この変更により、デフォルトハッシュは直接代入され、コピーが発生せず freeze が保証されます。さらに、ユーザーが提供するハッシュはキー正規化後に凍結され、Ractor 安全性が確保されます。
設計判断
デフォルトパーサは内部的に正しい形状であることが保証されているため、公開 API を経由せず直接インスタンス変数に設定する方針が採られました。これにより余計なハッシュコピーを回避し、パフォーマンスと安全性を同時に向上させられます。
一方で、カスタムパーサを設定する際には公開セッター parameter_parsers= を維持し、キー正規化と freeze を行うことで既存の外部インターフェースはそのまま利用可能です。これにより、後方互換性を損なうことなく安全性を強化しています。
この設計は「安全性の確保」と「最小限の侵入性」のトレードオフを意識し、内部実装の最適化と外部 API の一貫性を両立させた選択と言えます。