Rails 8.0 のデフォルトでは、ジョブがトランザクション内でスケジューリングされてしまう
こんにちは、terandard です。
先日 Rails 8.1 がリリースされましたね。
それに合わせて弊社のサービスを Rails 7.2 から 8.0 にアップデートしましたが、その際に発生した問題について共有します。
概要
Rails 7.2 で導入された新機能として、Active Record のトランザクション内でジョブのエンキューを行うと、そのトランザクションがコミットされた後にジョブがエンキューされるようになりました。
この機能は Rails 7.2 ではデフォルトで有効になっていますが、Rails 8.0 ではこの動作がデフォルトで無効化されています。
従来の挙動に戻すには、 ActiveJob::Base.enqueue_after_transaction_commit = true を追加する必要があります。
class ApplicationJob < ActiveJob::Base
self.enqueue_after_transaction_commit = true
end
なぜ変更されたのか
Rails 7.2 では ActiveJob::Base.enqueue_after_transaction_commit の設定値はシンボルで、:never、:always、:default の3つの値を取ることができました。
-
:neverエンキューを遅延しない -
:alwaysエンキューを常に遅延する -
:defaultキューアダプタで振る舞いを定義する
しかしこの設定では :default の挙動がキューアダプタに依存することになり、どちらの振る舞いをするのかが分かりづらいという問題がありました。
Actually, what adapters do set something else by default? I don't love the
:defaultoption being this mystery box that's dependent on the adapter. That just seems confusing. I think this setting should just either be true or false. Even the:alwaysimplies that there's a:sometimes, which is also just odd.
実際、デフォルトで何か別の設定を行うアダプターってあるの?:defaultオプションがアダプターに依存する謎の箱みたいな存在なのが気に入らない。混乱を招くだけだ。この設定は単純に true か false であるべきだと思う。:alwaysですら:sometimesが存在することを暗示しているようで、これもまた奇妙だ。
https://github.com/rails/rails/pull/52659#issuecomment-2302422632
そこで以下の PR によって Rails 8.0 から設定値が真偽値に変更されました。
この時にデフォルト値が false に変更されたため、Rails 8.0 ではトランザクション内でジョブがスケジューリングされなくなりました。
この変更は Changelog に記載されておらず、Rails 8.0 のアップデート時に問題になる可能性があるので注意が必要です。
デフォルト値に関しては議論が続いており、DHH も以下のように言及しています。
Yes, by default, all jobs should enqueue after the commit. And this should be a setting that Active Job controls. And it should also be downplayed as something people might want to change. I don't think people should change this. In fact, if we didn't have the history of going back and forth on it, I wouldn't even have made it a setting.
はい、デフォルトではすべてのジョブはコミット後にエンキューされるべきです。そしてこれは Active Job が制御する設定であるべきです。そして人々が変更したいと思うようなものとして軽視されるべきです。人々がこれを変更すべきだとは思いません。実際、これまでの歴史がなければ、私はこれを設定にすることすらしなかったでしょう。
https://github.com/rails/rails/pull/53375#issuecomment-2555694252
デフォルト値を true に戻す PR も提出されているので、今後の動向に注目したいところです。
ちなみに Rails 8.1 からシンボルでの設定は廃止されます。ただしこのリリースノートにもデフォルト値に関する記載はありません。
ActiveJob::Base.enqueue_after_transaction_commitに:never、:always、:defaultを設定するサポートを削除。- 非推奨化されていた
Rails.application.config.active_job.enqueue_after_transaction_commitを削除。
おわりに
Rails 7.2 で導入された Active Job のトランザクション内スケジューリング機能は便利ですが、Rails 8.0 ではデフォルトで無効化されているため、Rails 8.0 にアップデートする際は注意が必要です。
Rails 8.0 でこの機能を有効化したい場合は、 ActiveJob::Base.enqueue_after_transaction_commit = true を追加してください。
Kaigi on Rails 2025 でもこの話題について発表がありましたので、興味がある方はぜひご覧ください。
Discussion