Rails 6.1 Active Record Marshalフォーマットのサポート終了
Active Recordオブジェクトのシリアライズに使われていたRails 6.1形式のMarshalフォーマットが削除され、7.1形式のみがサポートされるようになりました。これにより、Attribute / AttributeSetといった内部APIを後方互換性を気にせず進化させられるようになります。
背景
Rails 6.1と7.1ではActive RecordオブジェクトのMarshal.dumpシリアライズ形式が異なり、これまでは後方互換性のために両形式がサポートされていました。config.active_record.marshalling_format_version = 6.1を設定、あるいはconfig.load_defaultsを6.1以下のバージョンで呼び出しているアプリケーションは旧形式を使用し続けることができました。
しかし、この後方互換性の維持がAttributeやAttributeSetといったActive Recordの内部表現の改善を妨げる要因になっていました。#53362でも同様の試みがなされており、今回のPR #57382でその目的が達成されています。
PRでは廃止警告(deprecation warning)を出す方法も検討されましたが、6.1形式がまだ使われているかどうかを検知する現実的な手段がないとして断念されています。このため、アップグレードガイドへの明示的な記載という形で移行を促す方針が取られました。
技術的な変更
Marshalフォーマットの変更は主にactiverecord/lib/active_record/marshalling.rbで行われ、7.1形式が唯一のサポート対象となりました。
変更前:
@format_version = 6.1
def format_version=(version)
case version
when 6.1
Methods.remove_method(:marshal_dump) if Methods.method_defined?(:marshal_dump)
when 7.1
Methods.alias_method(:marshal_dump, :_marshal_dump_7_1)
else
# ...
end
end
変更後:
@format_version = 7.1
def format_version=(version)
case version
when 7.1
Methods.alias_method(:marshal_dump, :_marshal_dump_7_1)
else
# ...
end
end
alias_method :marshal_dump, :_marshal_dump_7_1
デフォルト値が6.1から7.1に変更されただけでなく、クラス定義時にalias_method :marshal_dump, :_marshal_dump_7_1が直接呼び出されるようになりました。これにより、format_version=の呼び出しを経由せずとも7.1形式が常に有効になります。また6.1への分岐(remove_methodによるmarshal_dumpの除去)も削除されています。
railties/lib/rails/application/configuration.rbでは、load_defaults内のactive_record.marshalling_format_version = 7.1の設定が削除されました。もはやデフォルトが7.1固定であるため、明示的な設定が不要になったためです。
ガイドの変更では、configuring.mdから6.1をデフォルト値とするテーブル行が削除され、upgrading_ruby_on_rails.mdにはRails 8.2へのアップグレードにあたっての注意事項が追加されました。
### The old Active Record 6.1 marshalling format was removed.
If your application still sets `active_record.marshalling_format_version = 6.1`, which may
be done by not calling `config.load_defaults` or calling it with a version older or equal to `6.1`,
you MUST opt-in to the newer `7.1` marshal format before upgrading, and ensure all caches were
either flushed or upgraded.
設計判断
デフォルト値の変更とalias_methodの直接呼び出しを組み合わせる方針が採用されました。
従来、7.1形式の有効化はformat_version=経由でのalias_method呼び出しに依存していました。今回の変更では、Methodsモジュール定義内で直接alias_method :marshal_dump, :_marshal_dump_7_1を呼ぶことで、設定値の変更に関わらず7.1形式が確実に適用されます。format_version=のセッターは7.1のみを受け付けるよう維持されており、明示的な設定を行うアプリケーションとの互換インターフェースを残しつつ、6.1形式への逃げ道を塞ぐ設計です。
PRでは「現時点では6.1形式のレコードの読み込み(marshal_load)は引き続き動作する」とも述べられています。ただし、Attribute / AttributeSetの内部APIが変化した際にはいつでも破損しうるとも明記されており、読み込み側の互換性は保証対象外として扱われています。
まとめ
Rails 6.1 Marshalフォーマットのサポート削除は、後方互換性の維持コストを払ってきたActive Recordの内部API改善への布石となる変更です。キャッシュやセッションストアに6.1形式のシリアライズ済みオブジェクトが残っているアプリケーションは、Rails 8.2へのアップグレード前に7.1形式への移行とキャッシュのフラッシュが必須となります。