Ractor安全化のためのプロック共有機構を導入
Rails が Ractor 対応を進める中で、アプリケーションが提供する Proc が Ractor 共有可能であるかどうかを判定し、必要に応じて安全化する仕組みが追加されました。これにより、非共有プロックが原因で発生する Ractor::IsolationError を事前に検知し、適切に対処できるようになります。
背景
Rails アプリケーションが Ractor セーフであることは、マルチスレッド実行環境での安定運用に不可欠です。しかし、Rails.application.routes.draw などの API に Proc を渡すケースでは、内部でその Proc が共有されず Ractor::IsolationError が発生することがあります(例: resolve("Cart") { to_resolve })。この問題はエラーメッセージが遅延して現れるため、デバッグが困難です。
本 PR は、プロックの共有可否を明示的にチェックし、三つのモードで振る舞いを選択できる仕組みを提供します。デフォルトはオプトインで無効化されており、既存アプリへの影響を最小限に抑えつつ、将来的に安全化機能を段階的に導入できる設計となっています。
技術的な変更
ActiveSupport::Ractors に新たにアクセサ unshareable_proc_action とヘルパーメソッド try_shareable_proc が追加されました。このメソッドは渡された Proc を Ractor.make_shareable で共有化しようと試み、Ractor::IsolationError が発生した場合に :raise、:warn、nil の設定に応じて例外を再送出、デプリケーション警告を出す、何もしないのいずれかの動作を取ります。
module ActiveSupport
module Ractors
class << self
attr_accessor :unshareable_proc_action
def try_shareable_proc(proc = nil, &block)
proc ||= block
return proc unless unshareable_proc_action
shareable_proc(&proc)
rescue Ractor::IsolationError
case unshareable_proc_action
when :raise
raise
when :warn
ActiveSupport.deprecator.warn(<<~MSG)
Rails attempted to make a Proc from your application Ractor shareable but a Ractor
Isolation error was raised. The proc being returned is not Ractor safe and a runtime
error may occur anytime during the request lifecycle.
#{proc.inspect}
MSG
proc
end
end
ActionDispatch::Routing::RouteSet の URL ヘルパー生成ロジックが修正され、ActiveSupport::Ractors.try_shareable_proc がプロック取得時に呼び出されるようになりました。これにより、ルートヘルパー定義時点でプロックの安全性が検証されます。
class CustomUrlHelper
def initialize(name, defaults, &block)
@name = name
@defaults = defaults
- @block = block
+ @block = ActiveSupport::Ractors.try_shareable_proc(block)
end
テストスイートが拡充され、unshareable_proc_action の三モードすべてに対する期待動作が検証されています。RouteSetTest では resolve メソッドに非共有プロックを渡した際の例外、警告、無視の挙動が確認され、RactorsTest では try_shareable_proc の内部ロジックが単体テストで網羅されています。
設計判断
本機構はオプトイン方式で実装され、デフォルトは無効 (nil) です。これにより、既存アプリケーションが即座に破壊されるリスクを回避しつつ、開発者が意図的に安全化を有効化できるポイントを提供しています。unshareable_proc_action の設定はグローバルに行えるため、環境ごとに挙動を切り替えることが容易です。
例外を即座に通知する :raise と、デプリケーション警告で緩やかに導入する :warn の二択は、移行期間の柔軟性を確保する設計上のトレードオフを示しています。:raise は開発・テスト環境での早期検出に適し、:warn は本番環境での互換性維持と段階的リファクタリングを支援します。いずれのモードでもコード変更は最小限に抑えられ、既存 API との互換性が保たれています。