When I create a new User
in my app, I run an after_create
method which then asynchronously triggers a Sidekiq worker.
This worker ...
def perform user_id
@user = ::User.find user_id
# ...
end
... always returns the following error:
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=315928979197048617
But using the rails console to check if the User id findable:
User.find(315928979197048617)
=> #<User id: 315928979197048617>
Does this happen because the creation of the new User takes some time and the worker has already performed the method when finished? If, how would I fix that behavior?
You are absolutely right, the after_create
hook in Active Record actually runs before the record is fully committed to the database. This causes a race condition with your worker script and the database, the results of which are ActiveRecord::RecordNotFound
errors in the worker (though if the database wins the race then everything seems to go as planned).
To ensure that your user has been saved to the database before your worker tries to access it, you should use the after_commit
callback. Since you are only performing this operation on creation, you need after_commit on: :create
.
This does cause one last problem, after_commit
callbacks never seem to run under testing. This is because Rails wraps tests in transactions and the test transaction is never actually committed so the callback doesn't fire. This blog post has a good write up on using after_commit
and recommends the test_after_commit
gem to overcome this issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With