1

当我在我的应用程序中创建一个新的User时,我运行一个after_create方法,然后异步触发一个Sidekiq工作人员。Sidekiq在工作人员没有找到对象

这个工人...

def perform user_id 
    @user = ::User.find user_id 
    # ... 
end 

...总是返回以下错误:

ActiveRecord::RecordNotFound: Couldn't find User with 'id'=315928979197048617

但是用钢轨控制台来检查,如果用户ID容易找到:

User.find(315928979197048617) 
=> #<User id: 315928979197048617> 

这是否发生是因为t他创建新用户需要一些时间,并且工作人员在完成时已经执行了该方法?如果我如何解决这个问题?

回答

3

你是绝对正确的,Active Record中的after_create钩子实际上在记录完全提交到数据库之前运行。这会导致与你的工作者脚本和数据库的竞争状况,其结果是工作者中的ActiveRecord::RecordNotFound错误(尽管如果数据库赢得比赛,那么一切似乎都按计划进行)。

为确保您的用户在工作人员尝试访问数据库之前已保存到数据库,您应该使用after_commit回调。由于您只是在创建时执行此操作,因此您需要after_commit on: :create

这确实会导致最后一个问题,after_commit回调似乎无法在测试下运行。这是因为Rails在事务中包装测试,并且测试事务从未实际提交,所以回调不会触发。 This blog post has a good write up on using after_commit并建议test_after_commit gem来解决这个问题。