1

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"
Community
  • 1
  • 1
emrass
  • 6,253
  • 3
  • 35
  • 57
  • Have you tried removing the yaml engine customization (or is that even possible)? – Nick Nov 01 '11 at 13:04
  • This line is necessary for YAML to work (e.g. for my locales). I only mentioned it to show that I considered the problem might be caused by a YAML serialization issue - but find this unlikely. Thanks though. – emrass Nov 01 '11 at 15:18

2 Answers2

4

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
emrass
  • 6,253
  • 3
  • 35
  • 57
  • Didn't work for me until I did: Delayed::Backend::ActiveRecord::Job.attr_accessible – nivcaner Dec 11 '12 at 11:01
  • Thanks for the update - maybe there was a change in DelayedJob that caused this for you. I'll include your comment in the answer if someone else can confirm this (plus the DJ version you are using would be helpful) – emrass Dec 11 '12 at 12:47
1

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
John Bachir
  • 22,495
  • 29
  • 154
  • 227
  • Thanks for the hint. I tried that with no luck. Since I'm only storing the ID and classname I think a TEXT field should suffice (rather than LONGTEXT). – emrass Nov 01 '11 at 15:20