简体   繁体   中英

Passing a Block to a delayed_job

I have a function that is marked to be handled asynchronously by delayed_job:

class CapJobs
  def execute(params, id)
    begin
      unless Rails.env == "test"
        Capistrano::CLI.parse(params).execute!
      end
    rescue
      site = Site.find(id)
      site.records.create!(:date => DateTime.now, :action => "Task Failure: #{params[0]}", :type => :failure)    
      site.save
    ensure
      yield id
    end 
  end   
  handle_asynchronously :execute
end 

When I run this function I pass in a block:

 capjobs = CapJobs.new
 capjobs.execute(parameters, @site.id) do |id|
   asite = Site.find(id)
   asite.records.create!(:date => DateTime.now, :action => "Created", :type => :init)
   asite.status = "On Demo"
   asite.dev = true
   asite.save
 end

This works fine when run without delayed_job but when run with it I get the following error

2012-08-13T09:24:36-0300: [Worker(delayed_job host:eagle pid:12089)] SitesHelper::CapJobs#execute_without_delay failed with LocalJumpError: no block given (yield) - 0 failed attempts
2012-08-13T09:24:36-0300: [Worker(delayed_job host:eagle pid:12089)] PERMANENTLY removing SitesHelper::CapJobs#execute_without_delay because of 1 consecutive failures.
2012-08-13T09:24:36-0300: [Worker(delayed_job host:eagle pid:12089)] 1 jobs processed at 0.0572 j/s, 1 failed ...

It seems not to pick up the block that was passed in. Is this not the correct way of doing this or should I find a different method?

delayed_job works by saving your jobs into a data store (most often your primary database) and then loading the jobs out of this data store in a background process, where it is handled/executed.

To save a job into the database, delayed_job needs to somehow save what method to call on which object with what arguments. This is done by serializing everything into a string (delayed_job uses yaml for that). Unfortunately, blocks cannot be serialized. So the background worker does not know about the block argument and calls the method without it. This results in the LocalJumpError when the method is trying to yield to the block.

I found a method of doing this. It is kind of hacky but works well. I found this article that talks about creating a SerializableProc class. If I pass this to the function then everything works great.

Most people would treat this as an abstraction problem.

The proc code is probably not changing from run-to-run (except vars) and so you should make the block code into a class or instance method. Pass the name of that method, and then call it in your execute method, like

@some_data = CapJobs.send( target_method )

or perhaps-better-even

@some_data = DomainSpecificModel.send( target_method )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM