delayed_job 4.0.6 → 4.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,16 +5,21 @@ unless ENV['RAILS_ENV'] == 'test'
5
5
  raise "You need to add gem 'daemons' to your Gemfile if you wish to use it."
6
6
  end
7
7
  end
8
+ require 'fileutils'
8
9
  require 'optparse'
10
+ require 'pathname'
9
11
 
10
12
  module Delayed
11
13
  class Command # rubocop:disable ClassLength
12
14
  attr_accessor :worker_count, :worker_pools
13
15
 
16
+ DIR_PWD = Pathname.new Dir.pwd
17
+
14
18
  def initialize(args) # rubocop:disable MethodLength
15
19
  @options = {
16
20
  :quiet => true,
17
- :pid_dir => "#{Rails.root}/tmp/pids"
21
+ :pid_dir => "#{root}/tmp/pids",
22
+ :log_dir => "#{root}/log"
18
23
  }
19
24
 
20
25
  @worker_count = 1
@@ -28,7 +33,7 @@ module Delayed
28
33
  exit 1
29
34
  end
30
35
  opt.on('-e', '--environment=NAME', 'Specifies the environment to run this delayed jobs under (test/development/production).') do |_e|
31
- STDERR.puts 'The -e/--environment option has been deprecated and has no effect. Use RAILS_ENV and see http://github.com/collectiveidea/delayed_job/issues/#issue/7'
36
+ STDERR.puts 'The -e/--environment option has been deprecated and has no effect. Use RAILS_ENV and see http://github.com/collectiveidea/delayed_job/issues/7'
32
37
  end
33
38
  opt.on('--min-priority N', 'Minimum priority of jobs to run.') do |n|
34
39
  @options[:min_priority] = n
@@ -37,11 +42,14 @@ module Delayed
37
42
  @options[:max_priority] = n
38
43
  end
39
44
  opt.on('-n', '--number_of_workers=workers', 'Number of unique workers to spawn') do |worker_count|
40
- @worker_count = worker_count.to_i rescue 1 # rubocop:disable RescueModifier
45
+ @worker_count = worker_count.to_i rescue 1
41
46
  end
42
47
  opt.on('--pid-dir=DIR', 'Specifies an alternate directory in which to store the process ids.') do |dir|
43
48
  @options[:pid_dir] = dir
44
49
  end
50
+ opt.on('--log-dir=DIR', 'Specifies an alternate directory in which to store the delayed_job log.') do |dir|
51
+ @options[:log_dir] = dir
52
+ end
45
53
  opt.on('-i', '--identifier=n', 'A numeric identifier for the worker.') do |n|
46
54
  @options[:identifier] = n
47
55
  end
@@ -69,22 +77,27 @@ module Delayed
69
77
  opt.on('--exit-on-complete', 'Exit when no more jobs are available to run. This will exit if all jobs are scheduled to run in the future.') do
70
78
  @options[:exit_on_complete] = true
71
79
  end
80
+ opt.on('--daemon-options a, b, c', Array, 'options to be passed through to daemons gem') do |daemon_options|
81
+ @daemon_options = daemon_options
82
+ end
72
83
  end
73
- @args = opts.parse!(args)
84
+ @args = opts.parse!(args) + (@daemon_options || [])
74
85
  end
75
86
 
76
87
  def daemonize # rubocop:disable PerceivedComplexity
77
88
  dir = @options[:pid_dir]
78
- Dir.mkdir(dir) unless File.exist?(dir)
89
+ FileUtils.mkdir_p(dir) unless File.exist?(dir)
79
90
 
80
91
  if worker_pools
81
92
  setup_pools
82
93
  elsif @options[:identifier]
94
+ # rubocop:disable GuardClause
83
95
  if worker_count > 1
84
96
  raise ArgumentError, 'Cannot specify both --number-of-workers and --identifier'
85
97
  else
86
98
  run_process("delayed_job.#{@options[:identifier]}", @options)
87
99
  end
