Back to Blog

Gotcha with after_commit callback in Rails

on March 1, 2015

after_commit callback in Rails is triggered after the end of transaction.

For eg. I have a Post model for which the number of lines of the content are calculated in the after_commit callback:

1class Post < ActiveRecord::Base
2  after_commit :calculate_total_lines, if: -> (post) { post.previous_changes.include?(:content) }
3
4  def calculate_total_lines
5    update! total_lines: content.split("\n").length
6  end
7end
1post = Post.create! content: "Lets discuss Rails 5.\n", author: 'Prathamesh'
2assert_equal 1, post.total_lines

Now lets wrap the creation of post inside a transaction block:

1Post.transaction do
2  post = Post.create! content: "Lets discuss Rails 5.\n", author: 'Prathamesh'
3  assert_equal 1, post.total_lines
4end

The test will fail now.

1#   1) Failure:
2# BugTest#test_within_transaction [after_commit_test.rb:45]:
3# Expected: 1
4#   Actual: nil

Why? Lets recall. after_commit callback will get executed after the end of transaction.

So until all the code inside transaction is completed, the callback is not going to get executed.

Here is a gist with complete test.

Next time you are using an after_commit callback and a transaction, make sure that code inside the transaction is not dependent on the result of the callback.


You might also like

If you liked this blog post, check out similar ones from BigBinary