delayed_job 4.0.2 → 4.0.3

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.
@@ -25,7 +25,7 @@ Capistrano::Configuration.instance.load do
25
25
  end
26
26
 
27
27
  def args
28
- fetch(:delayed_job_args, "")
28
+ fetch(:delayed_job_args, '')
29
29
  end
30
30
 
31
31
  def roles
@@ -33,20 +33,20 @@ Capistrano::Configuration.instance.load do
33
33
  end
34
34
 
35
35
  def delayed_job_command
36
- fetch(:delayed_job_command, "script/delayed_job")
36
+ fetch(:delayed_job_command, 'script/delayed_job')
37
37
  end
38
38
 
39
- desc "Stop the delayed_job process"
39
+ desc 'Stop the delayed_job process'
40
40
  task :stop, :roles => lambda { roles } do
41
41
  run "cd #{current_path};#{rails_env} #{delayed_job_command} stop"
42
42
  end
43
43
 
44
- desc "Start the delayed_job process"
44
+ desc 'Start the delayed_job process'
45
45
  task :start, :roles => lambda { roles } do
46
46
  run "cd #{current_path};#{rails_env} #{delayed_job_command} start #{args}"
47
47
  end
48
48
 
49
- desc "Restart the delayed_job process"
49
+ desc 'Restart the delayed_job process'
50
50
  task :restart, :roles => lambda { roles } do
51
51
  run "cd #{current_path};#{rails_env} #{delayed_job_command} restart #{args}"
52
52
  end
@@ -1,15 +1,17 @@
1
1
  if defined?(ActiveRecord)
2
- class ActiveRecord::Base
3
- yaml_as "tag:ruby.yaml.org,2002:ActiveRecord"
2
+ module ActiveRecord
3
+ class Base
4
+ yaml_as 'tag:ruby.yaml.org,2002:ActiveRecord'
4
5
 
5
- def self.yaml_new(klass, tag, val)
6
- klass.unscoped.find(val['attributes'][klass.primary_key])
7
- rescue ActiveRecord::RecordNotFound
8
- raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass} , primary key: #{val['attributes'][klass.primary_key]} "
9
- end
6
+ def self.yaml_new(klass, _tag, val)
7
+ klass.unscoped.find(val['attributes'][klass.primary_key])
8
+ rescue ActiveRecord::RecordNotFound
9
+ raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass} , primary key: #{val['attributes'][klass.primary_key]}"
10
+ end
10
11
 
11
- def to_yaml_properties
12
- ['@attributes']
12
+ def to_yaml_properties
13
+ ['@attributes']
14
+ end
13
15
  end
14
16
  end
15
17
  end
@@ -1,7 +1,7 @@
1
1
  class Module
2
- yaml_as "tag:ruby.yaml.org,2002:module"
2
+ yaml_as 'tag:ruby.yaml.org,2002:module'
3
3
 
4
- def self.yaml_new(klass, tag, val)
4
+ def self.yaml_new(_klass, _tag, val)
5
5
  val.constantize
6
6
  end
7
7
 
@@ -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_as '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
 
@@ -1,17 +1,17 @@
1
1
  namespace :jobs do
2
- desc "Clear the delayed_job queue."
2
+ desc 'Clear the delayed_job queue.'
3
3
  task :clear => :environment do
4
4
  Delayed::Job.delete_all
5
5
  end
6
6
 
7
- desc "Start a delayed_job worker."
7
+ desc 'Start a delayed_job worker.'
8
8
  task :work => :environment_options do
9
9
  Delayed::Worker.new(@worker_options).start
10
10
  end
11
11
 
12
- desc "Start a delayed_job worker and exit when all available jobs are complete."
12
+ desc 'Start a delayed_job worker and exit when all available jobs are complete.'
13
13
  task :workoff => :environment_options do
14
- Delayed::Worker.new(@worker_options.merge({:exit_on_complete => true})).start
14
+ Delayed::Worker.new(@worker_options.merge(:exit_on_complete => true)).start
15
15
  end
16
16
 
17
17
  task :environment_options => :environment do
@@ -30,7 +30,7 @@ namespace :jobs do
30
30
  unprocessed_jobs = Delayed::Job.where('attempts = 0 AND created_at < ?', Time.now - args[:max_age].to_i).count