100
+ # rubocop:enable GuardClause
88
101
  else
89
102
  worker_count.times do |worker_index|
90
103
  process_name = worker_count == 1 ? 'delayed_job' : "delayed_job.#{worker_index}"
@@ -114,18 +127,19 @@ module Delayed
114
127
  end
115
128
 
116
129
  def run(worker_name = nil, options = {})
117
- Dir.chdir(Rails.root)
130
+ Dir.chdir(root)
118
131
 
119
132
  Delayed::Worker.after_fork
120
- Delayed::Worker.logger ||= Logger.new(File.join(Rails.root, 'log', 'delayed_job.log'))
133
+ Delayed::Worker.logger ||= Logger.new(File.join(@options[:log_dir], 'delayed_job.log'))
121
134
 
122
135
  worker = Delayed::Worker.new(options)
123
136
  worker.name_prefix = "#{worker_name} "
124
137
  worker.start
125
138
  rescue => e
126
- Rails.logger.fatal e
127
139
  STDERR.puts e.message
128
- exit 1
140
+ STDERR.puts e.backtrace
141
+ ::Rails.logger.fatal(e) if rails_logger_defined?
142
+ exit_with_error_status
129
143
  end
130
144
 
131
145
  private
@@ -134,13 +148,25 @@ module Delayed
134
148
  @worker_pools ||= []
135
149
 
136
150
  queues, worker_count = pool.split(':')
137
- if ['*', '', nil].include?(queues)
138
- queues = []
139
- else
140
- queues = queues.split(',')
141
- end
151
+ queues = ['*', '', nil].include?(queues) ? [] : queues.split(',')
142
152
  worker_count = (worker_count || 1).to_i rescue 1
143
153
  @worker_pools << [queues, worker_count]
144
154
  end
155
+
156
+ def root
157
+ @root ||= rails_root_defined? ? ::Rails.root : DIR_PWD
158
+ end
159
+
160
+ def rails_root_defined?
161
+ defined?(::Rails.root)
162
+ end
163
+
164
+ def rails_logger_defined?
165
+ defined?(::Rails.logger)
166
+ end
167
+
168
+ def exit_with_error_status
169
+ exit 1
170
+ end
145
171
  end
146
172
  end
@@ -8,5 +8,5 @@ module Delayed
8
8
  end
9
9
  end
10
10
 
11
- class FatalBackendError < Exception; end
11
+ class FatalBackendError < RuntimeError; end
12
12
  end
@@ -1,5 +1,5 @@
1
1
  module Delayed
2
- class InvalidCallback < Exception; end
2
+ class InvalidCallback < RuntimeError; end
3
3
 
4
4
  class Lifecycle
5
5
  EVENTS = {
@@ -10,7 +10,7 @@ module Delayed
10
10
  :error => [:worker, :job],
11
11
  :failure => [:worker, :job],
12
12
  :invoke_job => [:job]
13
- }
13
+ }.freeze
14
14
 
15
15
  def initialize
16
16
  @callbacks = EVENTS.keys.each_with_object({}) do |e, hash|
@@ -1,5 +1,3 @@
1
- require 'active_support/core_ext/module/aliasing'
2
-
3
1
  module Delayed
4
2
  class DelayProxy < Delayed::Compatibility.proxy_object_class
5
3
  def initialize(payload_class, target, options)
@@ -8,9 +6,11 @@ module Delayed
8
6
  @options = options
9
7
  end
10
8
 
9
+ # rubocop:disable MethodMissing
11
10
  def method_missing(method, *args)
12
11
  Job.enqueue({:payload_object => @payload_class.new(@target, method.to_sym, args)}.merge(@options))
13
12
  end
13
+ # rubocop:enable MethodMissing
14
14
  end
15
15
 
16
16
  module MessageSending
@@ -28,24 +28,36 @@ module Delayed
28
28
  warn '[DEPRECATION] `object.send_at(time, :method)` is deprecated. Use `object.delay(:run_at => time).method'
