我在 Heroku 上遇到了一个奇怪的错误,我认为这可能是一个竞争条件,我正在寻找解决它的任何建议。
我的应用程序有一个模型,该模型在创建后调用外部 API(Twilio,如果您好奇的话)。在这个调用中,它传递一个 url,一旦第三方完成他们的工作就会被回调。像这样:
class TextMessage < ActiveRecord::Base
after_create :send_sms
def send_sms
call.external.third.party.api(
:callback => sent_text_message_path(self)
)
end
end
然后我有一个 Controller 来处理回调:
class TextMessagesController < ActiveController::Base
def sent
@textmessage = TextMessage.find(params[:id])
@textmessage.sent = true
@textmessage.save
end
end
问题是第三方报告他们在回调中收到 404 错误,因为模型尚未创建。我们自己的日志证实了这一点:
2014-03-13T18:12:10.291730+00:00 app[web.1]: ActiveRecord::RecordNotFound (Couldn't find TextMessage with id=32)
我们检查过,ID 是正确的。更疯狂的是,我们在创建模型时加入了一个 puts
来记录,这就是我们得到的:
2014-03-13T18:15:22.192733+00:00 app[web.1]: TextMessage created with ID 35.
2014-03-13T18:15:22.192791+00:00 app[web.1]: ActiveRecord::RecordNotFound (Couldn't find TextMessage with id=35)
注意时间戳。这些事情似乎相隔 58 毫秒发生,所以我认为这是一个竞争条件。我们正在使用 Heroku(至少用于暂存),所以我认为问题可能出在他们的虚拟 Postgres 数据库上。
有没有人遇到过这种问题?如果是这样,您是如何解决的?有什么建议吗?
最佳答案
after_create
在保存文本消息的数据库事务中处理。因此,命中另一个 Controller 的回调无法读取文本消息。在数据库事务中进行外部调用不是一个好主意,因为事务会在缓慢的外部请求占用的整个时间内阻塞部分数据库。
最简单的解决方案是将 after_save
替换为 after_commit
(参见:http://apidock.com/rails/ActiveRecord/Transactions/ClassMethods/after_commit)
由于回调往往变得难以理解(并可能在测试时导致问题),我更愿意通过调用另一个方法来明确调用。也许是这样的:
# use instead of .save
def save_and_sent_sms
save and sent_sms
end
也许你想在后台发送短信,这样它就不会减慢用户的网络请求。搜索 gem delayed_job 或 resque 以获取更多信息。
关于ruby-on-rails - Ruby on Rails 中的竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22389287/