Commit 9db0c1975e3143930e5f100751465076a2db2d57
1 parent
1b7dcab6
Exists in
master
and in
28 other branches
Rescue and notify exceptions in feed-updater and delayed_job
Showing
3 changed files
with
48 additions
and
10 deletions
Show diff stats
lib/feed_updater.rb
| ... | ... | @@ -19,6 +19,18 @@ end |
| 19 | 19 | # stops the process. |
| 20 | 20 | class FeedUpdater |
| 21 | 21 | |
| 22 | + class ExceptionNotification < ActionMailer::Base | |
| 23 | + def mail error | |
| 24 | + environment = Environment.default | |
| 25 | + | |
| 26 | + recipients NOOSFERO_CONF['exception_recipients'] | |
| 27 | + from environment.contact_email | |
| 28 | + reply_to environment.contact_email | |
| 29 | + subject "[#{environment.name}] Feed-updater: #{error.message}" | |
| 30 | + body render(:text => error.backtrace.join("\n")) | |
| 31 | + end | |
| 32 | + end | |
| 33 | + | |
| 22 | 34 | # indicates how much time one feed will be left without updates |
| 23 | 35 | # (ActiveSupport::Duration). Default: <tt>4.hours</tt> |
| 24 | 36 | cattr_accessor :update_interval |
| ... | ... | @@ -79,6 +91,8 @@ class FeedUpdater |
| 79 | 91 | feed_handler.process(container) |
| 80 | 92 | end |
| 81 | 93 | end |
| 94 | + rescue Exception => e | |
| 95 | + FeedUpdater::ExceptionNotification.deliver_mail e if NOOSFERO_CONF['exception_recipients'].present? | |
| 82 | 96 | end |
| 83 | 97 | end |
| 84 | 98 | ... | ... |
script/delayed_job
| ... | ... | @@ -10,4 +10,25 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'envi |
| 10 | 10 | require 'daemons' |
| 11 | 11 | require 'delayed/command' |
| 12 | 12 | |
| 13 | +# based on https://groups.google.com/forum/#!topic/delayed_job/ZGMUFFppNgs | |
| 14 | +class Delayed::Worker | |
| 15 | + class ExceptionNotification < ActionMailer::Base | |
| 16 | + def mail error | |
| 17 | + environment = Environment.default | |
| 18 | + | |
| 19 | + recipients NOOSFERO_CONF['exception_recipients'] | |
| 20 | + from environment.contact_email | |
| 21 | + reply_to environment.contact_email | |
| 22 | + subject "[#{environment.name}] DelayedJob: #{error.message}" | |
| 23 | + body render(:text => error.backtrace.join("\n")) | |
| 24 | + end | |
| 25 | + end | |
| 26 | + | |
| 27 | + def handle_failed_job_with_notification job, error | |
| 28 | + Delayed::Worker::ExceptionNotification.deliver_mail error if NOOSFERO_CONF['exception_recipients'].present? | |
| 29 | + handle_failed_job_without_notification job, error | |
| 30 | + end | |
| 31 | + alias_method_chain :handle_failed_job, :notification | |
| 32 | +end | |
| 33 | + | |
| 13 | 34 | Delayed::Command.new(ARGV).daemonize | ... | ... |
vendor/plugins/delayed_job/lib/delayed/worker.rb
| ... | ... | @@ -10,12 +10,12 @@ module Delayed |
| 10 | 10 | self.max_attempts = 25 |
| 11 | 11 | self.max_run_time = 4.hours |
| 12 | 12 | self.default_priority = 0 |
| 13 | - | |
| 13 | + | |
| 14 | 14 | # By default failed jobs are destroyed after too many attempts. If you want to keep them around |
| 15 | 15 | # (perhaps to inspect the reason for the failure), set this to false. |
| 16 | 16 | cattr_accessor :destroy_failed_jobs |
| 17 | 17 | self.destroy_failed_jobs = true |
| 18 | - | |
| 18 | + | |
| 19 | 19 | self.logger = if defined?(Merb::Logger) |
| 20 | 20 | Merb.logger |
| 21 | 21 | elsif defined?(RAILS_DEFAULT_LOGGER) |
| ... | ... | @@ -24,9 +24,9 @@ module Delayed |
| 24 | 24 | |
| 25 | 25 | # name_prefix is ignored if name is set directly |
| 26 | 26 | attr_accessor :name_prefix |
| 27 | - | |
| 27 | + | |
| 28 | 28 | cattr_reader :backend |
| 29 | - | |
| 29 | + | |
| 30 | 30 | def self.backend=(backend) |
| 31 | 31 | if backend.is_a? Symbol |
| 32 | 32 | require "delayed/backend/#{backend}" |
| ... | ... | @@ -35,7 +35,7 @@ module Delayed |
| 35 | 35 | @@backend = backend |
| 36 | 36 | silence_warnings { ::Delayed.const_set(:Job, backend) } |
| 37 | 37 | end |
| 38 | - | |
| 38 | + | |
| 39 | 39 | def self.guess_backend |
| 40 | 40 | self.backend ||= if defined?(ActiveRecord) |
| 41 | 41 | :active_record |
| ... | ... | @@ -97,7 +97,7 @@ module Delayed |
| 97 | 97 | ensure |
| 98 | 98 | Delayed::Job.clear_locks!(name) |
| 99 | 99 | end |
| 100 | - | |
| 100 | + | |
| 101 | 101 | # Do num jobs and return stats on success/failure. |
| 102 | 102 | # Exit early if interrupted. |
| 103 | 103 | def work_off(num = 100) |
| ... | ... | @@ -117,7 +117,7 @@ module Delayed |
| 117 | 117 | |
| 118 | 118 | return [success, failure] |
| 119 | 119 | end |
| 120 | - | |
| 120 | + | |
| 121 | 121 | def run(job) |
| 122 | 122 | runtime = Benchmark.realtime do |
| 123 | 123 | Timeout.timeout(self.class.max_run_time.to_i) { job.invoke_job } |
| ... | ... | @@ -129,7 +129,7 @@ module Delayed |
| 129 | 129 | handle_failed_job(job, e) |
| 130 | 130 | return false # work failed |
| 131 | 131 | end |
| 132 | - | |
| 132 | + | |
| 133 | 133 | # Reschedule the job in the future (when a job fails). |
| 134 | 134 | # Uses an exponential scale depending on the number of failed attempts. |
| 135 | 135 | def reschedule(job, time = nil) |
| ... | ... | @@ -162,13 +162,14 @@ module Delayed |
| 162 | 162 | end |
| 163 | 163 | |
| 164 | 164 | protected |
| 165 | - | |
| 165 | + | |
| 166 | 166 | def handle_failed_job(job, error) |
| 167 | 167 | job.last_error = error.message + "\n" + error.backtrace.join("\n") |
| 168 | 168 | say "#{job.name} failed with #{error.class.name}: #{error.message} - #{job.attempts} failed attempts", Logger::ERROR |
| 169 | 169 | reschedule(job) |
| 170 | + rescue => e # don't crash here | |
| 170 | 171 | end |
| 171 | - | |
| 172 | + | |
| 172 | 173 | # Run the next job we can get an exclusive lock on. |
| 173 | 174 | # If no jobs are left we return nil |
| 174 | 175 | def reserve_and_run_one_job |
| ... | ... | @@ -186,6 +187,8 @@ module Delayed |
| 186 | 187 | end |
| 187 | 188 | |
| 188 | 189 | run(job) if job |
| 190 | + rescue => e | |
| 191 | + handle_failed_job(job, e) | |
| 189 | 192 | end |
| 190 | 193 | end |
| 191 | 194 | end | ... | ... |