29
29
  __delay__(:run_at => time).__send__(method, *args)
30
30
  end
31
+ end
31
32
 
32
- module ClassMethods
33
- def handle_asynchronously(method, opts = {})
34
- aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1 # rubocop:disable PerlBackrefs
35
- with_method, without_method = "#{aliased_method}_with_delay#{punctuation}", "#{aliased_method}_without_delay#{punctuation}"
36
- define_method(with_method) do |*args|
37
- curr_opts = opts.clone
38
- curr_opts.each_key do |key|
39
- next unless (val = curr_opts[key]).is_a?(Proc)
40
- curr_opts[key] = if val.arity == 1
41
- val.call(self)
42
- else
43
- val.call
44
- end
33
+ module MessageSendingClassMethods
34
+ def handle_asynchronously(method, opts = {}) # rubocop:disable PerceivedComplexity
35
+ aliased_method = method.to_s.sub(/([?!=])$/, '')
36
+ punctuation = $1 # rubocop:disable PerlBackrefs
37
+ with_method = "#{aliased_method}_with_delay#{punctuation}"
38
+ without_method = "#{aliased_method}_without_delay#{punctuation}"
39
+ define_method(with_method) do |*args|
40
+ curr_opts = opts.clone
41
+ curr_opts.each_key do |key|
42
+ next unless (val = curr_opts[key]).is_a?(Proc)
43
+ curr_opts[key] = if val.arity == 1
44
+ val.call(self)
45
+ else
46
+ val.call
45
47
  end
46
- delay(curr_opts).__send__(without_method, *args)
47
48
  end
48
- alias_method_chain method, :delay
49
+ delay(curr_opts).__send__(without_method, *args)
50
+ end
51
+
52
+ alias_method without_method, method
53
+ alias_method method, with_method
54
+
55
+ if public_method_defined?(without_method)
56
+ public method
57
+ elsif protected_method_defined?(without_method)
58
+ protected method
59
+ elsif private_method_defined?(without_method)
60
+ private method
49
61
  end
50
62
  end
51
63
  end
@@ -1,11 +1,7 @@
1
- require 'active_support/core_ext/module/delegation'
2
-
3
1
  module Delayed
4
2
  class PerformableMethod
5
3
  attr_accessor :object, :method_name, :args
6
4
 
7
- delegate :method, :to => :object
8
-
9
5
  def initialize(object, method_name, args)
10
6
  raise NoMethodError, "undefined method `#{method_name}' for #{object.inspect}" unless object.respond_to?(method_name, true)
11
7
 
@@ -30,9 +26,15 @@ module Delayed
30
26
  object.send(method_name, *args) if object
31
27
  end
32
28
 
29
+ def method(sym)
30
+ object.method(sym)
31
+ end
32
+
33
+ # rubocop:disable MethodMissing
33
34
  def method_missing(symbol, *args)
34
35
  object.send(symbol, *args)
35
36
  end
37
+ # rubocop:enable MethodMissing
36
38
 
37
39
  def respond_to?(symbol, include_private = false)
38
40
  super || object.respond_to?(symbol, include_private)
@@ -28,23 +28,29 @@ module Delayed
28
28
  end
29
29
 
30
30
  def visit_Psych_Nodes_Mapping(object) # rubocop:disable CyclomaticComplexity, MethodName, PerceivedComplexity
31
- return revive(Psych.load_tags[object.tag], object) if Psych.load_tags[object.tag]
31
+ klass = Psych.load_tags[object.tag]
32
+ if klass
33
+ # Implementation changed here https://github.com/ruby/psych/commit/2c644e184192975b261a81f486a04defa3172b3f
34
+ # load_tags used to have class values, now the values are strings
35
+ klass = resolve_class(klass) if klass.is_a?(String)
36
+ return revive(klass, object)
37
+ end
32
38
 
33
39
  case object.tag
34
- when /^!ruby\/object/
40
+ when %r{^!ruby/object}
35
41
  result = super
