- Ruby / Rails関連
週刊Railsウォッチ: Rails 7.2でメンテナンスポリシー更新、書籍『Ruby on Railsパフォーマンスアポクリファ』ほか(20240819)
こんにちは、hachi8833です。Railsガイドも先週7.2に更新完了しました↓。
#Railsガイド がRails 7.2に対応!!🆙✨
🛤 Rails 7.2リリースノート
🛠 Dev Containerでの開発
🚀 本番環境のパフォーマンス改善
(最適化のトレードオフ、PumaやYJITの設定、負荷テストや測定項目の解説など)が新たに追加され、既存ガイドも大幅に改訂されました...!!📕💖https://t.co/1WexdNRvJ9 pic.twitter.com/JTiHShnj1r
— Railsガイド 📕 (@RailsGuidesJP) August 16, 2024
🔗Rails: 先週の改修(Rails公式ニュースより)
🔗 Railsのメンテナンスポリシーが変更された
主な変更:
- リリースのメンテナンスは事前定義済みの固定期間に基づいて行われる。バグ修正は1年間、セキュリティ修正は2年間。
「重大なセキュリティ問題」と「通常のセキュリティ問題」の区別を廃止した。
npmバージョニングを更新し、プレリリースの
-
区切りを使わないようにした。サポート対象となる全リリース、およびサポート終了日については今後https://rubyonrails.org/maintenanceに追加される。https://rubyonrails.org/securityも自分が更新する予定。
同PRより
参考: Ruby on Rails のメンテナンスポリシー - Railsガイド
つっつきボイス:「お、メンテナンスポリシーの変更は把握しておかないといけないヤツですね」「重大なセキュリティ問題と通常のセキュリティ問題という区分がなくなったのね」
「ポリシーの変更は一足先にRailsガイドにも反映して、その後で以下の文言も追加してもらいました↓」
訳注:2024年8月に更新された本メンテナンスポリシーは、それ以降のリリースにのみ適用されます。したがってRails 6.1のEOLは旧メンテナンスポリシーに沿って2024年10月1日、Rails 7.0は2025年4月1日、Rails 7.1は2025年10月1日のままとなります。(» 原文を見る)
Ruby on Rails のメンテナンスポリシー - Railsガイドより
#Railsガイド の『Ruby on Railsのメンテナンスポリシー』が更新され、より具体的になりました! 🆙✨ (Thx! @hachi8833 💖)
📝 3種類のサポート分類
🔐 それぞれのサポート期間
📅 新機能などのリリース間隔などの詳細はコチラから! https://t.co/i5dKtYpoiH pic.twitter.com/CYw7ceBPrT
— 安川要平/Yohei Yasukawa (@yasulab) August 7, 2024
「従来はガイドに書かれていたサポート期間の情報が、今後はhttps://rubyonrails.org/maintenanceに表示されるようになったようです」「サイトが更新中なのか、まだセキュリティサポート期間が空欄ですね(編注: その後更新が終わって表示されました↓)」
「ちょうど今Rails 6.1のアプリをアップデートしているんですけど、6.1のサポートは今年の10月でおしまいか〜😅」「お疲れさまです」「Rails 3.x系あたりのアップデート依頼が来るのは今でも珍しくありませんね」「Rails 7.0のEOLは来年10月って、もうそんなに先の話じゃないですね」
「ところで、以下のendoflifeみたいに有志がやっているサポート終了情報は、公式情報に追従しきれなかったりとかで、たまに古かったり誤っていたりすることもありますよね↓」「そうなんですよね...」「だからこそ公式がEOL情報をわかりやすく出してくれるととてもありがたい」
参考: Ruby on Rails | endoflife.date
「現実には古いバージョンのまま動き続けているアプリはたくさんあるわけですけど、フレームワークがすべてのバージョンをサポートするのは負担が大きすぎるし進化も遅れるしで無理なので、こうやってサポート終了までの期間を誤解されにくい形で明確に示すのが重要になってきますね」
「アプリを普段から最新に保っていればアップデートはそれほど大変ではないはずなんですけど、数年放置していたりすると一気につらくなってきますよね」「たいていサードパーティgemでつらくなる」「その間にアプリを知っている開発者がいなくなっていたりするともう大変ですよね」「そういうアプリはアップグレードの調査だけで1か月かかって、テストコードもたくさん更新しなければならなくなって...と高く付きがち」
🔗 authenticationジェネレータにパスワードリセットを追加
パスワードリセットの基本的なフロー(署名済みIDをメーラーで知らせるため)。
これはオプトアウト可能にすべき。残りの作業:
- [x] 手動ルーティングのないPOSTで署名済みIDのトークンパラメータが認識されない問題の修正
同PRより
つっつきボイス:「DHHによる改修です」「先週追加されたsessionジェネレータ(ウォッチ20240807)の改修のようですね」「ちなみにその後以下でauthenticationジェネレータにリネームされたそうです↓」「この名前の方が適切でしょうね」「今はシンプルだけど、こうやってサポートを追加していくとだんだんDeviseみたいになっていったりして😆」
- PR: Rename sessions generator to authentication generator by dhh · Pull Request #52435 · rails/rails
🔗 has_secure_password
にデフォルトのパスワードリセット用トークンジェネレータを追加
- PR: Add a default password reset token to has_secure_password by dhh · Pull Request #52483 · rails/rails
has_secure_password
を使っている場合に、パスワードリセットトークン用のデフォルトのトークンジェネレータを追加。class User < ApplicationRecord has_secure_password end user = User.create!(name: "david", password: "123", password_confirmation: "123") token = user.password_reset_token User.find_by_password_reset_token(token) # userを返す # 16分後... User.find_by_password_reset_token(token) # nilを返す # トークン失効のためActiveSupport::MessageVerifier::InvalidSignatureが発生する User.find_by_password_reset_token!(token)
DHH
同Changelogより
つっつきボイス:「これもDHHによる改修です」「パスワードをリセットしてfind_by_password_reset_token
を取れるようになった: has_secure_password
などのRails組み込みの認証系機能が昔より強力になってきていて、authenticationジェネレータのサポートが進む流れでパスワードリセットトークン処理も追加されたということですね」「こういう作り込みに手間がかかったので以前はDeviseをインストールすることが多かったんですが、こうなってくると無理にDeviseをインストールしなくてもいいのではという気持ちになってきそう」「そういえばEvil Martiansもhas_secure_password
でかなりやれると書いていましたね」
参考: 1.12 SecurePassword
モジュール -- Active Model の基礎 - Railsガイド
「かつてDeviseのような認証系gemが重宝された理由のひとつは、よくあるログインや今回のようなパスワードリセットなどの機能もビューごとまとめて生成してくれたからなんですが、昨今はRailsをAPIサーバーにして直接Railsのビューをユーザーに出さないケースも増えてきたので、とりあえずDeviseをインストールするという必然性はかつてほどでもなくなりつつある感じ」「なるほど」「Deviseはこれまでデファクトスタンダードでしたけど、こうやってRails組み込みの認証機能が充実してくると、新規プロジェクトではDeviseを使わないことも視野に入れるようになってくるかもしれませんね」「Deviseで苦しむ人多かったですもんね」
🔗 アプリを起動してすぐ終了するbin/rails boot
コマンドを追加
自分は、ベンチマークの実行や、ブートロジックの理解のためにアプリケーションを頻繁に起動していることに気づいた。
こうした作業用に、以下のようなrunner
コマンドをよく使っている。bin/rails r 1
これにより、アプリケーションが起動し、既存の
runner
フックが呼び出され、整数リテラル1
で構成したRubyプログラムが実行される。自分は長年このトリックを便利に使ってきたが、これは間接的である。「アプリケーションを起動して他に何もしない」という厳密な意味を持つ、もっとシンプルな方法が今までなかった。
そこでこの新しい
boot
コマンドを作った。
同PRより
つっつきボイス:「Rails 7.2 RC1が出ているので今回の改修の多くは次のRails 8向けの改修になるはずですが、このboot
コマンドは7.2に入りました」「ここではローカルでの実験用にRailsアプリを起動して終了するコマンドが欲しいということだけど、CIやコンテナイメージのビルドなんかで正常に起動と終了することだけを確認するようなシチュエーションにも使えそう: 特にデプロイ可能なコンテナをビルドするときなんかは、さんざん時間をかけてビルドしたのに実は動かなかったみたいな状況は避けたいので、ビルド前のチェックなんかに使えるかも👍」「boot
という名前、もっといいのがありそうな気がしなくもないですね🤔」
🔗 check_box
をcheckbox
にリネーム
check_box*
メソッドをcheckbox*
にリネーム従来のメソッド名もエイリアスとして引き続き利用可能。
Jean Boussier
同Changelogより
つっつきボイス:「以下も同趣旨のプルリクだそうです↓」「命名を従来のアンダースコア_
区切りからアンダースコアなしのメソッド名に統一するということですね」
「HTMLでは<textarea>
という要素名があるので、そちらに寄せるのはわかるしそうした方がよさそう」「checkbox
はちょっと違和感あるかも?」「こっちは<input type="checkbox">
というふうに属性なので要素名ほど切実ではなさそうですけどね」「エイリアスで新旧どちらも使えるならOK」「こだわりないのでlintが自動修正してもOK」
🔗 Dockerのビルド実行時に警告が出たらエラーにするようになった
07/29に、DockerにDockerビルドチェックという機能が導入された。
デフォルトでは、Dockerビルドで警告が発生してもビルドは失敗しない(ゼロ以外の終了コードを返す)。警告時にエラーを発生させるには、Dockerfileに
# check=error=true
宣言を追加する必要がある。また、Dockerの公式ガイドの宣言サンプルにはスペースがないので、こちらもスペースなしにした。
同PRより
つっつきボイス:「Dockerfileに# check=error=true
を記述するとビルド時の警告をエラー扱いするのか」
# railties/lib/rails/generators/rails/app/templates/Dockerfile.tt
-# syntax = docker/dockerfile:1
+# syntax=docker/dockerfile:1
+# check=error=true
「ちょうど最近Dockerのこのあたりの新機能が社内でも話題になってましたね: たとえばdocker build . --check
するとビルドしなくなるのでチェックがめちゃくちゃ速くなるとか、docker --debug build
でデバッグビルドできるようになったとか↓」「なるほど」
参考: Docker ビルドチェックの紹介: ベストプラクティスによるDockerfileの最適化 | Docker
🔗 モデルでhuman_attribute_name
の訳文が見つからない場合にエラーを発生するよう改修
ActiveModel::Translation
用の読み込みフックactive_model_translation
を追加Shouichi Kamiya
ActiveModel::Translation
にraise_on_missing_translations
オプションを追加このオプションを設定すると、指定の属性の訳文が見つからない場合に
human_attribute_name
がエラーを発生するようになる。# ActiveModel::Translation.raise_on_missing_translations = false Post.human_attribute_name("title") => "Title" # ActiveModel::Translation.raise_on_missing_translations = true Post.human_attribute_name("title") => Translation missing. Options considered were: (I18n::MissingTranslationData) - en.activerecord.attributes.post.title - en.attributes.title raise exception.respond_to?(:to_exception) ? exception.to_exception : exception ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Shouichi Kamiya
同Changelogより
config.i18n.raise_on_missing_translations = true
になっていると、訳文が見つからないというエラーがコントローラとビューで発生するが、モデルではエラーが発生しない。
このプルリクは、raise_on_missing_translations
がtrueの場合にモデルでエラーになるよう変更する。
同PRより
つっつきボイス:「Railsのi18nは、今まではコントローラやビューでは訳文が見つからないときにエラーをraiseしてたけど、モデルではしてなかったのを修正したのね」「それは修正すべき」「見つからない場合は基本的にraiseでいいと思います👍」
「ところでhuman_attribute_name
っていうメソッド名、ちょっと長いですよね」「エイリアスがありそうでないのか〜」
参考: Rails API human_attribute_name
-- ActiveModel::Translation
🔗 ルーティングの非推奨化2件
- ルーティング高速化のため、複数パスを含むルーティング生成を非推奨化する
with_options
またはループを使えば、複数パスは楽に生成できる。# 変更前 get "/users", "/other_path", to: "users#index" # 変更後 get "/users", to: "users#index" get "/other_path", to: "users#index"
Gannon McGibbon
同Changelogより
動機/背景
このプルリクを作成した理由は、ルーティングのマッピングにおける複数パスの利用を非推奨化したいため。
Rails.application.routes.draw do get "/users", "/other_path/users", "/another_path/users", to: "users#index" end
上の代わりに以下のようにシンプルに書ける。
Rails.application.routes.draw do get "/users", to: "users#index" get "/other_path/users", to: "users#index" get "/another_path/users", to: "users#index" end
私見では、この方が少し読みやすいと思う(その分冗長にはなるが)し、自分が提案したいと思っているマッパー実装の今後の変更もシンプルになる。自分はルーティングマッパー内のハッシュをキーワードに置き換えたい。ベンチマークは以下のとおり。
# frozen_string_literal: true require "bundler/inline" gemfile(true) do source "https://rubygems.org" gem "rails", path: "." gem "benchmark-ips" gem "stringio" end require "benchmark/ips" require "action_controller/railtie" class TestApp < Rails::Application config.root = __dir__ config.hosts << "example.org" config.secret_key_base = "secret_key_base" config.logger = Logger.new($stdout) Rails.logger = config.logger end class TestController < ActionController::Base include Rails.application.routes.url_helpers def index render plain: "Home" end end Benchmark.ips do |x| x.report("draw") do Rails.application.routes.draw do resources :posts resource :user root to: "home#index" post "login", to: "sessions#login" delete "logout", to: "sessions#logout" end Rails.application.instance_variable_set(:@routes, nil) end end
ルーティング生成が1.25〜1.5倍高速になった。
mainブランチの場合:
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23] Warming up -------------------------------------- draw 114.000 i/100ms Calculating ------------------------------------- draw 1.150k (± 1.1%) i/s - 5.814k in 5.055991s
この機能を削除したフィーチャーブランチの場合:
ruby 3.3.1 (2024-04-23 revision c56cd86388) [arm64-darwin23] Warming up -------------------------------------- draw 141.000 i/100ms Calculating ------------------------------------- draw 1.496k (± 1.4%) i/s - 7.614k in 5.091321s
詳細
ハッシュキーでルーティングを生成するとルーティング生成が複雑になるため、非推奨化すべき。ルーティングマッパーでキーワードを使う形に移行すれば1.25〜1.5倍高速化するし、この機能を削除すれば大幅に簡単になる。ほとんどの開発者はルーティングでパスを1つしか使わないので、この機能はめったに使われていない可能性がある。
追加情報
この後のプルリクで、ハッシュキーによるルーティングパスの非推奨化(例:
get "/users" => "users#index"
)を構想中。
同PRより
つっつきボイス:「え、get "/users", "/other_path", to: "users#index"
みたいな書き方できるの?」「これは見たことなかった」「このサポートを削ると速くなるんだそうです」「使ったことない書き方だから非推奨でも全然OK😆」
ルーティング高速化のため、ハッシュキーのパスを渡してルーティングすることを非推奨化する。
# 変更前 get "/users" => "users#index" post "/logout" => :sessions mount MyApp => "/my_app" # 変更後 get "/users", to: "users#index" post "/logout", to: "sessions#logout" mount MyApp, at: "/my_app"
同Changelogより
#52409の続き。
動機/背景
このプルリクを作成した理由は、ルーティングのマッピングでハッシュキーのパスを非推奨化したいため。
# 変更前 get "/users" => "users#index" post "/logout" => :sessions # 変更後 get "/users", to: "users#index" post "/logout", to: "sessions#logout"
これによって、ルーティングマッパーでキーワードを使う方式に近づき、1.25〜1.5倍高速化する(ベンチマークについては上のプルリクのリンクを参照)。
詳細
ハッシュキーでルーティングを生成するとルーティング生成が複雑になるため、非推奨化すべき。ルーティングマッパーでキーワードを使う形に移行すれば1.25〜1.5倍高速化するし、この機能を削除すれば大幅に簡単になる。
同PRより
「こちらもルーティングの似たような非推奨化で、to:
の方が速いということだそうです」「to:
を使わずにget "/users" => "users#index"
のようにハッシュの=>
で書くのは昔からあった気がしますね(たしかRails 2ぐらいの頃)」「この非推奨に引っかかる人はそれなりにいそう: いずれにしろ今この書き方にする必要はないでしょうね」
「ところでこれがあくまで起動時のルーティングマップの生成を高速化するものだとしたら、起動時に1回しか実行されない部分が高速化されてもあまり嬉しくない気はしませんか?」「たとえばECSでコンテナをオートスケーリングしたときなんかは高速にサービスインできる方がありがたいんじゃないかな」「それもそうか」「ルーティング生成はRailsの処理の中では重い方ではありますね」
🔗 attribute_writer_missing
が追加
ActiveModel::AttributeAssignment#attribute_writer_missing
を導入見つからない属性の代入を適切に行える機会をインスタンスに提供する。
class Rectangle include ActiveModel::AttributeAssignment attr_accessor :length, :width def attribute_writer_missing(name, value) Rails.logger.warn "Tried to assign to unknown attribute #{name}" end end rectangle = Rectangle.new rectangle.assign_attributes(height: 10) # => Logs "Tried to assign to unknown attribute 'height'"
同Changelogより
動機/背景
外部データソース(API呼び出しなど)を元にビルドされたActive Modelインスタンスは、時間とともに元のデータの古いバージョンと同期が取れなくなる可能性がある。たとえば、APIがJSONレスポンスからフィールドを追加・削除し、解析されたJSONをアプリケーションがmass assignmentのためにモデルのコンストラクタに直接渡す可能性が考えられる。
最初にデータで何らかの操作(サニタイズや正規化など)を行わないと、モデルインスタンスで元のデータへの予想外の変更を適切に処理する機会を得られない。最良のケースでは修正や強制の措置を講じられる可能性もあるが、最悪の場合、不整合をトラッキングするかログ出力することになる。
詳細
未知の属性の代入を適切に行える機会をインスタンスに提供するため、
ActiveModel::AttributeAssignment#attribute_writer_missing
を導入する。class Rectangle include ActiveModel::AttributeAssignment attr_accessor :length, :width def attribute_writer_missing(name, value) Rails.logger.warn "Tried to assign to unknown attribute #{name}" end end rectangle = Rectangle.new rectangle.assign_attributes(height: 10) # => Logs "Tried to assign to unknown attribute 'height'"
デフォルトでは、
#attribute_writer_missing
をオーバーライドしないクラスはActiveModel::UnknownAttributeError
をraiseする。追加情報
attribute_writer_missing
の命名は、BasicObject#method_missing
の模倣を意図している。
ActiveModel::AttributeMethods#attribute_missing
というメソッドもあるが、こちらは属性のアクセスに関係する。
同PRより
つっつきボイス:「今までは属性が見つからない場合にUnknownAttributeError
を直接raise
していたけど、#attribute_writer_missing
メソッドを追加してオーバーライド可能にすることで、属性が見つからないときの処理をカスタマイズするときの入口として使えるということですね: こういうのがあると助かる場合がありそう👍」
# activemodel/lib/active_model/attribute_assignment.rb#L56
+ def attribute_writer_missing(name, value)
+ raise UnknownAttributeError.new(self, name)
+ end
+
private
def _assign_attributes(attributes)
attributes.each do |k, v|
_assign_attribute(k, v)
end
end
def _assign_attribute(k, v)
setter = :"#{k}="
public_send(setter, v)
rescue NoMethodError
if respond_to?(setter)
raise
else
- raise UnknownAttributeError.new(self, k.to_s)
+ attribute_writer_missing(k.to_s, v)
end
end
end
🔗 ログ出力から除外するデフォルト変数名のリストにcvvとcvcも追加
クレジットカードの詳細情報は一般にサーバーに送信すべきではなく、StripeやBraintreeなどで処理すべき。しかし、フォームでユーザーが誤ってクレジットカード番号を送信してしまうと、サーバーで利用されなくても詳細情報がログに出力されてしまうことになる。これは潜在的に「カードデータを保存」していることになるが、これをセキュアに行うための法的な要件は他にもたくさんある。
このプルリクは、新規アプリの
ActiveSupport::ParameterFilter
にデフォルトでcvv
とcvc
を追加する。つまり、これらの名前を持つパラメータはデフォルトでログ出力されなくなる。これは新規アプリのテンプレートを変更するだけであり、既存アプリは変更されない。同PRより
つっつきボイス:「あ〜なるほど、フィルタで除外するワードリストにクレジットカード番号で使われるcvv
とcvc
を追加したんですね↓」「クレジットカード系のパラメータを直接Railsサーバーで処理する実装にするのは現代日本では通常あり得ない(PCI DSS準拠を自前で頑張るということは非現実的)ので、実際にこのフィルタに助けられることは少ないとは思いますが、転ばぬ先の杖にはなりますね👍」
# railties/lib/rails/generators/rails/app/templates/config/initializers/filter_parameter_logging.rb.tt#L04
# Use this to limit dissemination of sensitive information.
# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
Rails.application.config.filter_parameters += [
- :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
+ :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc
]
参考: クレジットカード・セキュリティガイドライン【4.0版】が改訂されました (METI/経済産業省)
「ちなみにこのフィルタ除外機能は、ログ以外にたとえばinspect
結果からの除外でも使われていますね↓」
参考: 3.2.33 config.filter_parameters
-- Rails アプリケーションの設定項目 - Railsガイド
🔗 in_batches
で主キー以外のカラムも指定できるようになった
- カスタムカラムでのバッチングをサポート
Product.in_batches(cursor: [:shop_id, :id]) do |relation| # relationで何かする end
fatkodima
同Changelogより
利用例:
Product.in_batches(cursor: [:shop_id, :id]) do |relation| # relationで何かする end
現在のバッチ処理では主キーカラムのみを利用可能だが、この制約は厳しい。クラスタ化インデックスをより有効に活用するために、何らかの親(テナント)カラムを含める(あるいは一方の親からすべてのレコードを処理して他方の親からの処理を開始する)か、親内で何らかの「位置」カラムを用いてイテレーションするなどが必要になることがある。
カスタムカラムを用いてイテレーションする機能がないので、自分は既に独自gemでカスタムのバッチイテレータを何度か実装したことがある。また、現在のプロジェクトでもカスタムカラムをバッチ処理可能にする必要が生じている。
幸い、複数カラムによる順序付け(
:order
による)が既にサポートされているし、複合主キー(すなわち複数カラム)もサポートされているので、この機能のサポートは非常に簡単である。うっかりnon-uniqueなカラムを使って結果が誤ってしまう(行の重複や欠落)ことはよくあるので、この実装では、指定のカラムのリストにuniqueカラムの組み合わせ(主キーや、uniqueインデックスに含まれる一部のカラムなど)を含めることを強制している。
同PRより
つっつきボイス:「in_batches
に主キー以外のもの(デフォルトは主キー)も渡せるようになるのは便利だと思うけど、これって安全にやれるのかな?」「バッチ処理中に検索結果が変わった場合とかですよね」「それそれ、そもそもin_batches
に主キーしか指定できないという制限があったのは、主キーでロックしないとおかしなことが起きる可能性があるので、それを防ぐという意図があったと思うんですよ」「使うカラムがイミュータブルで、ソート順位に影響しないことが保証されれば大丈夫なんでしょうけどね」
「プルリクメッセージに"指定のカラムのリストにuniqueカラムの組み合わせを強制する"とあるので、uniqueにならないカラムの組み合わせは渡せないようにすることで防いでいるっぽいですね」「in_batches
に主キー以外のものを渡したいことはたしかにあるので、uniqueな複合主キーを前提にして実装したということでしょうね」
参考: Rails API in_batches
-- ActiveRecord::Batches
🔗 ローカル環境でsecret_key_base
にnil
を再び設定可能にした
- PR: Reallow setting secret_key_base to nil when local by skipkayhil · Pull Request #52351 · rails/rails
動機/背景
参照: #52062
従来は、バリデーションは利用時にのみ行われ、セッターでは行われなかったため、ローカル環境では
secret_key_base
をnil
に設定できた(またはSECRET_KEY_BASE_DUMMY
環境変数を利用)。
この部分が#52351で変更され、secret_key_base
に無効な値が設定される場所を正確に特定しやすくなった。しかしこれによって、development環境やtest環境で
secret_key_base
に無条件に外部値を設定する一部のアプリケーションが動かなくなった。この変更前は、設定値がnil
になる可能性があり、利用時に生成されたローカルのsecretにフォールバックしなければならなかった。詳細
このコミットは以前の振る舞いを復元し、この
nil
値が最終的に生成されたローカルsecretによって置き換えられるようになったので、アプリケーションが引き続きsecret_key_base
を無条件に設定可能になった。追加情報
振る舞いを復元するため、今は条件を空白のままにしているが、これを非推奨にして代わりにアプリケーション側で
credentials.secret_key_base
を設定するようにすべきだろうか?
同PRより
つっつきボイス:「そういえばこれは7.2のマイルストーンにしばらく残っていました」「ローカル環境でsecret_key_base
をnil
にできないようにしたら不都合が生じたので修正したんですね: production環境では当然secret_key_base
を設定すべきだけど、ローカル環境でsecret_key_base
をいちいち生成しなければならなくなると面倒なのもたしか」「SECRET_KEY_BASE_DUMMY
はRails 7.1でDHHが追加した環境変数でしたね(ウォッチ20230125)」
🔗 SQLIteアダプタでIMMEDIATE
トランザクションを有効にした
- SQLiteで可能な場合は
IMMEDIATE
トランザクションを利用するようになったSQLite3アダプタに対して実行されるトランザクションは、コンカレンシーサポートを改善してビジー例外を回避するため、デフォルトで
IMMEDIATE
になる。Stephen Margheim
同Changelogより
動機/背景
SQLiteがRailsアプリケーションのproduction向けデータベースエンジンとして人気が高まるにつれて、堅牢かつ回復力を備えたデフォルト設定の必要性も高まってきている。RailsアプリケーションでSQLiteを使うときに直面しがちな問題の1つは、
ActiveRecord::StatementInvalid (SQLite3::BusyException: database is locked)
例外がときたま発生すること。これらの例外は、
DEFERRED
トランザクションが書き込みクエリに遭遇して、トランザクションの途中でSQLiteデータベースのロックを取得しようとしたときに発生する。これはトランザクションの途中で発生するので、SQliteはbusy_handler
コールバックやbusy_timeout
コールバックを呼び出してトランザクションを再試行せずに、ただちにビジー例外でエラーになる。詳細
このプルリクでは、その存在期間中に2つの異なる手法を検討した。
- SQLiteアダプタのデフォルトのトランザクションモードをグローバルに
DEFERRED
からIMMEDIATE
に変更するRailsがActive Recordの書き込み操作をラップするためのトランザクションに対してのみ、トランザクションモードを
DEFERRED
からIMMEDIATE
に変更するトランザクションを手動で作成するテストが多数失敗したことと、他のアダプタでは意味も目的もない汎用の
mode
オプションを#transaction
に公開しない方が自分にとって好みでるため、オプション2を選んだ。オプション2では、
ActiveRecord::Base#with_transaction_returning_status
がAdapter#transaction
を直接呼び出すのではなく、Adapter#transaction_returning_status
を呼び出すようになった。
このメソッドは、デフォルトでは単なるエイリアスだが、SQLiteアダプタではtransaction_returning_status
メソッドを実装してIMMEDIATE
トランザクションモードが使われるようにした。
トランザクションモードの設定は、SQLite3アダプタに追加されたuse_*_transaction_mode!
メソッドで行い、test_fixtures.rb
モジュールでもこれを用いてfixtureトランザクションが常にDEFERRED
トランザクションを使うようにする。追加情報
このプルリクは、#50370とともに、断続的かつ頻繁にビジー例外をスローすることなく、SQLiteのコンカレンシー処理を安定させる。
同PRより
つっつきボイス:「これはSQLite3を使う人にのみ関係する改修ですね」「SQLite3のトランザクションにはIMMEDIATE
とDEFFERED
とEXCLUSIVE
という3つのモードがあるのね」
参考: SQLiteのtransactionにおけるimmediateとexclusive - Using Perl
🔗Rails
🔗 Rails WorldでDHHとMatzの対談企画(Rails公式ニュースより)
Special #RailsWorld announcement: @yukihiro_matz and @dhh will share the stage for the very first time for a fireside chat about #Ruby, #Rails and all things #opensource. This special conversation will be moderated and hosted by @Shopify founder and early Rails Core member @tobi.… pic.twitter.com/NJnw9Tml7q
— Ruby on Rails (@rails) August 7, 2024
つっつきボイス:「肩の凝らない対談企画という感じかな」「"fireside chat"がx.comの機械翻訳で"炉辺談話"と訳されていたのがうまいなと思いました☺️」
🔗 書籍『Ruby on Railsパフォーマンスアポクリファ』
新たに翻訳した書籍の紹介記事を書きました /
『Ruby on Railsパフォーマンスアポクリファ』 - snoozer05's blog https://t.co/BF34QIHkHE#はてなブログ— Koji Shimada / 島田浩二 (@snoozer05) August 8, 2024
つっつきボイス:「TechRachoのパフォーマンス関連の翻訳記事でもたびたびお世話になっているNate Berkopecさんの書籍が翻訳されたそうです」「ニュースレターをまとめたもので、技術書よりもお気楽な技術エッセイという感じかな: 紹介記事にもあるハッカーと画家とかを思わせますね」「日本だと1,000円で買えるんですね」
「雑談ですけど、アポクリファ(Apocrypha)はキリスト教関連の用語で"外典"という意味で、その反対語がCanon(正典、聖典)で、DNSとかでお馴染みのカノニカル(Canonical)にも通じる言葉です」「へ〜」「Apocryphaはギリシャ語由来なんですが、crypt(=隠された)という言葉が含まれていることからわかるように、暗号のcryptgraphyにも通じる言葉ですね」「エヴァンゲリオンに出てきた死海文書みたいなヤツかな😆」
参考: 外典 - Wikipedia
参考: 聖書正典 - Wikipedia
参考: JPRS用語辞典|CNAMEリソースレコード(シーネームリソースレコード)
参考: 死海文書 - Wikipedia
「こんな感じでIT技術用語の中にはたまに宗教由来の用語もあって、たとえばデータベースなどの昇順を表すascendingに通じるAscensionは、宗教の文脈だとキリストの昇天を表したりしますね」
参考: ascensionの意味・使い方・読み方|英辞郎 on the WEB
つっつき後に日本語版PDFを購入しました。冒頭でアポクリファについても触れていますね↓。
聖典はこちらです↓。
参考: The Complete Guide to Rails Performance
今週は以上です。
バックナンバー(2024年度第3四半期)
週刊Railsウォッチ: Rails 7.2 RC1がリリース、ストリーミングのレスポンス処理をRack 3で行うほか(20240807前編)
- 20240807前編 Rails 7.2 RC1がリリース、ストリーミングのレスポンス処理をRack 3で行うほか
- 20240709 シャーディング用メソッドを追加、localsマジックコメント修正ほか
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)