31
31
 
32
32
  if unprocessed_jobs > 0
33
- fail "#{unprocessed_jobs} jobs older than #{args[:max_age]} seconds have not been processed yet"
33
+ raise "#{unprocessed_jobs} jobs older than #{args[:max_age]} seconds have not been processed yet"
34
34
  end
35
35
 
36
36
  end
@@ -7,8 +7,7 @@ require 'logger'
7
7
  require 'benchmark'
8
8
 
9
9
  module Delayed
10
-
11
- class Worker
10
+ class Worker # rubocop:disable ClassLength
12
11
  DEFAULT_LOG_LEVEL = 'info'
13
12
  DEFAULT_SLEEP_DELAY = 5
14
13
  DEFAULT_MAX_ATTEMPTS = 25
@@ -19,8 +18,8 @@ module Delayed
19
18
  DEFAULT_READ_AHEAD = 5
20
19
 
21
20
  cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time,
22
- :default_priority, :sleep_delay, :logger, :delay_jobs, :queues,
23
- :read_ahead, :plugins, :destroy_failed_jobs, :exit_on_complete
21
+ :default_priority, :sleep_delay, :logger, :delay_jobs, :queues,
22
+ :read_ahead, :plugins, :destroy_failed_jobs, :exit_on_complete
24
23
 
25
24
  # Named queue into which jobs are enqueued by default
26
25
  cattr_accessor :default_queue_name
@@ -70,12 +69,12 @@ module Delayed
70
69
  require "delayed/backend/#{backend}"
71
70
  backend = "Delayed::Backend::#{backend.to_s.classify}::Job".constantize
72
71
  end
73
- @@backend = backend
72
+ @@backend = backend # rubocop:disable ClassVars
74
73
  silence_warnings { ::Delayed.const_set(:Job, backend) }
75
74
  end
76
75
 
77
76
  def self.guess_backend
78
- warn "[DEPRECATION] guess_backend is deprecated. Please remove it from your code."
77
+ warn '[DEPRECATION] guess_backend is deprecated. Please remove it from your code.'
79
78
  end
80
79
 
81
80
  def self.before_fork
@@ -93,12 +92,11 @@ module Delayed
93
92
  # Re-open file handles
94
93
  @files_to_reopen.each do |file|
95
94
  begin
96
- file.reopen file.path, "a+"
95
+ file.reopen file.path, 'a+'
97
96
  file.sync = true
98
- rescue ::Exception
97
+ rescue ::Exception # rubocop:disable HandleExceptions, RescueException
99
98
  end
100
99
  end
101
-
102
100
  backend.after_fork
103
101
  end
104
102
 
@@ -106,15 +104,15 @@ module Delayed
106
104
  @lifecycle ||= Delayed::Lifecycle.new
107
105
  end
108
106
 
109
- def initialize(options={})
110
- @quiet = options.has_key?(:quiet) ? options[:quiet] : true
107
+ def initialize(options = {})
108
+ @quiet = options.key?(:quiet) ? options[:quiet] : true
111
109
  @failed_reserve_count = 0
112
110
 
113
111
  [:min_priority, :max_priority, :sleep_delay, :read_ahead, :queues, :exit_on_complete].each do |option|
114
- self.class.send("#{option}=", options[option]) if options.has_key?(option)
112
+ self.class.send("#{option}=", options[option]) if options.key?(option)
115
113
  end
116
114
 
117
- self.plugins.each { |klass| klass.new }
115
+ plugins.each { |klass| klass.new }
118
116
  end
119
117
 
120
118
  # Every worker has a unique name which by default is the pid of the process. There are some
@@ -123,29 +121,27 @@ module Delayed
123
121
  # it crashed before.
124
122
  def name
125
123
  return @name unless @name.nil?
126
- "#{@name_prefix}host:#{Socket.gethostname} pid:#{Process.pid}" rescue "#{@name_prefix}pid:#{Process.pid}"
124
+ "#{@name_prefix}host:#{Socket.gethostname} pid:#{Process.pid}" rescue "#{@name_prefix}pid:#{Process.pid}" # rubocop:disable RescueModifier
127
125
  end
128
126
 
129
127
  # Sets the name of the worker.
130
128
  # Setting the name to nil will reset the default worker name
