diff --git a/app/helpers/transaction_helper.rb b/app/helpers/transaction_helper.rb new file mode 100644 index 00000000000..648d0fb948b --- /dev/null +++ b/app/helpers/transaction_helper.rb @@ -0,0 +1,38 @@ +## +# Allows running arbitrary code after the current transaction has been committed. +# Works even with nexted transactions. Useful for scheduling sidekiq jobs. +# Slightly simplified version of https://dev.to/evilmartians/rails-aftercommit-everywhere--4j9g +# Usage: +# Topic.transaction do +# puts "Some work before scheduling" +# TransactionHelper.after_commit do +# puts "Running after commit" +# end +# puts "Some work after scheduling" +# end +# +# Produces: +# > Some work before scheduling +# > Some work after scheduling +# > Running after commit + +module TransactionHelper + class AfterCommitWrapper + def initialize + @callback = Proc.new + end + + def committed!(*) + @callback.call + end + + def before_committed!(*); end + def rolledback!(*); end + end + + def self.after_commit(&blk) + ActiveRecord::Base.connection.add_transaction_record( + AfterCommitWrapper.new(&blk) + ) + end +end diff --git a/app/models/topic.rb b/app/models/topic.rb index 975825a0350..2aa41f6bb6b 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -686,7 +686,9 @@ class Topic < ActiveRecord::Base if post = self.ordered_posts.first notified_user_ids = [post.user_id, post.last_editor_id].uniq - Jobs.enqueue(:notify_category_change, post_id: post.id, notified_user_ids: notified_user_ids) + TransactionHelper.after_commit do + Jobs.enqueue(:notify_category_change, post_id: post.id, notified_user_ids: notified_user_ids) + end end end diff --git a/spec/helpers/transaction_helper_spec.rb b/spec/helpers/transaction_helper_spec.rb new file mode 100644 index 00000000000..d1c4a3e4947 --- /dev/null +++ b/spec/helpers/transaction_helper_spec.rb @@ -0,0 +1,32 @@ +require 'rails_helper' + +describe TransactionHelper do + + it "runs callbacks after outermost transaction is committed" do + outputString = "1" + + # Main transaction + ActiveRecord::Base.transaction do + outputString += "2" + + # Nested transaction + ActiveRecord::Base.transaction do + outputString += "3" + + TransactionHelper.after_commit do + outputString += "6" + end + outputString += "4" + end + + TransactionHelper.after_commit do + outputString += "7" + end + + outputString += "5" + end + + expect(outputString).to eq("1234567") + end + +end