delayed_job 4.0.3 → 4.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +38 -3
- data/delayed_job.gemspec +1 -1
- data/lib/delayed/backend/base.rb +27 -24
- data/lib/delayed/backend/shared_spec.rb +57 -5
- data/lib/delayed/command.rb +1 -1
- data/lib/delayed/performable_mailer.rb +2 -1
- data/lib/delayed/performable_method.rb +6 -2
- data/lib/delayed/psych_ext.rb +32 -27
- data/lib/delayed/railtie.rb +6 -0
- data/lib/delayed/recipes.rb +1 -1
- data/lib/delayed/syck_ext.rb +8 -0
- data/lib/delayed/tasks.rb +3 -0
- data/lib/delayed/worker.rb +19 -19
- data/lib/generators/delayed_job/delayed_job_generator.rb +1 -1
- data/spec/helper.rb +12 -1
- data/spec/performable_method_spec.rb +10 -0
- data/spec/sample_jobs.rb +6 -0
- data/spec/worker_spec.rb +11 -3
- data/spec/yaml_ext_spec.rb +17 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 458beb66285efe46237b4f094fc7eadbbecf3e79
|
4
|
+
data.tar.gz: 85e801616a1d8ad319b705386adcd602e3045234
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e375922727337f1aa87d578715f5679b1edddc3c15f3505cfb8d084ef063eeeace708ee32c85291bff340996662751640c0627babe0e8c0beb3bb55856e83432
|
7
|
+
data.tar.gz: 9da95592d2f8999d8ddb7365234791a117641bba32edb13361ec177b6a2f4d094a75707be4bdbee0eeacb0f7015fd7e4b072a222d2bb074b95497f0524eee3be
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
4.0.4 - 2014-09-24
|
2
|
+
==================
|
3
|
+
* Fix using options passed into delayed_job command
|
4
|
+
* Add the ability to set a default queue for a custom job
|
5
|
+
* Add the ability to override the max_run_time on a custom job. MUST be lower than worker setting
|
6
|
+
* Psych YAML overrides are now exclusively used only when loading a job payload
|
7
|
+
* SLEEP_DELAY and READ_AHEAD can be set for the rake task
|
8
|
+
* Some updates for Rails 4.2 support
|
9
|
+
|
1
10
|
4.0.3 - 2014-09-04
|
2
11
|
==================
|
3
12
|
* Added --pools option to delayed_job command
|
data/README.md
CHANGED
@@ -242,9 +242,11 @@ NewsletterJob = Struct.new(:text, :emails) do
|
|
242
242
|
end
|
243
243
|
end
|
244
244
|
|
245
|
-
Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.
|
245
|
+
Delayed::Job.enqueue NewsletterJob.new('lorem ipsum...', Customers.pluck(:email))
|
246
246
|
```
|
247
|
+
|
247
248
|
To set a per-job max attempts that overrides the Delayed::Worker.max_attempts you can define a max_attempts method on the job
|
249
|
+
|
248
250
|
```ruby
|
249
251
|
NewsletterJob = Struct.new(:text, :emails) do
|
250
252
|
def perform
|
@@ -252,10 +254,40 @@ NewsletterJob = Struct.new(:text, :emails) do
|
|
252
254
|
end
|
253
255
|
|
254
256
|
def max_attempts
|
255
|
-
|
257
|
+
3
|
256
258
|
end
|
257
259
|
end
|
258
|
-
|
260
|
+
```
|
261
|
+
|
262
|
+
To set a per-job max run time that overrides the Delayed::Worker.max_run_time you can define a max_run_time method on the job
|
263
|
+
|
264
|
+
NOTE: this can ONLY be used to set a max_run_time that is lower than Delayed::Worker.max_run_time. Otherwise the lock on the job would expire and another worker would start the working on the in progress job.
|
265
|
+
|
266
|
+
```ruby
|
267
|
+
NewsletterJob = Struct.new(:text, :emails) do
|
268
|
+
def perform
|
269
|
+
emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
|
270
|
+
end
|
271
|
+
|
272
|
+
def max_run_time
|
273
|
+
120 # seconds
|
274
|
+
end
|
275
|
+
end
|
276
|
+
```
|
277
|
+
|
278
|
+
To set a default queue name for a custom job that overrides Delayed::Worker.default_queue_name, you can define a queue_name method on the job
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
NewsletterJob = Struct.new(:text, :emails) do
|
282
|
+
def perform
|
283
|
+
emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
|
284
|
+
end
|
285
|
+
|
286
|
+
def queue_name
|
287
|
+
'newsletter_queue'
|
288
|
+
end
|
289
|
+
end
|
290
|
+
```
|
259
291
|
|
260
292
|
|
261
293
|
Hooks
|
@@ -332,6 +364,8 @@ By default all jobs will be queued without a named queue. A default named queue
|
|
332
364
|
|
333
365
|
It is possible to disable delayed jobs for testing purposes. Set `Delayed::Worker.delay_jobs = false` to execute all jobs realtime.
|
334
366
|
|
367
|
+
You may need to raise exceptions on SIGTERM signals, `Delayed::Worker.raise_signal_exceptions = :term` will cause the worker to raise a `SignalException` causing the running job to abort and be unlocked, which makes the job available to other workers. The default for this option is false.
|
368
|
+
|
335
369
|
Here is an example of changing job parameters in Rails:
|
336
370
|
|
337
371
|
```ruby
|
@@ -343,6 +377,7 @@ Delayed::Worker.max_run_time = 5.minutes
|
|
343
377
|
Delayed::Worker.read_ahead = 10
|
344
378
|
Delayed::Worker.default_queue_name = 'default'
|
345
379
|
Delayed::Worker.delay_jobs = !Rails.env.test?
|
380
|
+
Delayed::Worker.raise_signal_exceptions = :term
|
346
381
|
```
|
347
382
|
|
348
383
|
Cleaning up
|
data/delayed_job.gemspec
CHANGED
data/lib/delayed/backend/base.rb
CHANGED
@@ -7,13 +7,17 @@ module Delayed
|
|
7
7
|
|
8
8
|
module ClassMethods
|
9
9
|
# Add a job to the queue
|
10
|
-
def enqueue(*args)
|
11
|
-
options =
|
12
|
-
:priority => Delayed::Worker.default_priority,
|
13
|
-
:queue => Delayed::Worker.default_queue_name
|
14
|
-
}.merge!(args.extract_options!)
|
15
|
-
|
10
|
+
def enqueue(*args) # rubocop:disable CyclomaticComplexity
|
11
|
+
options = args.extract_options!
|
16
12
|
options[:payload_object] ||= args.shift
|
13
|
+
options[:priority] ||= Delayed::Worker.default_priority
|
14
|
+
|
15
|
+
if options[:queue].nil?
|
16
|
+
if options[:payload_object].respond_to?(:queue_name)
|
17
|
+
options[:queue] = options[:payload_object].queue_name
|
18
|
+
end
|
19
|
+
options[:queue] ||= Delayed::Worker.default_queue_name
|
20
|
+
end
|
17
21
|
|
18
22
|
if args.size > 0
|
19
23
|
warn '[DEPRECATION] Passing multiple arguments to `#enqueue` is deprecated. Pass a hash with :priority and :run_at.'
|
@@ -25,16 +29,10 @@ module Delayed
|
|
25
29
|
raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
|
26
30
|
end
|
27
31
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
job.save
|
33
|
-
end
|
34
|
-
end
|
35
|
-
else
|
36
|
-
Delayed::Job.new(:payload_object => options[:payload_object]).tap do |job|
|
37
|
-
job.invoke_job
|
32
|
+
new(options).tap do |job|
|
33
|
+
Delayed::Worker.lifecycle.run_callbacks(:enqueue, job) do
|
34
|
+
job.hook(:enqueue)
|
35
|
+
Delayed::Worker.delay_jobs ? job.save : job.invoke_job
|
38
36
|
end
|
39
37
|
end
|
40
38
|
end
|
@@ -84,14 +82,8 @@ module Delayed
|
|
84
82
|
end
|
85
83
|
|
86
84
|
def payload_object
|
87
|
-
|
88
|
-
|
89
|
-
# When the method is there, we need to load our YAML like this...
|
90
|
-
@payload_object ||= YAML.load(handler, :safe => false)
|
91
|
-
else
|
92
|
-
@payload_object ||= YAML.load(handler)
|
93
|
-
end
|
94
|
-
rescue TypeError, LoadError, NameError, ArgumentError => e
|
85
|
+
@payload_object ||= YAML.load_dj(handler)
|
86
|
+
rescue TypeError, LoadError, NameError, ArgumentError, SyntaxError, Psych::SyntaxError => e
|
95
87
|
raise DeserializationError, "Job failed to load: #{e.message}. Handler: #{handler.inspect}"
|
96
88
|
end
|
97
89
|
|
@@ -136,6 +128,17 @@ module Delayed
|
|
136
128
|
payload_object.max_attempts if payload_object.respond_to?(:max_attempts)
|
137
129
|
end
|
138
130
|
|
131
|
+
def max_run_time
|
132
|
+
return unless payload_object.respond_to?(:max_run_time)
|
133
|
+
return unless (run_time = payload_object.max_run_time)
|
134
|
+
|
135
|
+
if run_time > Delayed::Worker.max_run_time
|
136
|
+
Delayed::Worker.max_run_time
|
137
|
+
else
|
138
|
+
run_time
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
139
142
|
def fail!
|
140
143
|
update_attributes(:failed_at => self.class.db_time_now)
|
141
144
|
end
|
@@ -14,6 +14,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
14
14
|
Delayed::Worker.min_priority = nil
|
15
15
|
Delayed::Worker.default_priority = 99
|
16
16
|
Delayed::Worker.delay_jobs = true
|
17
|
+
Delayed::Worker.default_queue_name = 'default_tracking'
|
17
18
|
SimpleJob.runs = 0
|
18
19
|
described_class.delete_all
|
19
20
|
end
|
@@ -62,9 +63,19 @@ shared_examples_for 'a delayed_job backend' do
|
|
62
63
|
end
|
63
64
|
|
64
65
|
it 'is able to set queue' do
|
65
|
-
job = described_class.enqueue :payload_object =>
|
66
|
+
job = described_class.enqueue :payload_object => NamedQueueJob.new, :queue => 'tracking'
|
66
67
|
expect(job.queue).to eq('tracking')
|
67
68
|
end
|
69
|
+
|
70
|
+
it 'uses default queue' do
|
71
|
+
job = described_class.enqueue :payload_object => SimpleJob.new
|
72
|
+
expect(job.queue).to eq(Delayed::Worker.default_queue_name)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "uses the payload object's queue" do
|
76
|
+
job = described_class.enqueue :payload_object => NamedQueueJob.new
|
77
|
+
expect(job.queue).to eq(NamedQueueJob.new.queue_name)
|
78
|
+
end
|
68
79
|
end
|
69
80
|
|
70
81
|
context 'with multiple arguments' do
|
@@ -175,9 +186,17 @@ shared_examples_for 'a delayed_job backend' do
|
|
175
186
|
|
176
187
|
it 'raises a DeserializationError when the YAML.load raises argument error' do
|
177
188
|
job = described_class.new :handler => '--- !ruby/struct:GoingToRaiseArgError {}'
|
178
|
-
expect(YAML).to receive(:
|
189
|
+
expect(YAML).to receive(:load_dj).and_raise(ArgumentError)
|
179
190
|
expect { job.payload_object }.to raise_error(Delayed::DeserializationError)
|
180
191
|
end
|
192
|
+
|
193
|
+
it 'raises a DeserializationError when the YAML.load raises syntax error' do
|
194
|
+
# only test with Psych since the other YAML parsers don't raise a SyntaxError
|
195
|
+
if YAML.parser.class.name !~ /syck|yecht/i
|
196
|
+
job = described_class.new :handler => 'message: "no ending quote'
|
197
|
+
expect { job.payload_object }.to raise_error(Delayed::DeserializationError)
|
198
|
+
end
|
199
|
+
end
|
181
200
|
end
|
182
201
|
|
183
202
|
describe 'reserve' do
|
@@ -398,25 +417,58 @@ shared_examples_for 'a delayed_job backend' do
|
|
398
417
|
expect(@job.max_attempts).to be_nil
|
399
418
|
end
|
400
419
|
|
401
|
-
it 'uses the
|
420
|
+
it 'uses the max_attempts value on the payload when defined' do
|
402
421
|
expect(@job.payload_object).to receive(:max_attempts).and_return(99)
|
403
422
|
expect(@job.max_attempts).to eq(99)
|
404
423
|
end
|
405
424
|
end
|
406
425
|
|
426
|
+
describe '#max_run_time' do
|
427
|
+
before(:each) { @job = described_class.enqueue SimpleJob.new }
|
428
|
+
|
429
|
+
it 'is not defined' do
|
430
|
+
expect(@job.max_run_time).to be_nil
|
431
|
+
end
|
432
|
+
|
433
|
+
it 'results in a default run time when not defined' do
|
434
|
+
expect(worker.max_run_time(@job)).to eq(Delayed::Worker::DEFAULT_MAX_RUN_TIME)
|
435
|
+
end
|
436
|
+
|
437
|
+
it 'uses the max_run_time value on the payload when defined' do
|
438
|
+
expect(@job.payload_object).to receive(:max_run_time).and_return(30.minutes)
|
439
|
+
expect(@job.max_run_time).to eq(30.minutes)
|
440
|
+
end
|
441
|
+
|
442
|
+
it 'results in an overridden run time when defined' do
|
443
|
+
expect(@job.payload_object).to receive(:max_run_time).and_return(45.minutes)
|
444
|
+
expect(worker.max_run_time(@job)).to eq(45.minutes)
|
445
|
+
end
|
446
|
+
|
447
|
+
it 'job set max_run_time can not exceed default max run time' do
|
448
|
+
expect(@job.payload_object).to receive(:max_run_time).and_return(Delayed::Worker::DEFAULT_MAX_RUN_TIME + 60)
|
449
|
+
expect(worker.max_run_time(@job)).to eq(Delayed::Worker::DEFAULT_MAX_RUN_TIME)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
407
453
|
describe 'yaml serialization' do
|
408
454
|
context 'when serializing jobs' do
|
409
455
|
it 'raises error ArgumentError for new records' do
|
410
456
|
story = Story.new(:text => 'hello')
|
411
457
|
if story.respond_to?(:new_record?)
|
412
|
-
expect { story.delay.tell }.to raise_error(
|
458
|
+
expect { story.delay.tell }.to raise_error(
|
459
|
+
ArgumentError,
|
460
|
+
"job cannot be created for non-persisted record: #{story.inspect}"
|
461
|
+
)
|
413
462
|
end
|
414
463
|
end
|
415
464
|
|
416
465
|
it 'raises error ArgumentError for destroyed records' do
|
417
466
|
story = Story.create(:text => 'hello')
|
418
467
|
story.destroy
|
419
|
-
expect { story.delay.tell }.to raise_error(
|
468
|
+
expect { story.delay.tell }.to raise_error(
|
469
|
+
ArgumentError,
|
470
|
+
"job cannot be created for non-persisted record: #{story.inspect}"
|
471
|
+
)
|
420
472
|
end
|
421
473
|
end
|
422
474
|
|
data/lib/delayed/command.rb
CHANGED
@@ -109,7 +109,7 @@ module Delayed
|
|
109
109
|
Delayed::Worker.before_fork
|
110
110
|
Daemons.run_proc(process_name, :dir => options[:pid_dir], :dir_mode => :normal, :monitor => @monitor, :ARGV => @args) do |*_args|
|
111
111
|
$0 = File.join(options[:prefix], process_name) if @options[:prefix]
|
112
|
-
run process_name
|
112
|
+
run process_name, options
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
@@ -10,7 +10,7 @@ module Delayed
|
|
10
10
|
raise NoMethodError, "undefined method `#{method_name}' for #{object.inspect}" unless object.respond_to?(method_name, true)
|
11
11
|
|
12
12
|
if object.respond_to?(:persisted?) && !object.persisted?
|
13
|
-
raise
|
13
|
+
raise(ArgumentError, "job cannot be created for non-persisted record: #{object.inspect}")
|
14
14
|
end
|
15
15
|
|
16
16
|
self.object = object
|
@@ -19,7 +19,11 @@ module Delayed
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def display_name
|
22
|
-
|
22
|
+
if object.is_a?(Class)
|
23
|
+
"#{object}.#{method_name}"
|
24
|
+
else
|
25
|
+
"#{object.class}##{method_name}"
|
26
|
+
end
|
23
27
|
end
|
24
28
|
|
25
29
|
def perform
|
data/lib/delayed/psych_ext.rb
CHANGED
@@ -1,22 +1,3 @@
|
|
1
|
-
if defined?(ActiveRecord)
|
2
|
-
ActiveRecord::Base.class_eval do
|
3
|
-
# rubocop:disable BlockNesting
|
4
|
-
if instance_methods.include?(:encode_with)
|
5
|
-
def encode_with_override(coder)
|
6
|
-
encode_with_without_override(coder)
|
7
|
-
coder.tag = "!ruby/ActiveRecord:#{self.class.name}" if coder.respond_to?(:tag=)
|
8
|
-
end
|
9
|
-
alias_method :encode_with_without_override, :encode_with
|
10
|
-
alias_method :encode_with, :encode_with_override
|
11
|
-
else
|
12
|
-
def encode_with(coder)
|
13
|
-
coder['attributes'] = attributes
|
14
|
-
coder.tag = "!ruby/ActiveRecord:#{self.class.name}" if coder.respond_to?(:tag=)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
1
|
module Delayed
|
21
2
|
class PerformableMethod
|
22
3
|
# serialize to YAML
|
@@ -31,16 +12,41 @@ module Delayed
|
|
31
12
|
end
|
32
13
|
|
33
14
|
module Psych
|
34
|
-
|
35
|
-
|
36
|
-
|
15
|
+
def self.load_dj(yaml)
|
16
|
+
result = parse(yaml)
|
17
|
+
result ? Delayed::PsychExt::ToRuby.create.accept(result) : result
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Delayed
|
22
|
+
module PsychExt
|
23
|
+
class ToRuby < Psych::Visitors::ToRuby
|
24
|
+
unless respond_to?(:create)
|
25
|
+
def self.create
|
26
|
+
new
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def visit_Psych_Nodes_Mapping(object) # rubocop:disable CyclomaticComplexity, MethodName, PerceivedComplexity
|
37
31
|
return revive(Psych.load_tags[object.tag], object) if Psych.load_tags[object.tag]
|
38
32
|
|
39
33
|
case object.tag
|
34
|
+
when /^!ruby\/object/
|
35
|
+
result = super
|
36
|
+
if defined?(ActiveRecord::Base) && result.is_a?(ActiveRecord::Base)
|
37
|
+
begin
|
38
|
+
result.class.find(result[result.class.primary_key])
|
39
|
+
rescue ActiveRecord::RecordNotFound => error # rubocop:disable BlockNesting
|
40
|
+
raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass}, primary key: #{id} (#{error.message})"
|
41
|
+
end
|
42
|
+
else
|
43
|
+
result
|
44
|
+
end
|
40
45
|
when /^!ruby\/ActiveRecord:(.+)$/
|
41
46
|
klass = resolve_class(Regexp.last_match[1])
|
42
47
|
payload = Hash[*object.children.map { |c| accept c }]
|
43
48
|
id = payload['attributes'][klass.primary_key]
|
49
|
+
id = id.value if defined?(ActiveRecord::Attribute) && id.is_a?(ActiveRecord::Attribute)
|
44
50
|
begin
|
45
51
|
klass.unscoped.find(id)
|
46
52
|
rescue ActiveRecord::RecordNotFound => error
|
@@ -66,17 +72,16 @@ module Psych
|
|
66
72
|
raise Delayed::DeserializationError, "DataMapper::ObjectNotFoundError, class: #{klass} (#{error.message})"
|
67
73
|
end
|
68
74
|
else
|
69
|
-
|
75
|
+
super
|
70
76
|
end
|
71
77
|
end
|
72
|
-
alias_method_chain :visit_Psych_Nodes_Mapping, :class
|
73
78
|
|
74
|
-
def
|
79
|
+
def resolve_class(klass_name)
|
80
|
+
return nil if !klass_name || klass_name.empty?
|
75
81
|
klass_name.constantize
|
76
82
|
rescue
|
77
|
-
|
83
|
+
super
|
78
84
|
end
|
79
|
-
alias_method_chain :resolve_class, :constantize
|
80
85
|
end
|
81
86
|
end
|
82
87
|
end
|
data/lib/delayed/railtie.rb
CHANGED
@@ -7,6 +7,12 @@ module Delayed
|
|
7
7
|
ActiveSupport.on_load(:action_mailer) do
|
8
8
|
ActionMailer::Base.extend(Delayed::DelayMail)
|
9
9
|
end
|
10
|
+
|
11
|
+
Delayed::Worker.logger = if defined?(Rails)
|
12
|
+
Rails.logger
|
13
|
+
elsif defined?(RAILS_DEFAULT_LOGGER)
|
14
|
+
RAILS_DEFAULT_LOGGER
|
15
|
+
end
|
10
16
|
end
|
11
17
|
|
12
18
|
rake_tasks do
|
data/lib/delayed/recipes.rb
CHANGED
@@ -38,7 +38,7 @@ 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"
|
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'
|
data/lib/delayed/syck_ext.rb
CHANGED
@@ -32,3 +32,11 @@ class Struct
|
|
32
32
|
"Struct::#{ name }"
|
33
33
|
end
|
34
34
|
end
|
35
|
+
|
36
|
+
module YAML
|
37
|
+
def load_dj(yaml)
|
38
|
+
# See https://github.com/dtao/safe_yaml
|
39
|
+
# When the method is there, we need to load our YAML like this...
|
40
|
+
respond_to?(:unsafe_load) ? load(yaml, :safe => false) : load(yaml)
|
41
|
+
end
|
42
|
+
end
|
data/lib/delayed/tasks.rb
CHANGED
@@ -21,6 +21,9 @@ namespace :jobs do
|
|
21
21
|
:queues => (ENV['QUEUES'] || ENV['QUEUE'] || '').split(','),
|
22
22
|
:quiet => false
|
23
23
|
}
|
24
|
+
|
25
|
+
@worker_options[:sleep_delay] = ENV['SLEEP_DELAY'].to_i if ENV['SLEEP_DELAY']
|
26
|
+
@worker_options[:read_ahead] = ENV['READ_AHEAD'].to_i if ENV['READ_AHEAD']
|
24
27
|
end
|
25
28
|
|
26
29
|
desc "Exit with error status if any jobs older than max_age seconds haven't been attempted yet."
|
data/lib/delayed/worker.rb
CHANGED
@@ -19,7 +19,8 @@ module Delayed
|
|
19
19
|
|
20
20
|
cattr_accessor :min_priority, :max_priority, :max_attempts, :max_run_time,
|
21
21
|
:default_priority, :sleep_delay, :logger, :delay_jobs, :queues,
|
22
|
-
:read_ahead, :plugins, :destroy_failed_jobs, :exit_on_complete
|
22
|
+
:read_ahead, :plugins, :destroy_failed_jobs, :exit_on_complete,
|
23
|
+
:default_log_level
|
23
24
|
|
24
25
|
# Named queue into which jobs are enqueued by default
|
25
26
|
cattr_accessor :default_queue_name
|
@@ -30,13 +31,14 @@ module Delayed
|
|
30
31
|
attr_accessor :name_prefix
|
31
32
|
|
32
33
|
def self.reset
|
33
|
-
self.
|
34
|
-
self.
|
35
|
-
self.
|
36
|
-
self.
|
37
|
-
self.
|
38
|
-
self.
|
39
|
-
self.
|
34
|
+
self.default_log_level = DEFAULT_LOG_LEVEL
|
35
|
+
self.sleep_delay = DEFAULT_SLEEP_DELAY
|
36
|
+
self.max_attempts = DEFAULT_MAX_ATTEMPTS
|
37
|
+
self.max_run_time = DEFAULT_MAX_RUN_TIME
|
38
|
+
self.default_priority = DEFAULT_DEFAULT_PRIORITY
|
39
|
+
self.delay_jobs = DEFAULT_DELAY_JOBS
|
40
|
+
self.queues = DEFAULT_QUEUES
|
41
|
+
self.read_ahead = DEFAULT_READ_AHEAD
|
40
42
|
end
|
41
43
|
|
42
44
|
reset
|
@@ -57,12 +59,6 @@ module Delayed
|
|
57
59
|
cattr_accessor :raise_signal_exceptions
|
58
60
|
self.raise_signal_exceptions = false
|
59
61
|
|
60
|
-
self.logger = if defined?(Rails)
|
61
|
-
Rails.logger
|
62
|
-
elsif defined?(RAILS_DEFAULT_LOGGER)
|
63
|
-
RAILS_DEFAULT_LOGGER
|
64
|
-
end
|
65
|
-
|
66
62
|
def self.backend=(backend)
|
67
63
|
if backend.is_a? Symbol
|
68
64
|
require "delayed/serialization/#{backend}"
|
@@ -130,13 +126,13 @@ module Delayed
|
|
130
126
|
|
131
127
|
def start # rubocop:disable CyclomaticComplexity, PerceivedComplexity
|
132
128
|
trap('TERM') do
|
133
|
-
say 'Exiting...'
|
129
|
+
Thread.new { say 'Exiting...' }
|
134
130
|
stop
|
135
131
|
raise SignalException, 'TERM' if self.class.raise_signal_exceptions
|
136
132
|
end
|
137
133
|
|
138
134
|
trap('INT') do
|
139
|
-
say 'Exiting...'
|
135
|
+
Thread.new { say 'Exiting...' }
|
140
136
|
stop
|
141
137
|
raise SignalException, 'INT' if self.class.raise_signal_exceptions && self.class.raise_signal_exceptions != :term
|
142
138
|
end
|
@@ -200,7 +196,7 @@ module Delayed
|
|
200
196
|
def run(job)
|
201
197
|
job_say job, 'RUNNING'
|
202
198
|
runtime = Benchmark.realtime do
|
203
|
-
Timeout.timeout(
|
199
|
+
Timeout.timeout(max_run_time(job).to_i, WorkerTimeout) { job.invoke_job }
|
204
200
|
job.destroy
|
205
201
|
end
|
206
202
|
job_say job, format('COMPLETED after %.4f', runtime)
|
@@ -240,12 +236,12 @@ module Delayed
|
|
240
236
|
end
|
241
237
|
end
|
242
238
|
|
243
|
-
def job_say(job, text, level =
|
239
|
+
def job_say(job, text, level = default_log_level)
|
244
240
|
text = "Job #{job.name} (id=#{job.id}) #{text}"
|
245
241
|
say text, level
|
246
242
|
end
|
247
243
|
|
248
|
-
def say(text, level =
|
244
|
+
def say(text, level = default_log_level)
|
249
245
|
text = "[Worker(#{name})] #{text}"
|
250
246
|
puts text unless @quiet
|
251
247
|
return unless logger
|
@@ -260,6 +256,10 @@ module Delayed
|
|
260
256
|
job.max_attempts || self.class.max_attempts
|
261
257
|
end
|
262
258
|
|
259
|
+
def max_run_time(job)
|
260
|
+
job.max_run_time || self.class.max_run_time
|
261
|
+
end
|
262
|
+
|
263
263
|
protected
|
264
264
|
|
265
265
|
def handle_failed_job(job, error)
|
data/spec/helper.rb
CHANGED
@@ -20,9 +20,20 @@ require 'active_record'
|
|
20
20
|
require 'delayed_job'
|
21
21
|
require 'delayed/backend/shared_spec'
|
22
22
|
|
23
|
-
|
23
|
+
if ENV['DEBUG_LOGS']
|
24
|
+
Delayed::Worker.logger = Logger.new(STDOUT)
|
25
|
+
else
|
26
|
+
require 'tempfile'
|
27
|
+
|
28
|
+
tf = Tempfile.new('dj.log')
|
29
|
+
Delayed::Worker.logger = Logger.new(tf.path)
|
30
|
+
tf.unlink
|
31
|
+
end
|
24
32
|
ENV['RAILS_ENV'] = 'test'
|
25
33
|
|
34
|
+
# Trigger AR to initialize
|
35
|
+
ActiveRecord::Base # rubocop:disable Void
|
36
|
+
|
26
37
|
module Rails
|
27
38
|
def self.root
|
28
39
|
'.'
|
@@ -37,6 +37,16 @@ describe Delayed::PerformableMethod do
|
|
37
37
|
expect { Delayed::PerformableMethod.new(clazz.new, :private_method, []) }.not_to raise_error
|
38
38
|
end
|
39
39
|
|
40
|
+
describe 'display_name' do
|
41
|
+
it 'returns class_name#method_name for instance methods' do
|
42
|
+
expect(Delayed::PerformableMethod.new('foo', :count, ['o']).display_name).to eq('String#count')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns class_name.method_name for class methods' do
|
46
|
+
expect(Delayed::PerformableMethod.new(Class, :inspect, []).display_name).to eq('Class.inspect')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
40
50
|
describe 'hooks' do
|
41
51
|
%w[before after success].each do |hook|
|
42
52
|
it "delegates #{hook} hook to object" do
|
data/spec/sample_jobs.rb
CHANGED
data/spec/worker_spec.rb
CHANGED
@@ -29,7 +29,15 @@ describe Delayed::Worker do
|
|
29
29
|
|
30
30
|
it 'logs with job name and id' do
|
31
31
|
expect(@worker).to receive(:say).
|
32
|
-
with('Job ExampleJob (id=123) message', Delayed::Worker
|
32
|
+
with('Job ExampleJob (id=123) message', Delayed::Worker.default_log_level)
|
33
|
+
@worker.job_say(@job, 'message')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'has a configurable default log level' do
|
37
|
+
Delayed::Worker.default_log_level = 'error'
|
38
|
+
|
39
|
+
expect(@worker).to receive(:say).
|
40
|
+
with('Job ExampleJob (id=123) message', 'error')
|
33
41
|
@worker.job_say(@job, 'message')
|
34
42
|
end
|
35
43
|
end
|
@@ -140,10 +148,10 @@ describe Delayed::Worker do
|
|
140
148
|
it_behaves_like 'a worker which logs on the correct severity', severity
|
141
149
|
end
|
142
150
|
|
143
|
-
it
|
151
|
+
it 'logs a message on the default log\'s level' do
|
144
152
|
expect(@worker.logger).to receive(:send).
|
145
153
|
with('info', "#{@expected_time}: #{@worker_name} #{@text}")
|
146
|
-
@worker.say(@text, Delayed::Worker
|
154
|
+
@worker.say(@text, Delayed::Worker.default_log_level)
|
147
155
|
end
|
148
156
|
end
|
149
157
|
end
|
data/spec/yaml_ext_spec.rb
CHANGED
@@ -4,32 +4,45 @@ describe 'YAML' do
|
|
4
4
|
it 'autoloads classes' do
|
5
5
|
expect do
|
6
6
|
yaml = "--- !ruby/class Autoloaded::Clazz\n"
|
7
|
-
expect(
|
7
|
+
expect(load_with_delayed_visitor(yaml)).to eq(Autoloaded::Clazz)
|
8
8
|
end.not_to raise_error
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'autoloads the class of a struct' do
|
12
12
|
expect do
|
13
13
|
yaml = "--- !ruby/class Autoloaded::Struct\n"
|
14
|
-
expect(
|
14
|
+
expect(load_with_delayed_visitor(yaml)).to eq(Autoloaded::Struct)
|
15
15
|
end.not_to raise_error
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'autoloads the class for the instance of a struct' do
|
19
19
|
expect do
|
20
20
|
yaml = '--- !ruby/struct:Autoloaded::InstanceStruct {}'
|
21
|
-
expect(
|
21
|
+
expect(load_with_delayed_visitor(yaml).class).to eq(Autoloaded::InstanceStruct)
|
22
|
+
end.not_to raise_error
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'autoloads the class of an anonymous struct' do
|
26
|
+
expect do
|
27
|
+
yaml = "--- !ruby/struct\nn: 1\n"
|
28
|
+
object = YAML.load(yaml)
|
29
|
+
expect(object).to be_kind_of(Struct)
|
30
|
+
expect(object.n).to eq(1)
|
22
31
|
end.not_to raise_error
|
23
32
|
end
|
24
33
|
|
25
34
|
it 'autoloads the class for the instance' do
|
26
35
|
expect do
|
27
36
|
yaml = "--- !ruby/object:Autoloaded::InstanceClazz {}\n"
|
28
|
-
expect(
|
37
|
+
expect(load_with_delayed_visitor(yaml).class).to eq(Autoloaded::InstanceClazz)
|
29
38
|
end.not_to raise_error
|
30
39
|
end
|
31
40
|
|
32
41
|
it 'does not throw an uninitialized constant Syck::Syck when using YAML.load with poorly formed yaml' do
|
33
42
|
expect { YAML.load(YAML.dump('foo: *bar')) }.not_to raise_error
|
34
43
|
end
|
44
|
+
|
45
|
+
def load_with_delayed_visitor(yaml)
|
46
|
+
YAML.load_dj(yaml)
|
47
|
+
end
|
35
48
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delayed_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandon Keepers
|
@@ -15,7 +15,7 @@ authors:
|
|
15
15
|
autorequire:
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
|
-
date: 2014-09-
|
18
|
+
date: 2014-09-24 00:00:00.000000000 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: activesupport
|