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?
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!