131
- def name=(val)
132
- @name = val
133
- end
129
+ attr_writer :name
134
130
 
135
- def start
131
+ def start # rubocop:disable CyclomaticComplexity, PerceivedComplexity
136
132
  trap('TERM') do
137
133
  say 'Exiting...'
138
134
  stop
139
- raise SignalException.new('TERM') if self.class.raise_signal_exceptions
135
+ raise SignalException, 'TERM' if self.class.raise_signal_exceptions
140
136
  end
141
137
 
142
138
  trap('INT') do
143
139
  say 'Exiting...'
144
140
  stop
145
- raise SignalException.new('INT') if self.class.raise_signal_exceptions && self.class.raise_signal_exceptions != :term
141
+ raise SignalException, 'INT' if self.class.raise_signal_exceptions && self.class.raise_signal_exceptions != :term
146
142
  end
147
143
 
148
- say "Starting job worker"
144
+ say 'Starting job worker'
149
145
 
150
146
  self.class.lifecycle.run_callbacks(:execute, self) do
151
147
  loop do
@@ -159,13 +155,13 @@ module Delayed
159
155
 
160
156
  if count.zero?
161
157
  if self.class.exit_on_complete
162
- say "No more jobs available. Exiting"
158
+ say 'No more jobs available. Exiting'
163
159
  break
164
- else
165
- sleep(self.class.sleep_delay) unless stop?
160
+ elsif !stop?
161
+ sleep(self.class.sleep_delay)
166
162
  end
167
163
  else
168
- say "#{count} jobs processed at %.4f j/s, %d failed" % [count / @realtime, @result.last]
164
+ say format("#{count} jobs processed at %.4f j/s, %d failed", count / @realtime, @result.last)
169
165
  end
170
166
 
171
167
  break if stop?
@@ -189,16 +185,16 @@ module Delayed
189
185
  num.times do
190
186
  case reserve_and_run_one_job
191
187
  when true
192
- success += 1
188
+ success += 1
193
189
  when false
194
- failure += 1
190
+ failure += 1
195
191
  else
196
192
  break # leave if no work could be done
197
193
  end
198
194
  break if stop? # leave if we're exiting
199
195
  end
200
196
 
201
- return [success, failure]
197
+ [success, failure]
202
198
  end
203
199
 
204
200
  def run(job)
@@ -207,13 +203,13 @@ module Delayed
207
203
  Timeout.timeout(self.class.max_run_time.to_i, WorkerTimeout) { job.invoke_job }
208
204
  job.destroy
209
205
  end
210
- job_say job, 'COMPLETED after %.4f' % runtime
206
+ job_say job, format('COMPLETED after %.4f', runtime)
211
207
  return true # did work
212
208
  rescue DeserializationError => error
213
209
  job.last_error = "#{error.message}\n#{error.backtrace.join("\n")}"
214
210
  failed(job)
215
- rescue Exception => error
216
- self.class.lifecycle.run_callbacks(:error, self, job){ handle_failed_job(job, error) }
211
+ rescue => error
212
+ self.class.lifecycle.run_callbacks(:error, self, job) { handle_failed_job(job, error) }
217
213
  return false # work failed
218
214
  end
219
215
 
@@ -233,8 +229,14 @@ module Delayed
233
229
 
234
230
  def failed(job)
235
231
  self.class.lifecycle.run_callbacks(:failure, self, job) do
236
- job.hook(:failure)
237
- self.class.destroy_failed_jobs ? job.destroy : job.fail!
232
+ begin
233
+ job.hook(:failure)
234
+ rescue => error
235
+ say "Error when running failure callback: #{error}", 'error'
236
+ say error.backtrace.join("\n"), 'error'
237
+ ensure
238
+ self.class.destroy_failed_jobs ? job.destroy : job.fail!
239
+ end
238
240
  end
239
241
  end
240
242
 
@@ -246,13 +248,12 @@ module Delayed
246
248
  def say(text, level = DEFAULT_LOG_LEVEL)
247
249
  text = "[Worker(#{name})] #{text}"
248
250
  puts text unless @quiet
