【问题标题】:Race Condition in Ruby on RailsRuby on Rails 中的竞争条件
【发布时间】:2014-03-13 19:44:57
【问题描述】:

我在 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 数据库上。

以前有人遇到过这种问题吗?如果是这样,你是如何解决的?有什么建议吗?

【问题讨论】:

    标签: ruby-on-rails ruby postgresql heroku race-condition


    【解决方案1】:

    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 了解更多信息。

    【讨论】:

      【解决方案2】:

      您是否有主/从数据库,您总是向主数据库写入但从从属数据库读取?这听起来像是数据库复制滞后。

      我们通过在特定控制器操作中强制从主数据库进行读取来解决此类问题。

      另一种方法是在复制完成后调用send_sms。

      【讨论】:

      • 这听起来完全正确,但我们使用的是 Heroku,因此我们无权访问我们的任何数据库设置。延迟 SMS 是我们找到的一种解决方案,但我们也注意到我们也可以在发送的回调中加入延迟。两者看起来都很丑。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-09
      • 2011-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多