当我在我的应用程序中创建一个新的 User
时,我运行 after_create
方法然后异步触发Sidekiq工作程序 .
这个 Worker ......
def perform user_id
@user = ::User.find user_id
# ...
end
...始终返回以下错误:
ActiveRecord :: RecordNotFound:找不到'id'的用户= 315928979197048617
但是使用rails控制台来检查User id是否可以找到:
User.find(315928979197048617)
=> #<User id: 315928979197048617>
这是否会发生,因为新用户的创建需要一些时间,并且工作人员在完成后已经执行了该方法?如果,我将如何解决这种行为?
1 回答
你是绝对正确的,Active Record中的
after_create
钩实际上在记录完全提交到数据库之前运行 . 这会导致您的工作人员脚本和数据库出现竞争条件,其结果是工作人员中的错误(尽管如果数据库赢得竞争,那么一切似乎都按计划进行) .要确保在工作程序尝试访问之前将用户保存到数据库,应使用
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来克服这个问题 .