RailsアプリをRuby 3.4.1にアップデートした
昨年末12月25日にRubyの最新バージョン3.4がリリースされました。私たちのチームでは、この最新バージョンにいち早く追従。開発・運用している複数のRuby on Railsアプリケーションのバージョンを、すべて3.4.1にアップデートしました(2025年1月10日現在)。アップデート後1週間ほど問題なく運用できていますので、速報として記事化します。
ランタイムの前提
この記事ではRuby 3.4ランタイムは以下を指します。
上記からも分かるように、今回の検証はx86_64環境での結果です。なお、aarch64-linux環境への切り替えは未完了です。現在、CPUアーキテクチャ移行にご興味のあるStaff Software Engineerを積極的に募集しています。
また「+PRISM」とある通り、Ruby 3.4から標準パーサーとなったPrismが内部的には使われています。WebアプリケーションにおいてはPumaならびにSidekiqプロセスとして利用していますが、production環境においては問題は発生していません。DevEx(Developer Experience)の観点から必須としているRubyコードリンターのRuboCopにおいて、一点エラーが発生していました。現在は、そのエラーはruby/prism@65cf545で修正されていることを確認済みです(prism 1.4.0でのリリースが予想されます)。
移行前はRuby 3.3.6+YJIT、移行後はRuby 3.4.1+YJITとなります。これは、過去数年議論されてきたYJIT有効化による大幅な高速化は、今回のバージョンアップでは期待していませんでした。こちらは https://speed.yjit.org/ がデイリーで更新されている情報も参考にしています。
YJIT 3.4
https://railsatscale.com/2025-01-10-yjit-3-4-even-faster-and-more-memory-efficient/ (Maxime Chevalier-Boisvert著)に書いてあることが全てだと思いますが、YJIT 3.3→YJIT 3.4におけるYJITランタイムの高速化は5-7%だと主張されています。特に今回のアプリケーションでRubyの影響を受けるのはActiveRecordの箇所だと捉えると、その高速化は+2.6%と推測されます。
実際に移行前後での差異は軽微で、有意な差がある高速化は全体平均値や主要な個別箇所では確認できませんでした。canaryリリースをしない選択をしており、production環境において同一ソースコードの実行結果の差を取得しておらず、他のソースコード修正も同時並行でproductionにリリースされているためです。
--yjit-mem-sizeの調整
Ruby 3.1から提供されてきた --yjit-exec-mem-size オプションに加え、YJIT 3.4では --yjit-mem-size オプションが2024年10月に新たに導入されています(https://github.com/ruby/ruby/pull/11810)。--yjit-exec-mem-size オプションもデフォルト48 MiBのままで運用していたため、yjit-mem-size=128 (in MiB) で運用を開始し目立った問題は発生していません。
全体のメモリ消費量
こちらについてもRuby 3.1-3.3周辺のアップデートの中で、Ruby/YJITコミュニティ的にも議論されてきており、組織内でも
grpc / google-protobuf gem
システム内部の一部のコミュニケーションにgRPCを利用しています。そのため、依存ライブラリとしてgrpcとしては、grpc gemならびにその依存関係あにあるgoogle-protobuf gemを利用しています。
Ruby 3.3リリース直後と同様に、Ruby 3.4リリース直後もこれらのgemではネイティブビルドが利用できなくなりました。DevExはやや低下しますが、最新技術の活用を優先しています。この制約は約1ヶ月程度で解消される見込みで、通常は毎年2月ごろに改善されています。
具体的なworkaroundとしては以下の設定を各所に適用しています。
Stacktraceにクラス名が含まれるように
2024年2月に、stacktraceにメソッド名のみが含まれていたものがクラス名+メソッド名が出力されるようになりました。
https://github.com/ruby/ruby/pull/9605
これにより、stacktraceを確認するだけでエラー発生原因が把握しやすくなったと感じています。
一方で、この値をフィンガープリントとしたエラー管理において、Ruby 3.3以前に「後回しにした」(*1)エラーが、再度新規エラーとして扱われるようになりました。幸い、該当するエラーは数件程度だったため、手動でグルーピングを実施しました。最も多く発生したのはSentryでのエラーでした。件数は片手で数えられる程度でしたので、今回は複数あるグルーピング手法の中からmerge issuesを選択しましたが、件数が多い場合はルールベースで対応する方が効率的だと考えています。
(*1) 後回しにした = SentryにおいてはArchive、BugSnagにおいてはSnooze、Datadog Error TrackingにおいてはIgnore(d)。
準備プロセス
プレリリース版の利用
プレリリース版より検証することが重要です。今回のRuby 3.4シリーズのプレリリースは以下の3版だったため、preview2とrc1を利用して、評価・検証を進めました。
Ruby 3.4.0-rc1 2024-12-12
Ruby 3.4.0-preview2 2024-10-07
Ruby 3.4.0-preview1 2024-05-16
いつやるのか
プロジェクト実施を意思決定する以前から利用していたRuby 3.3は、2027年3月にEOLを迎えることが確定していました。そのため、今回のバージョンアップはそれまでの約2年間で実施する必要がありました。組織・プロダクトの状況を考慮すると、最大2年間は後回しにすることも可能でしたが、2024年のコミュニティへのフィードバックという貢献を最大化するため、このタイミングでの実施に至りました(実際には12月末はホリデーシーズンでチームは稼働していなかったため、年明けからの開始となりました)。
投資開始タイミング
2025年第1週には最終プロセスに入り、すべて完了しています。しかし、productionレベルでの投資は2024年10月頃から緩やかに開始していました。例年以上に、特に今年の1月はチームに多くの新入社員が参画したこともあり、最初の週の実施はハードスケジュールでした。個人的には、2週目の実施でもよかったのではないかと感じています。
リスク回避
早期に実施することにリスクを感じる方もいるかと思いますが、私たちのチームではプロダクト内でのリスク回避策を講じることで、リスクを限定的に抑えられています。このあたりについては、読者の皆様が技術選定をどのように行っているのか、今後お伝えできればと思っていますし、他社の事例についても情報交換したいと考えています(執筆時点では、一定規模以上でRuby 3.4にアップデートしたという報告事例は見当たりません)。今週末には、約9年ぶりに東京で開催されるRubyに関する会議「東京Ruby会議12」(Tokyo RubyKaigi 12)が開催されますので、世の中のRuby 3.4利用状況について議論できることを楽しみにしています。
2025年の抱負
昨年私個人は大きく停滞してしまったので、2025年も私個人もチームメンバーもOSSコミュニティに大きく貢献していきたいなと思う次第です。それが私のかなえたい夢です。
参考
クレジット
画像: ruby/prism The MIT License - Copyright 2022-present, Shopify Inc.