We write about Ruby on Rails, React.js, React Native, remote work, open source, engineering and design.
Rails 6.0 was recently released.
Rails 6 fixes a bug where after_commit callbacks are called on failed update in a transaction block.
Let's checkout the bug in Rails 5.2 and the fix in Rails 6.
Let's define an
after_commit
callback in User
model and try updating an
invalid user object in a transaction block.
1>> class User < ApplicationRecord
2>> validates :name, :email, presence: true
3>>
4>> after_commit :show_success_message
5>>
6>> private
7>>
8>> def show_success_message
9>> p 'User has been successfully saved into the database.'
10>> end
11>> end
12
13=> :show_success_message
14
15>> user = User.create(name: 'Jon Snow', email: 'jon@bigbinary.com')
16begin transaction
17User Create (0.8ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "Jon Snow"], ["email", "jon@bigbinary.com"], ["created_at", "2019-07-14 15:35:33.517694"], ["updated_at", "2019-07-14 15:35:33.517694"]]
18commit transaction
19"User has been successfully saved into the database."
20
21=> #<User id: 1, name: "Jon Snow", email: "jon@bigbinary.com", created_at: "2019-07-14 15:35:33", updated_at: "2019-07-14 15:35:33">
22
23>> User.transaction do
24>> user.email = nil
25>> p user.valid?
26>> user.save
27>> end
28begin transaction
29false
30commit transaction
31"User has been successfully saved into the database."
32
33=> false
As we can see here, that that the after_commit callback show_success_message
was called even if object was never saved in the transaction.
Now, let's try the same thing in Rails 6.
1>> class User < ApplicationRecord
2>> validates :name, :email, presence: true
3>>
4>> after_commit :show_success_message
5>>
6>> private
7>>
8>> def show_success_message
9>> p 'User has been successfully saved into the database.'
10>> end
11>> end
12
13=> :show_success_message
14
15>> user = User.create(name: 'Jon Snow', email: 'jon@bigbinary.com')
16SELECT sqlite_version(*)
17begin transaction
18User Create (1.0ms) INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "Jon Snow"], ["email", "jon@bigbinary.com"], ["created_at", "2019-07-14 15:40:54.022045"], ["updated_at", "2019-07-14 15:40:54.022045"]]
19commit transaction
20"User has been successfully saved into the database."
21
22=> #<User id: 1, name: "Jon Snow", email: "jon@bigbinary.com", created_at: "2019-07-14 15:40:54", updated_at: "2019-07-14 15:40:54">
23
24>> User.transaction do
25>> user.email = nil
26>> p user.valid?
27>> user.save
28>> end
29false
30
31=> false
Now, we can see that after_commit callback was never called if the object was not saved.
Here is the relevant issue and the pull request.