AbstractController::Caching の view_cache_dependency を Ractor セーフに
AbstractController::Caching の view_cache_dependency が Ractor 共有可能な Proc に変換され、Ractor 環境でも安全に利用できるようになりました。これにより、Rails のキャッシュ依存関係宣言がマルチスレッド・マルチプロセスの新しい実行モデルと整合します。
背景
Ractor は Ruby の並行実行モデルで、オブジェクトを共有する際に明示的な共有可能性が求められます。AbstractController::Caching.view_cache_dependency はブロック(Proc)を受け取り、内部で _view_cache_dependencies 配列に保持し、後に instance_exec で実行しますが、従来はその Proc が共有可能かどうかのチェックが行われていませんでした。
その結果、Ractor が有効な環境でキャッシュ依存関係を宣言すると、未共有の Proc が Ractor 境界を越えて渡され、Ractor::IsolationError が発生するリスクがありました。既存の実装は class_attribute :_view_cache_dependencies, default: [] とし、配列を直接 += で拡張していましたが、配列自体もミュータブルであるため安全性が確保できませんでした。
この問題を放置すると、Ractor 移行を検討するアプリケーションでキャッシュ機構が利用できず、パフォーマンスやスケーラビリティの恩恵を受けられないという実務的な障壁が残ります。そこで、view_cache_dependency を Ractor セーフに改修する必要が生じました。
技術的な変更
_view_cache_dependencies は class_attribute として定義され、デフォルト値を [].freeze に変更しました。さらに view_cache_dependency の実装を、既存の配列に対するミュータブルな追加から、フリーズされた新しい配列を生成して置き換える形に刷新し、追加される Proc には ActiveSupport::Ractors.try_shareable_proc を通すことで共有可能性を保証します。
変更前:
class_attribute :_view_cache_dependencies, default: []
module ClassMethods
def view_cache_dependency(&dependency)
self._view_cache_dependencies += [dependency]
end
end
変更後:
class_attribute :_view_cache_dependencies, default: [].freeze
module ClassMethods
def view_cache_dependency(&dependency)
self._view_cache_dependencies = [
*_view_cache_dependencies,
ActiveSupport::Ractors.try_shareable_proc(dependency)
].freeze
end
end
この改修により、_view_cache_dependencies が常に凍結された配列となり、変更時に新しい配列を生成して置き換えるため、参照の競合が起きません。また、try_shareable_proc が返す Proc は Ractor 境界で安全に共有できることが保証され、非共有 Proc が渡された場合は例外が発生して早期に問題を検出できます。既存の非 Ractor 環境では挙動に差異がなく、互換性が保たれます。
設計判断
class_attribute と freeze を組み合わせた設計は、Rails の設定系オブジェクトで広く採用されている不変性パターンを踏襲しています。新たに別属性を導入せず、既存の _view_cache_dependencies を拡張した点は、API の破壊を避けつつ安全性を高める狙いです。
ActiveSupport::Ractors.try_shareable_proc の使用は、Ractor 共有可能性の判定ロジックを Rails コアに委任する選択です。これにより、Proc が共有不能である場合に即座に例外を投げ、テストで検証できるようになりました(ActiveSupport::Testing::RactorsAssertions を利用)。同様の安全化は、同時期にマージされた PR #57743 での InheritableOptions#to_h の修正と同様のパターンです。
結果として、最低限のコード変更で後方互換性と Ractor 安全性の両立が実現されました。配列の凍結と Proc の変換という二重の防御が、将来的な拡張や他コンポーネントからの呼び出しに対しても堅牢な基盤を提供します。
まとめ
今回の変更は、AbstractController::Caching.view_cache_dependency を Ractor 共有可能な形に変換し、内部配列を凍結することでマルチプロセス実行時の安全性を確保した点が核心です。これにより、Rails アプリケーションはキャッシュ依存関係を宣言したまま Ractor 環境へ移行でき、追加の回避策を講じる必要がなくなります。
テストスイートには ActiveSupport::Testing::RactorsAssertions を用いた検証が追加され、_view_cache_dependencies が Ractor 共有可能であることを自動的に確認しています。変更は既存コードへの影響を最小限に抑えつつ、Rails が提供する新しい並行実行モデルへの対応を前倒しで実装した好例と言えるでしょう。