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 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__), &#39;..&#39;, &#39;config&#39;, &#39;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
... ...