249
- if logger
250
- # TODO: Deprecate use of Fixnum log levels
251
- if !level.is_a?(String)
252
- level = Logger::Severity.constants.detect {|i| Logger::Severity.const_get(i) == level }.to_s.downcase
253
- end
254
- logger.send(level, "#{Time.now.strftime('%FT%T%z')}: #{text}")
251
+ return unless logger
252
+ # TODO: Deprecate use of Fixnum log levels
253
+ unless level.is_a?(String)
254
+ level = Logger::Severity.constants.detect { |i| Logger::Severity.const_get(i) == level }.to_s.downcase
255
255
  end
256
+ logger.send(level, "#{Time.now.strftime('%FT%T%z')}: #{text}")
256
257
  end
257
258
 
258
259
  def max_attempts(job)
@@ -271,14 +272,14 @@ module Delayed
271
272
  # If no jobs are left we return nil
272
273
  def reserve_and_run_one_job
273
274
  job = reserve_job
274
- self.class.lifecycle.run_callbacks(:perform, self, job){ run(job) } if job
275
+ self.class.lifecycle.run_callbacks(:perform, self, job) { run(job) } if job
275
276
  end
276
277
 
277
278
  def reserve_job
278
279
  job = Delayed::Job.reserve(self)
279
280
  @failed_reserve_count = 0
280
281
  job
281
- rescue Exception => error
282
+ rescue ::Exception => error # rubocop:disable RescueException
282
283
  say "Error while reserving job: #{error}"
283
284
  Delayed::Job.recover_from(error)
284
285
  @failed_reserve_count += 1
@@ -286,5 +287,4 @@ module Delayed
286
287
  nil
287
288
  end
288
289
  end
289
-
290
290
  end
@@ -2,11 +2,10 @@ require 'rails/generators'
2
2
  require 'delayed/compatibility'
3
3
 
4
4
  class DelayedJobGenerator < Rails::Generators::Base
5
-
6
- self.source_paths << File.join(File.dirname(__FILE__), 'templates')
5
+ source_paths << File.join(File.dirname(__FILE__), 'templates')
7
6
 
8
7
  def create_executable_file
9
- template "script", "#{Delayed::Compatibility.executable_prefix}/delayed_job"
8
+ template 'script', "#{Delayed::Compatibility.executable_prefix}/delayed_job"
10
9
  chmod "#{Delayed::Compatibility.executable_prefix}/delayed_job", 0755
11
10
  end
12
11
  end
@@ -25,12 +25,11 @@ module Delayed
25
25
  self.attempts = 0
26
26
  self.priority = 0
27
27
  self.id = (self.class.id += 1)
28
- hash.each{|k,v| send(:"#{k}=", v)}
28
+ hash.each { |k, v| send(:"#{k}=", v) }
29
29
  end
30
30
 
31
- @jobs = []
32
31
  def self.all
33
- @jobs
32
+ @jobs ||= []
34
33
  end
35
34
 
36
35
  def self.count
@@ -47,29 +46,33 @@ module Delayed
47
46
  end
48
47
  end
49
48
 
50
- def self.create!(*args); create(*args); end
49
+ def self.create!(*args)
50
+ create(*args)
51
+ end
51
52
 
52
53
  def self.clear_locks!(worker_name)
53
- all.select{|j| j.locked_by == worker_name}.each {|j| j.locked_by = nil; j.locked_at = nil}
54
+ all.select { |j| j.locked_by == worker_name }.each do |j|
55
+ j.locked_by = nil
56
+ j.locked_at = nil
57
+ end
54
58
  end
55
59
 
56
60
  # Find a few candidate jobs to run (in case some immediately get locked by others).
57
- def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)
61
+ def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time) # rubocop:disable CyclomaticComplexity, PerceivedComplexity
58
62
  jobs = all.select do |j|
59
63
  j.run_at <= db_time_now &&
60
- (j.locked_at.nil? || j.locked_at < db_time_now - max_run_time || j.locked_by == worker_name) &&
61
- !j.failed?
64
+ (j.locked_at.nil? || j.locked_at < db_time_now - max_run_time || j.locked_by == worker_name) &&
65
+ !j.failed?
62
66
  end
