delayed_job 4.0.6 → 4.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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