36
- if defined?(ActiveRecord::Base) && result.is_a?(ActiveRecord::Base)
42
+ if jruby_is_seriously_borked && result.is_a?(ActiveRecord::Base)
37
43
  klass = result.class
38
44
  id = result[klass.primary_key]
39
45
  begin
40
- klass.find(id)
46
+ klass.unscoped.find(id)
41
47
  rescue ActiveRecord::RecordNotFound => error # rubocop:disable BlockNesting
42
48
  raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass}, primary key: #{id} (#{error.message})"
43
49
  end
44
50
  else
45
51
  result
46
52
  end
47
- when /^!ruby\/ActiveRecord:(.+)$/
53
+ when %r{^!ruby/ActiveRecord:(.+)$}
48
54
  klass = resolve_class(Regexp.last_match[1])
49
55
  payload = Hash[*object.children.map { |c| accept c }]
50
56
  id = payload['attributes'][klass.primary_key]
@@ -54,7 +60,7 @@ module Delayed
54
60
  rescue ActiveRecord::RecordNotFound => error
55
61
  raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass}, primary key: #{id} (#{error.message})"
56
62
  end
57
- when /^!ruby\/Mongoid:(.+)$/
63
+ when %r{^!ruby/Mongoid:(.+)$}
58
64
  klass = resolve_class(Regexp.last_match[1])
59
65
  payload = Hash[*object.children.map { |c| accept c }]
60
66
  id = payload['attributes']['_id']
@@ -63,7 +69,7 @@ module Delayed
63
69
  rescue Mongoid::Errors::DocumentNotFound => error
64
70
  raise Delayed::DeserializationError, "Mongoid::Errors::DocumentNotFound, class: #{klass}, primary key: #{id} (#{error.message})"
65
71
  end
66
- when /^!ruby\/DataMapper:(.+)$/
72
+ when %r{^!ruby/DataMapper:(.+)$}
67
73
  klass = resolve_class(Regexp.last_match[1])
68
74
  payload = Hash[*object.children.map { |c| accept c }]
69
75
  begin
@@ -78,6 +84,13 @@ module Delayed
78
84
  end
79
85
  end
80
86
 
87
+ # defined? is triggering something really messed up in
88
+ # jruby causing both the if AND else clauses to execute,
89
+ # however if the check is run here, everything is fine
90
+ def jruby_is_seriously_borked
91
+ defined?(ActiveRecord::Base)
92
+ end
93
+
81
94
  def resolve_class(klass_name)
82
95
  return nil if !klass_name || klass_name.empty?
83
96
  klass_name.constantize
@@ -4,10 +4,6 @@ require 'rails'
4
4
  module Delayed
5
5
  class Railtie < Rails::Railtie
6
6
  initializer :after_initialize do
7
- ActiveSupport.on_load(:action_mailer) do
8
- ActionMailer::Base.extend(Delayed::DelayMail)
9
- end
10
-
11
7
  Delayed::Worker.logger ||= if defined?(Rails)
12
8
  Rails.logger
13
9
  elsif defined?(RAILS_DEFAULT_LOGGER)
@@ -38,17 +38,17 @@ Capistrano::Configuration.instance.load do
38
38
 
39
39
  desc 'Stop the delayed_job process'
40
40
  task :stop, :roles => lambda { roles } do
41
- run "cd #{current_path};#{rails_env} #{delayed_job_command} stop #{args}"
41
+ run "cd #{current_path} && #{rails_env} #{delayed_job_command} stop #{args}"
42
42
  end
43
43
 
44
44
  desc 'Start the delayed_job process'
45
45
  task :start, :roles => lambda { roles } do
46
- run "cd #{current_path};#{rails_env} #{delayed_job_command} start #{args}"
46
+ run "cd #{current_path} && #{rails_env} #{delayed_job_command} start #{args}"
47
47
  end
48
48
 
49
49
  desc 'Restart the delayed_job process'
50
50
  task :restart, :roles => lambda { roles } do
