Slide 1

Slide 1 text

クイズを作ってわかったこと Rails 7.2 のリリースノートを見て思ったこと 2024/11/12 After Kaigi on Rails 2024 LT Night ikaruga / Atsushi Katsuura 1

Slide 2

Slide 2 text

誰 ikaruga / Atsushi Katsuura 渋谷からきました。 普段はRubyやPHPを読み書きしたりゲーセンに通ったり日本 酒とクラフトジンを飲んだりしています。 Resolv::DNS.new.getresources( 'me.ikaruga.org', Resolv::DNS::Resource::IN::TXT ).map(&:data) => ["GITHUB=ikaruga777", "TWITTER=@UVB_76", "WORK=GMO Pepabo Inc.", "[email protected]"] 2

Slide 3

Slide 3 text

Topic.transaction do topic = Topic.create NewTopicNotificationJob.perform_later(topic) end 3

Slide 4

Slide 4 text

(Rails 7.1)こんなコードがあるとして class UserJob < ApplicationJob queue_as :default def perform(id) User.find(id) # ---------- これ、どうなる? end end User.transaction do user = User.create! UserJob.perform_later(user.id) end 4

Slide 5

Slide 5 text

Rails 7.1 class UserJob < ApplicationJob queue_as :default def perform(id) User.find(id) # ---------- これ、どうなる? end end User.transaction do user = User.create! UserJob.perform_later(user.id) end コミット前にUserJob内でfindされてRecordNotFoundが発生する(可能性がある) 5

Slide 6

Slide 6 text

はずだった 6

Slide 7

Slide 7 text

Rails 7.2 Release Notes Now Active Job will automatically defer the enqueuing to after the transaction is committed, and drop the job if the transaction is rolled back. https://guides.rubyonrails.org/7_2_release_notes.html#prevent-jobs-from-being- scheduled-within-transactions 7

Slide 8

Slide 8 text

Rails 7.2 では class UserJob < ApplicationJob queue_as :default def perform(id) User.find(id) # コミット後に実行されることが保障されて最高 end end User.transaction do user = User.create! UserJob.perform_later(user.id) # コミットされるまでエンキューが遅延される end 8

Slide 9

Slide 9 text

Rails 7.2 では class UserJob < ApplicationJob queue_as :default self.enqueue_after_transaction_commit = :never # ++ オプションがある def perform(id) User.find(id) end end User.transaction do user = User.create! UserJob.perform_later(user.id) # いままでどおり即時エンキュー end 9

Slide 10

Slide 10 text

Kaigi on Rails 2024 Day 1 https://world.hey.com/this.week.in.rails/active-job-config- enqueue_after_transaction_commit-has-been-deprecated-20ecc5f7 10

Slide 11

Slide 11 text

""deprecated"" 11

Slide 12

Slide 12 text

enqueue_after_transaction_commit の設定使っていたら警告出すぜ ( 8.0.0 で適用されている ) https://world.hey.com/this.week.in.rails/active-job-config- enqueue_after_transaction_commit-has-been-deprecated-20ecc5f7 12

Slide 13

Slide 13 text

便利! 13

Slide 14

Slide 14 text

完 14

Slide 15

Slide 15 text

ではない 15

Slide 16

Slide 16 text

果たして直感的か? User.transaction do user = User.create! UserJob.perform_later(user.id) # --コミットされるまでエンキューが遅延される end 16

Slide 17

Slide 17 text

びっくり挙動かも(個人の感想) 安全になるとはいえ... 他の言語やフレームワーク利用者からすると直感的ではなさそう 複数のバージョンのRailsアプリケーション見てると、それぞれで「これトランザ クションの中に書いても安全なんだっけ?」と気にしないといけないことが増える 17

Slide 18

Slide 18 text

手札 今までどおり外に出すを徹底する User.transaction do @user = User.create! end UserJob.perform_later(@user.id) コールバックを明示する User.transaction do @user = User.create! transaction.after_commit do UserJob.perform_later(@user.id) end end 18

Slide 19

Slide 19 text

できることないかね 19

Slide 20

Slide 20 text

廊下にて < Copかけるのでは? Transactionの中でperform_laterぽいことしたら add_offense するとよい 20

Slide 21

Slide 21 text

Cop 10秒クッキング "Transactionの中で" すでにある rubocop-rails lib/rubocop/cop/rails/transaction_exit_statement.rb https://github.com/rubocop/rubocop- rails/blob/master/lib/rubocop/cop/rails/transaction_exit_statement.rb#L98 21

Slide 22

Slide 22 text

Cop 10秒クッキング "エンキューしたら" Claudeに聞いた(割愛) 22

Slide 23

Slide 23 text

Cop 10秒クッキング できた https://gist.github.com/ikaruga777/b8b637f8db25a4d6ec8a4e40753fbc05 $ rubocop example.rb Inspecting 1 file C Offenses: example.rb:2:3: C: ActiveJob/PerformInTransactions: トランザクションの中でperform_laterすなー Job.set(:wait_until).perform_later ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ example.rb:9:7: C: ActiveJob/PerformInTransactions: トランザクションの中でperform_laterすなー SomeJob.perform_later(id) # Copが警告を出す ^^^^^^^^^^^^^^^^^^^^^^^^^ 1 file inspected, 2 offenses detected 23

Slide 24

Slide 24 text

メソッド跨ぐと検知できないという致命的な欠陥 24

Slide 25

Slide 25 text

動的解析じゃ(思想) (すごく雑に書くと) 遅延されるジョブのエンキュー処理は after_all_transactions_commit というコールバックに登録される そのコールバックをフックして検知する 25

Slide 26

Slide 26 text

まとめ 7.2 からはトランザクション内でのエンキューが遅延されるので安全になりそう 自分は外に出してた方が直感的だと思ったので、検知する方法を会期中に考えて いた 廊下の議論ってのも楽しいですね 26

Slide 27

Slide 27 text

おまけ これの実装 ARに after_all_transactions_commit というコールバックが増えた https://github.com/Shopify/rails/blame/main/activerecord/lib/active_record.rb#L514-L545 ジョブをエンキューする時に、設定が有効だったら after_all_transactions_commit に登 録するってことをしている https://github.com/Shopify/rails/blob/main/activejob/lib/active_job/enqueue_after_transac tion_commit.rb https://github.com/Shopify/rails/blob/1b4c73e2a17e592b007e00f083ef4f9b0703ebcf/a ctivejob/lib/active_job.rb#L42 27