63
-
64
- jobs = jobs.select{|j| Worker.queues.include?(j.queue)} if Worker.queues.any?
65
- jobs = jobs.select{|j| j.priority >= Worker.min_priority} if Worker.min_priority
66
- jobs = jobs.select{|j| j.priority <= Worker.max_priority} if Worker.max_priority
67
- jobs.sort_by{|j| [j.priority, j.run_at]}[0..limit-1]
67
+ jobs.select! { |j| j.priority <= Worker.max_priority } if Worker.max_priority
68
+ jobs.select! { |j| j.priority >= Worker.min_priority } if Worker.min_priority
69
+ jobs.select! { |j| Worker.queues.include?(j.queue) } if Worker.queues.any?
70
+ jobs.sort_by! { |j| [j.priority, j.run_at] }[0..limit - 1]
68
71
  end
69
72
 
70
73
  # Lock this job for this worker.
71
74
  # Returns true if we have the lock, false otherwise.
72
- def lock_exclusively!(max_run_time, worker)
75
+ def lock_exclusively!(_max_run_time, worker)
73
76
  now = self.class.db_time_now
74
77
  if locked_by != worker
75
78
  # We don't own this job so we will update the locked_by name and the locked_at
@@ -77,7 +80,7 @@ module Delayed
77
80
  self.locked_by = worker
78
81
  end
79
82
 
80
- return true
83
+ true
81
84
  end
82
85
 
83
86
  def self.db_time_now
@@ -85,7 +88,7 @@ module Delayed
85
88
  end
86
89
 
87
90
  def update_attributes(attrs = {})
88
- attrs.each{|k,v| send(:"#{k}=", v)}
91
+ attrs.each { |k, v| send(:"#{k}=", v) }
89
92
  save
90
93
  end
91
94
 
@@ -100,7 +103,9 @@ module Delayed
100
103
  true
101
104
  end
102
105
 
103
- def save!; save; end
106
+ def save!
107
+ save
108
+ end
104
109
 
105
110
  def reload
106
111
  reset
@@ -0,0 +1,57 @@
1
+ require 'helper'
2
+ require 'delayed/command'
3
+
4
+ describe Delayed::Command do
5
+ describe 'parsing --pool argument' do
6
+ it 'should parse --pool correctly' do
7
+ command = Delayed::Command.new(['--pool=*:1', '--pool=test_queue:4', '--pool=mailers,misc:2'])
8
+
9
+ expect(command.worker_pools).to eq [
10
+ [[], 1],
11
+ [['test_queue'], 4],
12
+ [%w[mailers misc], 2]
13
+ ]
14
+ end
15
+
16
+ it 'should allow * or blank to specify any pools' do
17
+ command = Delayed::Command.new(['--pool=*:4'])
18
+ expect(command.worker_pools).to eq [
19
+ [[], 4],
20
+ ]
21
+
22
+ command = Delayed::Command.new(['--pool=:4'])
23
+ expect(command.worker_pools).to eq [
24
+ [[], 4],
25
+ ]
26
+ end
27
+
28
+ it 'should default to one worker if not specified' do
29
+ command = Delayed::Command.new(['--pool=mailers'])
30
+ expect(command.worker_pools).to eq [
31
+ [['mailers'], 1],
32
+ ]
33
+ end
34
+ end
35
+
36
+ describe 'running worker pools defined by multiple --pool arguments' do
37
+ it 'should run the correct worker processes' do
38
+ command = Delayed::Command.new(['--pool=*:1', '--pool=test_queue:4', '--pool=mailers,misc:2'])
39
+
40
+ expect(Dir).to receive(:mkdir).with('./tmp/pids').once
41
+
42
+ [
43
+ ['delayed_job.0', {:quiet => true, :pid_dir => './tmp/pids', :queues => []}],
44
+ ['delayed_job.1', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
45
+ ['delayed_job.2', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
46
+ ['delayed_job.3', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
47
+ ['delayed_job.4', {:quiet => true, :pid_dir => './tmp/pids', :queues => ['test_queue']}],
48
+ ['delayed_job.5', {:quiet => true, :pid_dir => './tmp/pids', :queues => %w[mailers misc]}],
49
+ ['delayed_job.6', {:quiet => true, :pid_dir => './tmp/pids', :queues => %w[mailers misc]}]
50
+ ].each do |args|
51
+ expect(command).to receive(:run_process).with(*args).once
52
+ end
53
+
54
+ command.daemonize
55
+ end
56
+ end
57
+ end