51
- run "cd #{current_path};#{rails_env} #{delayed_job_command} restart #{args}"
51
+ run "cd #{current_path} && #{rails_env} #{delayed_job_command} restart #{args}"
52
52
  end
53
53
  end
54
54
  end
@@ -1,7 +1,7 @@
1
1
  if defined?(ActiveRecord)
2
2
  module ActiveRecord
3
3
  class Base
4
- yaml_as 'tag:ruby.yaml.org,2002:ActiveRecord'
4
+ yaml_tag 'tag:ruby.yaml.org,2002:ActiveRecord'
5
5
 
6
6
  def self.yaml_new(klass, _tag, val)
7
7
  klass.unscoped.find(val['attributes'][klass.primary_key])
@@ -1,5 +1,5 @@
1
1
  class Module
2
- yaml_as 'tag:ruby.yaml.org,2002:module'
2
+ yaml_tag 'tag:ruby.yaml.org,2002:module'
3
3
 
4
4
  def self.yaml_new(_klass, _tag, val)
5
5
  val.constantize
@@ -20,7 +20,7 @@ class Module
20
20
  end
21
21
 
22
22
  class Class
23
- yaml_as 'tag:ruby.yaml.org,2002:class'
23
+ yaml_tag 'tag:ruby.yaml.org,2002:class'
24
24
  remove_method :to_yaml if respond_to?(:to_yaml) && method(:to_yaml).owner == Class # use Module's to_yaml
25
25
  end
26
26
 
@@ -29,7 +29,7 @@ class Struct
29
29
  # Constantize the object so that ActiveSupport can attempt
30
30
  # its auto loading magic. Will raise LoadError if not successful.
31
31
  name.constantize
32
- "Struct::#{ name }"
32
+ "Struct::#{name}"
33
33
  end
34
34
  end
35
35
 
@@ -19,7 +19,7 @@ namespace :jobs do
19
19
  :min_priority => ENV['MIN_PRIORITY'],
20
20
  :max_priority => ENV['MAX_PRIORITY'],
21
21
  :queues => (ENV['QUEUES'] || ENV['QUEUE'] || '').split(','),
22
- :quiet => false
22
+ :quiet => ENV['QUIET']
23
23
  }
24
24
 
25
25
  @worker_options[:sleep_delay] = ENV['SLEEP_DELAY'].to_i if ENV['SLEEP_DELAY']
@@ -1,20 +1,22 @@
1
1
  require 'timeout'
2
+ require 'active_support/dependencies'
2
3
  require 'active_support/core_ext/numeric/time'
3
4
  require 'active_support/core_ext/class/attribute_accessors'
4
- require 'active_support/core_ext/kernel'
5
- require 'active_support/core_ext/enumerable'
5
+ require 'active_support/hash_with_indifferent_access'
6
+ require 'active_support/core_ext/hash/indifferent_access'
6
7
  require 'logger'
7
8
  require 'benchmark'
8
9
 
9
10
  module Delayed
10
11
  class Worker # rubocop:disable ClassLength
11
- DEFAULT_LOG_LEVEL = 'info'
12
+ DEFAULT_LOG_LEVEL = 'info'.freeze
12
13
  DEFAULT_SLEEP_DELAY = 5
13
14
  DEFAULT_MAX_ATTEMPTS = 25
14
15
  DEFAULT_MAX_RUN_TIME = 4.hours
15
16
  DEFAULT_DEFAULT_PRIORITY = 0
16
17
  DEFAULT_DELAY_JOBS = true
17
- DEFAULT_QUEUES = []
18
+ DEFAULT_QUEUES = [].freeze
19
+ DEFAULT_QUEUE_ATTRIBUTES = HashWithIndifferentAccess.new.freeze
18
20
  DEFAULT_READ_AHEAD = 5
19
21
 
20
22
  cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time,
@@ -25,7 +27,7 @@ module Delayed
25
27
  # Named queue into which jobs are enqueued by default
