Gotcha with after_commit callback in Rails

Prathamesh Sonpatki

By Prathamesh Sonpatki

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.

Stay up to date with our blogs. Sign up for our newsletter.

We write about Ruby on Rails, ReactJS, React Native, remote work,open source, engineering & design.