ActiveRecord の readonly 属性を Ractor セーフに
ActiveRecord::Base の _attr_readonly がデフォルトで Ractor 共有可能 になるよう変更され、Ractor 環境でも安全に利用できるようになりました。
背景
Ractor 対応作業では、クラス属性として保持している 可変オブジェクト(Hash や Array)が複数スレッド間で共有できないことが障壁となります。ActiveRecord::Base._attr_readonly も同様に配列で保持されており、凍結されていないため Ractor 間でコピーが必要でした。以前に例外ラッパー設定を Ractor セーフにした作業(#57483)と同様のアプローチで、readonly 属性の共有可能化が行われました。
この変更により、属性追加時に配列を dup → freeze する代わりに、ActiveSupport::Ractors.make_shareable で共有可能オブジェクトに変換する方針が採られました。
技術的な変更
attr_readonly の実装変更
@@
- self._attr_readonly |= attributes.map(&:to_s)
+ self._attr_readonly = ActiveSupport::Ractors.make_shareable(_attr_readonly | attributes.map(&:to_s))
_attr_readonly に属性を追加する際、従来の OR 演算子で配列を拡張 した処理を、ActiveSupport::Ractors.make_shareable でラップ した形に置き換えました。これにより、結果の配列は Ractor で安全に共有可能となります。
テスト補助の拡充
Ractor 共有可能性を検証するためのアサーションヘルパーが二つ追加されました。
@@
- def assert_ractor_shareable(obj)
- assert_nothing_raised { Ractor.make_shareable(obj) }
- end
+ def assert_ractor_make_shareable(obj)
+ assert_nothing_raised { Ractor.make_shareable(obj) }
+ end
+
+ def assert_ractor_shareable(obj)
+ assert Ractor.shareable?(obj), "Expected #{obj.inspect} to be shareable, but it is not."
+ end
Ruby 4.0 以上では実際に Ractor.make_shareable と Ractor.shareable? を呼び出して検証し、古いバージョンではダミー実装が提供されます。
テストケースの追加
ActiveSupport::Testing::RactorsAssertions を BasicsTest にインクルードし、_attr_readonly が共有可能であることを確認するテストが追加されました。
@@
include ActiveSupport::Testing::RactorsAssertions
@@
def test_readonly_attributes_are_ractor_safe
assert_ractor_shareable ReadonlyAuthorPost._attr_readonly
end
また、バックトレースクリーナーのテストでも assert_ractor_make_shareable を使用するよう変更されています。
設計判断
変更点のスコープ
-
属性追加時に共有可能化: 既存の
attr_readonly呼び出しコードはそのまま動作し、内部だけが Ractor 対応になるため、後方互換性が保たれます。 -
make_shareableのみ使用: 配列全体を凍結してから保存する従来の手法ではなく、ActiveSupport::Ractors.make_shareableに委譲することで、将来的に他の Ractor 共有ロジックと統一できます。
アサーションの二分化
assert_ractor_shareable と assert_ractor_make_shareable を分けたのは、「既に共有可能か」 と 「共有可能にできるか」 を明示的に区別する設計意図があるためです。これにより、テストが意図する検証内容が明確になります。
まとめ
_attr_readonly を ActiveSupport::Ractors.make_shareable で包むことで、ActiveRecord の readonly 属性が Ractor 環境でも安全に共有できるようになりました。テスト支援も整備されたため、今後の変更が Ractor 互換性を損なうリスクが低減します。既存コードへの影響はなく、Rails の Ractor 対応がさらに前進したと言えるでしょう。