26
28
  cattr_accessor :default_queue_name
27
29
 
28
- cattr_reader :backend
30
+ cattr_reader :backend, :queue_attributes
29
31
 
30
32
  # name_prefix is ignored if name is set directly
31
33
  attr_accessor :name_prefix
@@ -38,11 +40,11 @@ module Delayed
38
40
  self.default_priority = DEFAULT_DEFAULT_PRIORITY
39
41
  self.delay_jobs = DEFAULT_DELAY_JOBS
40
42
  self.queues = DEFAULT_QUEUES
43
+ self.queue_attributes = DEFAULT_QUEUE_ATTRIBUTES
41
44
  self.read_ahead = DEFAULT_READ_AHEAD
45
+ @lifecycle = nil
42
46
  end
43
47
 
44
- reset
45
-
46
48
  # Add or remove plugins in this list before the worker is instantiated
47
49
  self.plugins = [Delayed::Plugins::ClearLocks]
48
50
 
@@ -69,6 +71,11 @@ module Delayed
69
71
  silence_warnings { ::Delayed.const_set(:Job, backend) }
70
72
  end
71
73
 
74
+ # rubocop:disable ClassVars
75
+ def self.queue_attributes=(val)
76
+ @@queue_attributes = val.with_indifferent_access
77
+ end
78
+
72
79
  def self.guess_backend
73
80
  warn '[DEPRECATION] guess_backend is deprecated. Please remove it from your code.'
74
81
  end
@@ -97,13 +104,29 @@ module Delayed
97
104
  end
98
105
 
99
106
  def self.lifecycle
100
- @lifecycle ||= Delayed::Lifecycle.new
107
+ # In case a worker has not been set up, job enqueueing needs a lifecycle.
108
+ setup_lifecycle unless @lifecycle
109
+
110
+ @lifecycle
111
+ end
112
+
113
+ def self.setup_lifecycle
114
+ @lifecycle = Delayed::Lifecycle.new
115
+ plugins.each { |klass| klass.new }
101
116
  end
102
117
 
103
118
  def self.reload_app?
104
119
  defined?(ActionDispatch::Reloader) && Rails.application.config.cache_classes == false
105
120
  end
106
121
 
122
+ def self.delay_job?(job)
123
+ if delay_jobs.is_a?(Proc)
124
+ delay_jobs.arity == 1 ? delay_jobs.call(job) : delay_jobs.call
125
+ else
126
+ delay_jobs
127
+ end
128
+ end
129
+
107
130
  def initialize(options = {})
108
131
  @quiet = options.key?(:quiet) ? options[:quiet] : true
109
132
  @failed_reserve_count = 0
@@ -112,7 +135,9 @@ module Delayed
112
135
  self.class.send("#{option}=", options[option]) if options.key?(option)
113
136
  end
114
137
 
115
- plugins.each { |klass| klass.new }
138
+ # Reset lifecycle on the offhand chance that something lazily
139
+ # triggered its creation before all plugins had been registered.
140
+ self.class.setup_lifecycle
116
141
  end
117
142
 
118
143
  # Every worker has a unique name which by default is the pid of the process. There are some
@@ -121,7 +146,7 @@ module Delayed
121
146
  # it crashed before.
122
147
  def name
123
148
  return @name unless @name.nil?
124
- "#{@name_prefix}host:#{Socket.gethostname} pid:#{Process.pid}" rescue "#{@name_prefix}pid:#{Process.pid}" # rubocop:disable RescueModifier
149
+ "#{@name_prefix}host:#{Socket.gethostname} pid:#{Process.pid}" rescue "#{@name_prefix}pid:#{Process.pid}"
125
150
  end
126
151
 
127
152
  # Sets the name of the worker.
@@ -151,7 +176,7 @@ module Delayed
151
176
  end
152
177
  end
153
178
 
154
- count = @result.sum
179
+ count = @result[0] + @result[1]
155
180
 
156
181
  if count.zero?
