delayed_job 4.0.3 → 4.0.4
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.
- 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
|