Question

I use DelayedJob to handle certain tasks in the background.

For example, in my application a user can "like" a message, and when that happens the poster gets notified. This notification is handled in the background.

Occasionally, what can happen is that the liker decides to undo his action and delete his "like" before the notification goes out. In those instances the background code hits a "RecordNotFound" error as the "like" no longer exists.

I thought I handled this case by rescuing the error as so (self here is the 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

However, in practice this does not seem to be rescuing the errors, as I still see such errors in my logs:

{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'

Any ideas why my rescue statement is not working in this case?

Was it helpful?

Solution 2

I finally solved this by using a class method instead of an instance method for my delay calls. Let me show you by way of example. Here's how I previously structured my delay calls:

  def background_send_push_notifications
    self.delay.send_push_notifications
  end

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

The issue I kept hitting was that it was common for a user to immediately unlike, right after liking something. This meant that the Like object was no more when the delayed_job tried to execute, and I'd get lots of "RecordNotFound" errors.

Now I have transitioned the delay call to a class method that does the object lookup in the background and returns if it no longer exists. Here's the new structure

  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

Hope this helps somebody!

OTHER TIPS

The Like object does not exist, so the method with rescue is not even being called.

Try defining your delayed method as a class method, not an instance method. That way, delayed job will be able to execute the method, even if the instance does not exist. E.g.,

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top