157
182
  if self.class.exit_on_complete
@@ -181,7 +206,8 @@ module Delayed
181
206
  # Do num jobs and return stats on success/failure.
182
207
  # Exit early if interrupted.
183
208
  def work_off(num = 100)
184
- success, failure = 0, 0
209
+ success = 0
210
+ failure = 0
185
211
 
186
212
  num.times do
187
213
  case reserve_and_run_one_job
@@ -190,7 +216,7 @@ module Delayed
190
216
  when false
191
217
  failure += 1
192
218
  else
193
- break # leave if no work could be done
219
+ break # leave if no work could be done
194
220
  end
195
221
  break if stop? # leave if we're exiting
196
222
  end
@@ -200,18 +226,20 @@ module Delayed
200
226
 
201
227
  def run(job)
202
228
  job_say job, 'RUNNING'
203
- runtime = Benchmark.realtime do
229
+ runtime = Benchmark.realtime do
204
230
  Timeout.timeout(max_run_time(job).to_i, WorkerTimeout) { job.invoke_job }
205
231
  job.destroy
206
232
  end
207
233
  job_say job, format('COMPLETED after %.4f', runtime)
208
- return true # did work
234
+ return true # did work
209
235
  rescue DeserializationError => error
210
- job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
236
+ job_say job, "FAILED permanently with #{error.class.name}: #{error.message}", 'error'
237
+
238
+ job.error = error
211
239
  failed(job)
212
- rescue => error
240
+ rescue Exception => error # rubocop:disable RescueException
213
241
  self.class.lifecycle.run_callbacks(:error, self, job) { handle_failed_job(job, error) }
214
- return false # work failed
242
+ return false # work failed
215
243
  end
216
244
 
217
245
  # Reschedule the job in the future (when a job fails).
@@ -223,7 +251,7 @@ module Delayed
223
251
  job.unlock
224
252
  job.save!
225
253
  else
226
- job_say job, "REMOVED permanently because of #{job.attempts} consecutive failures", 'error'
254
+ job_say job, "FAILED permanently because of #{job.attempts} consecutive failures", 'error'
227
255
  failed(job)
228
256
  end
229
257
  end
@@ -236,13 +264,13 @@ module Delayed
236
264
  say "Error when running failure callback: #{error}", 'error'
237
265
  say error.backtrace.join("\n"), 'error'
238
266
  ensure
239
- self.class.destroy_failed_jobs ? job.destroy : job.fail!
267
+ job.destroy_failed_jobs? ? job.destroy : job.fail!
240
268
  end
241
269
  end
242
270
  end
243
271
 
244
272
  def job_say(job, text, level = default_log_level)
245
- text = "Job #{job.name} (id=#{job.id}) #{text}"
273
+ text = "Job #{job.name} (id=#{job.id})#{say_queue(job.queue)} #{text}"
246
274
  say text, level
247
275
  end
248
276
 
@@ -267,8 +295,12 @@ module Delayed
267
295
 
268
296
  protected
269
297
 
298
+ def say_queue(queue)
299
+ " (queue=#{queue})" if queue
300
+ end
301
+
270
302
  def handle_failed_job(job, error)
271
- job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
303
+ job.error = error
272
304
  job_say job, "FAILED (#{job.attempts} prior attempts) with #{error.class.name}: #{error.message}", 'error'
273
305
  reschedule(job)
274
306
  end
@@ -294,8 +326,14 @@ module Delayed
294
326
 
295
327
  def reload!
296
328
  return unless self.class.reload_app?
297
- ActionDispatch::Reloader.cleanup!
298
- ActionDispatch::Reloader.prepare!
329
+ if defined?(ActiveSupport::Reloader)
330
+ Rails.application.reloader.reload!
331
+ else
332
+ ActionDispatch::Reloader.cleanup!
333
+ ActionDispatch::Reloader.prepare!
334
+ end
299
335
  end
300
336
  end
301
337
  end
338
+
339
+ Delayed::Worker.reset