Commit 9db0c1975e3143930e5f100751465076a2db2d57

Authored by Braulio Bhavamitra
1 parent 1b7dcab6

Rescue and notify exceptions in feed-updater and delayed_job

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