Question

I created a simplistic achievements system and wanted to introduce delayed_job (2.1.4) to take care of the processing. However, the handler column in the delayed_jobs table is always nil, which results in the last_error text: Job failed to load: instance of IO needed. Handler nil

Here is my setup:

Achievement Observer

class AchievementObserver < ActiveRecord::Observer
  observe User, Comment, ...

  def after_create(record)
    # initiate delayed job to check conditions
    Delayed::Job.enqueue(TrophyJob.new(record.id, record.class.name))
  end
  ...
end

Trophy Job

class TrophyJob < Struct.new(:record_id, :record_type)
  def perform
    case record_type
    when "User"
      UserProfileCompleteTrophy.progress(User.find(record_id)) # complete your profile settings
      NewsletterReceiverTrophy.progress(User.find(record_id)) # sign up for the newsletter
    when "Comment"
      CommentsAuthoredTrophy.progress(Comment.find(record_id)) # create x comments
    ...
    end
  end
end

The entry in the delayed_jobs table gets created. However, the handler is always NULL. I've already tried various things (passed through complete objects before, now went for id + classname as described here: Weird exception with delayed_job; tried xxxTrophy.delay.progress(...) in the observer; etc.) - all without luck.

I also have

require 'yaml'
YAML::ENGINE.yamler= 'syck'

in my boot.rb.

One thing worth mentioning: Although the last_error text gets filled by delayed_job, the attempts and failed_at columns remain NULL.

What am I missing?

UPDATE

I verified that serialization works as one would expect:

ruby-1.9.2-p290 :004 > TrophyJob.new(1, "User").to_yaml
 => "--- !ruby/struct:TrophyJob \nrecord_id: 1\nrecord_type: User\n"
Was it helpful?

Solution

Found the solution: my problem was caused by mass-assignment protection. I have an initializer to protect against mass-assignment:

# SECURITY: protect against mass assignment vulnerabilities
# enforces explicitly setting attributes accessible in models (whitelisting)
ActiveRecord::Base.send(:attr_accessible, nil)

This prevented delayed_job to access the handler field! Not sure if this can be considered a bug in DJ. Here is the initializer code that solved my problem:

# Imortant: delayed job requires some attributes to be accessible - make sure they are
Delayed::Job.attr_accessible :priority, :payload_object, :handler, :run_at, :failed_at

OTHER TIPS

It's possible that your handler column is too small for what's being put into it. you can fix this in a migration like so:

change_column :delayed_jobs, :handler, :text, :limit => 64.kilobytes + 1
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top