I am using delayed_job to perform a system call. This system call invokes a Groovy script.
class Job
def perform
command "groovy file.groovy"
system command
end
def success(job)
# handle success
end
def error(job, exception)
# handle error, exception
end
end
This is all working perfectly but I always get back a "success" status since my groovy always exits correctly. I am currently throwing RuntimeExeptions in the Groovy Script to provoke failing job. When I call system "groovy progra.groovy" and it raises an exception (The groovy program raises an Exception), the return value of system call is, as expected false. But when doing the same via delayed_job, it never accesses the def error method but the def success method.
Do you have any advise on how delayed_job actually controls the returns of the perform method? When does it enter the error or failure hook. Unfortunately I have not found anything on this topic in the documentation.
Thanks in advance
Have you tried something like this :
def perform
command = "groovy file.groovy"
system command || raise "Error executing command : #{command}"
end
I'm pretty sure delayed_job wraps the perform call in a rescue block and call success or error based on any exception it catches.
Related
I've a ruby script that has been implemented as an independent functionality.
Now I would like to execute this script in my rails environament, with the added difficulty of executing it as a background job, because it needs a great amount of time processing.
After adding the delayed_job gem, I've tryied calling the following sentence:
delay.system("ruby my_script.rb")
And this is the error I get:
Completed 500 Internal Server Error in 95ms
TypeError (can't dump anonymous module: #<Module:0x007f8a9ce14dc0>):
app/controllers/components_controller.rb:49:in `create'
Calling the self.delay method from your controller won't work, because DJ will try to serialize your controller into the Job. You'd better create a class to handle your task then flag its method as asynchronous :
class AsyncTask
def run
system('ruby my_script.rb')
end
handle_asynchronously :run
end
In your controller :
def create
...
AsyncTask.new.run
...
end
See the second example in the "Queing Jobs" section of the readme.
Like Jef stated the best solution is to create a custom job.
The problem with Jef's answer is that its syntax (as far as I know) is not correct and that's his job handles a single system command while the following will allow you more customization:
# lib/system_command_job.rb
class SystemCommandJob < Struct.new(:cmd)
def perform
system(cmd)
end
end
Note the cmd argument for the Struct initializer. It allows you to pass arguments to your job so the code would look like:
Delayed::Job.enqueue(SystemCommandJob.new("ruby my_script.rb"))
I've got a delayed job running and I want to saved the runtime when it finishes, the worker logs the runtime to the logs like this...
XXXXXJob completed after 1.6372
I was wondering how you access this statistic from inside the delayed job class?
I've tried self.runtime but that's throwing an error.
Cheers.
I'm afraid delayed_job gem doesn't provide API for accessing that stats. Looks like here is the place were they calculate running time and that's what is happening there:
runtime = Benchmark.realtime do
invoke_job
destroy
end
logger.info "* [JOB] #{name} completed after %.4f" % runtime
So runtime is calculated using Benchmark module and logged, but it's not stored anywhere for later usage.
Though, you still can calculate runtime of the job yourself by wrapping its logic with similar Benchmark.realtime call or something like that.
I’m working on this issue: Rails exception notifier in rake tasks
My question: Is there any function/plugin/gem/whatever to setup a generic error/exception handler callback as in PHP with set_error_handler and set_exception_handler?
I need a way to register a callback function used as a catchall outside any begin .. rescue .. end block. For example:
def my_handler *args
# exception processing code here
end
some_magic_method my_handler
raise "An Exception" # this will be handled by my_handler
In PHP this could be achieved with the set_exception_handler function. Is there any such function in Ruby/Rails?
If such feature exists I could solve my previous issue in a simple way.
A Rails-only solution would be fine for my needs.
I don't believe Ruby provides a way to do this, either with exceptions or with throw/catch. In general, doing something this way is a code smell and something to avoid. It makes control flow extremely hard to figure out. I would try to find some other way to approach the problem if at all possible.
If you want to do this in the HTTP Request-handling cycle you may use an around filter in your application controller:
class ApplicationController < ActionController::Base
around_filter do |controller, action|
action.call
rescue ExceptionXPTO
# ... handle the exception ...
end
end
I found a partial solution to my issue which works for the simple case I mentioned in the question. However this can not catch the exception, but it can be useful if someone needs only exception logging or reporting.
#!/usr/bin/env ruby
at_exit do
if $!
puts "Program ended with an exception #{$!.message}"
puts $!.backtrace.join("\n")
# or log the exception here
end
end
loop do
value = rand(3)
puts "Value is #{value}"
break if value == 2
raise "An Exception" if value == 0
end
puts "Program ended normally"
I'm trying to make my delayed jobs hooks work, but they don't seem to be. Are they outdated? If they are not, can you show me an example of yours?
Here's mine:
class PhotoJob < Struct.new(:image_id)
def perform
Photo.find(self.image_id).regenerate_styles!
end
def error(job, exception)
if Photo.exists?(self.image_id)
Photo.find(self.image_id).regenerate_styles!
else
Delayed::Job.find(self).destroy
end
end
end
The reason I say they do not work is because if I upload a hundred images, half of them will fail with an error. If the error is raised, then the hook should be run, right?
Here's the catch, if I find the photo that's failing, and run Photo.find(n).regenerate_styles! , then the photo regenerates appropriately and works.
So I'm guessing that Delayed Jobs' hooks are not working.
They've been working ok for me:
class BaseProcessorJob
def perform
raise NotImplementedError
end
def error(job, exception)
Rails.logger.error "Job failed #{exception}"
end
end
I originally had a similar problem with hooks not getting called because of an outdated delayed_job gem (it was actually installed as a plugin, which caused my problems). Not sure if any of that helps you though.
I have been happily using the DelayedJob idiom:
foo.send_later(:bar)
This calls the method bar on the object foo in the DelayedJob process.
And I've been using DaemonSpawn to kick off the DelayedJob process on my server.
But... if foo throws an exception Hoptoad doesn't catch it.
Is this a bug in any of these packages... or do I need to change some configuration... or do I need to insert some exception handling in DS or DJ that will call the Hoptoad notifier?
In response to the first comment below.
class DelayedJobWorker < DaemonSpawn::Base
def start(args)
ENV['RAILS_ENV'] ||= args.first || 'development'
Dir.chdir RAILS_ROOT
require File.join('config', 'environment')
Delayed::Worker.new.start
end
Try monkeypatching Delayed::Worker#handle_failed_job :
# lib/delayed_job_airbrake.rb
module Delayed
class Worker
protected
def handle_failed_job_with_airbrake(job, error)
say "Delayed job failed -- logging to Airbrake"
HoptoadNotifier.notify(error)
handle_failed_job_without_airbrake(job, error)
end
alias_method_chain :handle_failed_job, :airbrake
end
end
This worked for me.
(in a Rails 3.0.10 app using delayed_job 2.1.4 and hoptoad_notifier 2.4.11)
Check out the source for Delayed::Job... there's a snippet like:
# This is a good hook if you need to report job processing errors in additional or different ways
def log_exception(error)
logger.error "* [JOB] #{name} failed with #{error.class.name}: #{error.message} - #{attempts} failed attempts"
logger.error(error)
end
I haven't tried it, but I think you could do something like:
class Delayed::Job
def log_exception_with_hoptoad(error)
log_exception_without_hoptoad(error)
HoptoadNotifier.notify(error)
end
alias_method_chain :log_exception, :hoptoad
end
Hoptoad uses the Rails rescue_action_in_public hook method to intercept exceptions and log them. This method is only executed when the request is dispatched by a Rails controller.
For this reason, Hoptoad is completely unaware of any exception generated, for example, by rake tasks or the rails script/runner.
If you want to have Hoptoad tracking your exception, you should manually integrate it.
It should be quite straightforward. The following code fragment demonstrates how Hoptoad is invoked
def rescue_action_in_public_with_hoptoad exception
notify_hoptoad(exception) unless ignore?(exception) || ignore_user_agent?
rescue_action_in_public_without_hoptoad(exception)
end
Just include Hoptoad library in your environment and call notify_hoptoad(exception) should work. Make sure your environment provides the same API of a Rails controller or Hoptoad might complain.
Just throwing it out there - your daemon should require the rails environment that you're working on. It should look something along the lines of:
RAILS_ENV = ARGV.first || ENV['RAILS_ENV'] || 'production'
require File.join('config', 'environment')
This way you can specify environment in which daemon is called.
Since it runs delayed job chances are daemon already does that (it needs activerecord), but maybe you're only requiring minimal activerecord to make delayed_job happy without rails.