【问题标题】:Rescue "RecordNotFound" errors in DelayedJob workers抢救 DelayedJob 工作人员中的“RecordNotFound”错误
【发布时间】:2013-03-04 21:47:10
【问题描述】:

我使用 DelayedJob 在后台处理某些任务。

例如,在我的应用程序中,用户可以“点赞”一条消息,当这种情况发生时,发帖人会收到通知。此通知在后台处理。

有时,点赞者决定在通知发出之前撤消他的操作并删除他的“点赞”。在这些情况下,后台代码会遇到“RecordNotFound”错误,因为“like”不再存在。

我以为我是通过挽救错误来处理这种情况的(这里的 self 是 Like):

  def send_push_notifications
    begin
      user = self.message.user
      message = "#{self.user.name} liked your workout"
      Urbanairship::push_now(user, message, ["message", self.message.id]) if self.user != user
    rescue ActiveRecord::RecordNotFound
      # Do nothing
    end
  end

但是,实际上这似乎并不能挽救错误,因为我仍然在日志中看到此类错误:

{ActiveRecord::RecordNotFound, class: Like , primary key: 1557 
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.2/lib/delayed/serialization/active_record.rb:12:in `rescue in yaml_new'
/app/vendor/bundle/ruby/1.9.1/gems/delayed_job-3.0.2/lib/delayed/serialization/active_record.rb:6:in `yaml_new'
/usr/local/lib/ruby/1.9.1/syck.rb:135:in `transfer'
/usr/local/lib/ruby/1.9.1/syck.rb:135:in `node_import'
/usr/local/lib/ruby/1.9.1/syck.rb:135:in `load'
/usr/local/lib/ruby/1.9.1/syck.rb:135:in `load'

任何想法为什么我的救援声明在这种情况下不起作用?

【问题讨论】:

  • 在 Urbanairship::push_now 调用之前它会失败吗?你能粘贴整个堆栈跟踪吗?
  • 抱歉,这里是完整的堆栈跟踪:gist.github.com/pejmanjohn/8cf9f1f1d55a90f1ae39
  • 您确定错误发生在您的begin 和rescue 之间吗?尝试在begin 之后直接添加raise ActiveRecord::RecordNotFound。如果可行,并且被捕获,请将其直接移至 rescue 之前并尝试。也许这可以给你一些线索......
  • @244an 这是一个公平的观点。我通过在调用者上添加另一个开始/救援到此方法进行测试,但我仍然看到问题。另外,这是我在 DelayedJob 中看到的,它指出处理程序是“send_push_notifications”。还有其他想法吗? dl.dropbox.com/u/443447/…
  • 你的一切都升级了吗? DelayedJob 正在反序列化 YAML 和所有爵士乐。也许那里有不兼容?除此之外,有没有办法让你显式调用 Like#find 并绕过 DJ 的内置内容。

标签: ruby-on-rails delayed-job rescue


【解决方案1】:

Like 对象不存在,因此带有rescue 的方法甚至没有被调用。

尝试将延迟方法定义为类方法,而不是实例方法。这样,延迟作业将能够执行该方法,即使该实例不存在。例如,

class Like < ActiveRecord::Base

...

def self.send_push_notifications(like_id=nil)
  begin
    like = Like.find like_id
    user = like.message.user
    message = "#{like.user.name} liked your workout"
    Urbanairship::push_now(user, message, ["message", like.message.id]) if like.user != user
  rescue ActiveRecord::RecordNotFound
    # Do nothing
  end
end

...

end

【讨论】:

  • 不,那不完全正确。 Delayed_job 会重试失败的作业。它保持计数,最多重试 n 次,其中 n 是可配置的(我相信默认为 25)。它失败的次数越多,它在重试之前等待的时间就越长。 DelayedJob.retry_at 属性是下一次尝试的时间。现在仔细观察,我认为您的救援没有被调用,因为 Like 对象不存在。将send_push_notifications重新定义为类方法,而不是实例方法,那么delayed_job即使对象不存在也可以调用。如果您需要更多说明,我会编辑答案。
  • 我们将不胜感激。我可以告诉你,我正在检查重试属性,这些作业的重试属性为“0”,而对于其他失败的作业,我确实看到多次退休。
【解决方案2】:

我终于通过使用类方法而不是实例方法进行延迟调用来解决这个问题。让我以示例的方式向您展示。以下是我之前构建延迟调用的方式:

  def background_send_push_notifications
    self.delay.send_push_notifications
  end

  def send_push_notifications
    message = "#{self.user.name} liked your workout"
    ...
  end

我一直遇到的问题是,用户在喜欢某件事后立即不喜欢是很常见的。这意味着当延迟作业尝试执行时,Like 对象不再存在,并且我会收到很多“RecordNotFound”错误。

现在我已将延迟调用转换为在后台执行对象查找并在对象不再存在时返回的类方法。这是新结构

  def background_send_push_notifications
    Like.delay.send_push_notifications(self.id)
  end

  def self.send_push_notifications(id)
    like = Like.find_by_id(id)
    like.send_push_notifications unless like.nil?
  end

  def send_push_notifications
    message = "#{self.user.name} liked your workout"
    ...
  end

希望这对某人有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多