🐥

Ruby on Rails 7.1 へのアップデートを行った

2024/05/13に公開

こんにちは、simomu です。
先日弊社のプロダクトの一つの Rails のバージョンを 7.0 から 7.1 にアップデートを行いました。
今回は Rails 7.1 にアップデートする際に実施した変更内容を話していきます。

rails app:update の実行

基本的にはインストールしている Rails のバージョンを 7.1 にアップデートしたうえで、
Rails アップグレードガイド に従い rails app:update を実行します。config/ 配下の設定を上書きされるかどうかを聞かれますが、我々の場合は一旦全部上書きした後に git での差分を見ながら適用していく変更内容を精査していきました。

Rails のバージョン間の config/ 配下のデフォルトの設定の差分を事前に確認したい場合は、RailsDiff が便利です。

config.load_default7.1 へ変更してみて、テストを実行してみる

この時点で、変更が必要になった箇所は以下のとおりです。

ActiveRecord のserialize で coder を明示的に指定する

従来では、serialize で指定したカラムのデータのシリアライズはデフォルトで YAML が指定されていましたが、default_column_serializer が Rails 7.1 からは nil になりました。

従来どおりの挙動を維持する場合は

config.active_record.default_column_serializer = YAML

でデフォルトを設定するか、 カラム毎にserialize のオプションで coder: YAML を指定する必要がありました。

serialize :column_name, coder: YAML

また、これに関連してカラムのデシリアライズ後のオブジェクトを指定している場合は、キーワード引数を用いる必要があります。

# Rails 7.0 まで
serialize :column_name, Array

# Rails 7.1
serialize :column_name, type: Array

attr_readonly で生成した属性に値を代入すると例外が発生するようになった

attr_readonly 指定された属性への値の代入操作は行えるが値は変化はしない、という振る舞いに依存したテストが存在していたため、それらを修正しました。

class Test < ApplicationRecord
  attr_readonly :readonly_column
end

# Rails 7.0 まで
record.readonly_column = 'new value' # => no raise error, but not changed

# Rails 7.1
record.readonly_column = 'new value' # => ActiveRecord::ReadonlyAttributeError: readonly_column

なお、 attr_readonly 指定された属性に値を代入すると例外が発生する振る舞いは

Rails.application.config.active_record.raise_on_assign_to_attr_readonly = false

でオフにすることもできます。

ActiveJob のリトライ設定の exponentially_longerpolynomially_longer にリネーム

挙動に変化はありませんが、ActiveJob のリトライ設定の wait のオプションで exponentially_longer が非推奨になり、polynomially_longer を使用するようにとの警告が出ていたので修正を行いました。

# Rails 7.0
retry_on HogeError, wait: :exponentially_longer

# Rails 7.1
retry_on HogeError, wait: :polynomially_longer

new_framework_defaults_7_1.rb の中で互換性に問題がある設定を確認

new_framework_defaults_7_1.rb を確認し、互換性の問題ですぐに Rails 7.1 の設定を有効にできなさそうなものや、別途時間を取って対応したほうが良さそうな設定を確認し、変更していきました。
その中で今回のアップデートにおいて影響があったのは以下の通りでした。

ActiveRecord::Encryption の非決定論的暗号化されたカラムの復号に関する設定

Rails 7.0 において、ActiveRecord::Encryption の暗号化のダイジェストを SHA256 設定にしているにも関わらず、非決定論的暗号化[1]を用いている場合はその設定が有効にならず SHA1 で暗号化される不具合があったとのことです。
https://github.com/rails/rails/pull/48530

Rails 7.1 からはその不具合が修正され、非決定論的暗号化でも SHA256 設定が正しく有効になりましたが、既存の非決定論的暗号化されたカラムはすでに SHA1 で暗号化されてしまっているため、該当カラムを復号しようとすると以下のような ActiveRecord::Encryption::Errors::Decryption エラーが発生します。

ActiveRecord::Encryption::Errors::Decryption: ActiveRecord::Encryption::Errors::Decryption
from /usr/local/bundle/ruby/x.x.x/gems/activerecord-7.1.3.2/lib/active_record/encryption/encryptor.rb:58:in `rescue in decrypt'

既存の非決定論的暗号化されたカラムを復号できるようにするには以下の設定を有効にする必要があります。

Rails.application.config.active_record.encryption.support_sha1_for_non_deterministic_encryption = true

この設定を入れたうえでリリースを行った後、非決定論的暗号化を採用しているカラムを ActiveRecord::Encryption#encrypt を用いて SHA256 で再暗号化を行った上でsupport_sha1_for_non_deterministic_encryption を false にする対応を行いました。

cache_format_version に関する設定

Rails の cache の機能を利用している場合や、session_store に cache_store を指定している場合などはこの設定に注意する必要があります。

Rails 7.1 から cache に値を保存するときのシリアライズフォーマットに新しいバージョンのものが追加されました。Rails 7.1 は、それより古い cache_format_version のキャッシュを読み取ることは従来通り可能ですが、cache_format_version 7.1 で保存されたキャッシュを Rails 7.0 が読み取ることはできません。

これは Rails アプリケーションが複数台起動している環境下で、デプロイ時にローリングアップデート等の戦略を採用している場合に Rails 7.1 で生成したキャッシュを Rails 7.0 で読み込もうとしてしまって以下のような TypeError が発生します。

TypeError incompatible marshal file format (can't be read) format version 4.8 required; 17.1 given

なお、Rails アップグレードガイドの 7.0 -> 7.1 の項目にはこのことは記述されていませんが、同様の内容は 6.1 -> 7.0 の項目には記述があります。
https://railsguides.jp/upgrading_ruby_on_rails.html#activesupport-cacheの新しいシリアライズフォーマット

Rails 6.1アプリケーションはこの新しいシリアライズフォーマットを読み取れないので、シームレスにアップグレードするには、まずRails 7.0へのアップグレードをconfig.active_support.cache_format_version = 6.1でデプロイし、Railsプロセスがすべて更新されたことを確かめてからconfig.active_support.cache_format_version = 7.0を設定する必要があります。

Rails 7.1 に関しても上記引用と同じ対応が必要です。

また、Rails ガイドの cache_format_version には

どの形式も前方互換性と後方互換性があります。つまり、ある形式で書かれたキャッシュエントリは別の形式で読み取り可能です。

とありますが、これはあくまで同一 Rails バージョンであれば、cache_format_version には前方・後方互換性があるということであり、Rails のバージョンそのものが異なる場合は前方互換性はないので注意が必要です。

まとめ

今回は弊社のプロダクトの一つの Rails のバージョンを 7.0 から 7.1 にアップデートする際に行った修正や設定の変更についてまとめました。
Rails 7.1 へのアップデート時に参考になれば幸いです。

脚注
  1. deterministic: false もしくは無指定のもの ↩︎

SocialPLUS Tech Blog

Discussion