我遇到了一个关于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
然后我有一个控制器来处理回调:
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数据库是问题所在。
之前有没有人遇到过这类问题?如果是这样,你是如何解决的?有什么建议吗?
答案 0 :(得分:2)
after_create
以保存文本消息。因此,命中另一个控制器的回调无法读取文本消息。在数据库事务中进行外部调用并不是一个好主意,因为事务会在外部请求缓慢的整个过程中阻塞部分数据库。
简单的解决方案是将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
也许你想在后台发送短信,所以它不会减慢用户的网络请求。搜索gems delayed_job或resque以获取更多信息。
答案 1 :(得分:0)
你有主/从数据库,你总是写到主数据库,但从奴隶读取?这听起来像db复制滞后。
我们通过强制在特定控制器操作中从master数据库进行读取来解决此类问题。
另一种方法是在复制完成后调用send_sms
。