delayed_job 4.0.6 → 4.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +54 -0
- data/README.md +89 -15
- data/delayed_job.gemspec +5 -3
- data/lib/delayed/backend/base.rb +26 -32
- data/lib/delayed/backend/job_preparer.rb +53 -0
- data/lib/delayed/backend/shared_spec.rb +92 -10
- data/lib/delayed/command.rb +40 -14
- data/lib/delayed/exceptions.rb +1 -1
- data/lib/delayed/lifecycle.rb +2 -2
- data/lib/delayed/message_sending.rb +29 -17
- data/lib/delayed/performable_method.rb +6 -4
- data/lib/delayed/psych_ext.rb +20 -7
- data/lib/delayed/railtie.rb +0 -4
- data/lib/delayed/recipes.rb +3 -3
- data/lib/delayed/serialization/active_record.rb +1 -1
- data/lib/delayed/syck_ext.rb +3 -3
- data/lib/delayed/tasks.rb +1 -1
- data/lib/delayed/worker.rb +62 -24
- data/lib/delayed_job.rb +11 -7
- data/lib/generators/delayed_job/delayed_job_generator.rb +1 -1
- data/spec/autoloaded/clazz.rb +1 -2
- data/spec/autoloaded/instance_clazz.rb +1 -2
- data/spec/autoloaded/instance_struct.rb +3 -3
- data/spec/autoloaded/struct.rb +3 -3
- data/spec/daemons.rb +2 -0
- data/spec/delayed/backend/test.rb +0 -5
- data/spec/delayed/command_spec.rb +131 -9
- data/spec/helper.rb +0 -4
- data/spec/message_sending_spec.rb +29 -4
- data/spec/performable_mailer_spec.rb +0 -1
- data/spec/performable_method_spec.rb +3 -4
- data/spec/psych_ext_spec.rb +23 -1
- data/spec/sample_jobs.rb +5 -3
- data/spec/worker_spec.rb +27 -1
- metadata +17 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3671145ff491daea2d3e26336b54cdae0d4e0293654c11fdcb3a41a516741219
|
4
|
+
data.tar.gz: 1dab4a137c0cec054cd7c73fc395d5a918f5fc7cc619ae3879687ae17b36a439
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 707798d3698ef7e67896381a43b75f03f0b63653c79c514b3e932b53b6883ded11476fe8b5749e44b5722562d7f479fdb6b70f46c9c279fe08fe9eaafb0110a6
|
7
|
+
data.tar.gz: 917341ca90f82622329579ae796378b241822903f5570488e2a6f9dc785028f7e14567c395d5258b409ba8b2c30ee062c0cfeaf985e094b185416296d0e3a18b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,57 @@
|
|
1
|
+
4.1.8 - 2019-08-16
|
2
|
+
=================
|
3
|
+
* Support for Rails 6.0.0
|
4
|
+
|
5
|
+
4.1.7 - 2019-06-20
|
6
|
+
=================
|
7
|
+
* Fix loading Delayed::PerformableMailer when ActionMailer isn't loaded yet
|
8
|
+
|
9
|
+
4.1.6 - 2019-06-19
|
10
|
+
=================
|
11
|
+
* Properly initialize ActionMailer outside railties (#1077)
|
12
|
+
* Fix Psych load_tags support (#1093)
|
13
|
+
* Replace REMOVED with FAILED in log message (#1048)
|
14
|
+
* Misc doc updates (#1052, #1074, #1064, #1063)
|
15
|
+
|
16
|
+
4.1.5 - 2018-04-13
|
17
|
+
=================
|
18
|
+
* Allow Rails 5.2
|
19
|
+
|
20
|
+
4.1.4 - 2017-12-29
|
21
|
+
=================
|
22
|
+
* Use `yaml_tag` instead of deprecated `yaml_as` (#996)
|
23
|
+
* Support ruby 2.5.0
|
24
|
+
|
25
|
+
4.1.3 - 2017-05-26
|
26
|
+
=================
|
27
|
+
* Don't mutate the options hash (#877)
|
28
|
+
* Log an error message when a deserialization error occurs (#894)
|
29
|
+
* Adding the queue name to the log output (#917)
|
30
|
+
* Don't include ClassMethods with MessageSending (#924)
|
31
|
+
* Fix YAML deserialization error if original object is soft-deleted (#947)
|
32
|
+
* Add support for Rails 5.1 (#982)
|
33
|
+
|
34
|
+
4.1.2 - 2016-05-16
|
35
|
+
==================
|
36
|
+
* Added Delayed::Worker.queue_attributes
|
37
|
+
* Limit what we require in ActiveSupport
|
38
|
+
* Fix pid file creation when there is no tmp directory
|
39
|
+
* Rails 5 support
|
40
|
+
|
41
|
+
4.1.1 - 2015-09-24
|
42
|
+
==================
|
43
|
+
* Fix shared specs for back-ends that reload objects
|
44
|
+
|
45
|
+
4.1.0 - 2015-09-22
|
46
|
+
==================
|
47
|
+
* Alter `Delayed::Command` to work with or without Rails
|
48
|
+
* Allow `Delayed::Worker.delay_jobs` configuration to be a proc
|
49
|
+
* Add ability to set destroy failed jobs on a per job basis
|
50
|
+
* Make `Delayed::Worker.new` idempotent
|
51
|
+
* Set quiet from the environment
|
52
|
+
* Rescue `Exception` instead of `StandardError` in worker
|
53
|
+
* Fix worker crash on serialization error
|
54
|
+
|
1
55
|
4.0.6 - 2014-12-22
|
2
56
|
==================
|
3
57
|
* Revert removing test files from the gem
|
data/README.md
CHANGED
@@ -1,14 +1,17 @@
|
|
1
|
+
**If you're viewing this at https://github.com/collectiveidea/delayed_job,
|
2
|
+
you're reading the documentation for the master branch.
|
3
|
+
[View documentation for the latest release
|
4
|
+
(4.1.8).](https://github.com/collectiveidea/delayed_job/tree/v4.1.8)**
|
5
|
+
|
1
6
|
Delayed::Job
|
2
7
|
============
|
3
|
-
[][coveralls]
|
8
|
+
[][gem]
|
9
|
+
[][travis]
|
10
|
+
[][codeclimate]
|
11
|
+
[][coveralls]
|
8
12
|
|
9
13
|
[gem]: https://rubygems.org/gems/delayed_job
|
10
14
|
[travis]: https://travis-ci.org/collectiveidea/delayed_job
|
11
|
-
[gemnasium]: https://gemnasium.com/collectiveidea/delayed_job
|
12
15
|
[codeclimate]: https://codeclimate.com/github/collectiveidea/delayed_job
|
13
16
|
[coveralls]: https://coveralls.io/r/collectiveidea/delayed_job
|
14
17
|
|
@@ -32,8 +35,7 @@ multitude of core tasks. Amongst those tasks are:
|
|
32
35
|
|
33
36
|
Installation
|
34
37
|
============
|
35
|
-
delayed_job 3.0.0 only supports Rails 3.0+.
|
36
|
-
branch](https://github.com/collectiveidea/delayed_job/tree/v2.0) for Rails 2.
|
38
|
+
delayed_job 3.0.0 only supports Rails 3.0+.
|
37
39
|
|
38
40
|
delayed_job supports multiple backends for storing the job queue. [See the wiki
|
39
41
|
for other backends](https://github.com/collectiveidea/delayed_job/wiki/Backends).
|
@@ -58,14 +60,30 @@ running the following command:
|
|
58
60
|
rails generate delayed_job:active_record
|
59
61
|
rake db:migrate
|
60
62
|
|
63
|
+
For Rails 4.2+, see [below](#active-job)
|
64
|
+
|
61
65
|
Development
|
62
66
|
===========
|
63
67
|
In development mode, if you are using Rails 3.1+, your application code will automatically reload every 100 jobs or when the queue finishes.
|
64
68
|
You no longer need to restart Delayed Job every time you update your code in development.
|
65
69
|
|
66
|
-
|
67
|
-
|
68
|
-
|
70
|
+
Active Job
|
71
|
+
==========
|
72
|
+
In Rails 4.2+, set the queue_adapter in config/application.rb
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
config.active_job.queue_adapter = :delayed_job
|
76
|
+
```
|
77
|
+
|
78
|
+
See the [rails guide](http://guides.rubyonrails.org/active_job_basics.html#setting-the-backend) for more details.
|
79
|
+
|
80
|
+
Rails 4.x
|
81
|
+
=========
|
82
|
+
If you are using the protected_attributes gem, it must appear before delayed_job in your gemfile. If your jobs are failing with:
|
83
|
+
|
84
|
+
ActiveRecord::StatementInvalid: PG::NotNullViolation: ERROR: null value in column "handler" violates not-null constraint
|
85
|
+
|
86
|
+
then this is the fix you're looking for.
|
69
87
|
|
70
88
|
Upgrading from 2.x to 3.0.0 on Active Record
|
71
89
|
============================================
|
@@ -103,9 +121,17 @@ device = Device.new
|
|
103
121
|
device.deliver
|
104
122
|
```
|
105
123
|
|
106
|
-
|
107
|
-
|
108
|
-
|
124
|
+
## Parameters
|
125
|
+
|
126
|
+
`#handle_asynchronously` and `#delay` take these parameters:
|
127
|
+
|
128
|
+
- `:priority` (number): lower numbers run first; default is 0 but can be reconfigured (see below)
|
129
|
+
- `:run_at` (Time): run the job after this time (probably in the future)
|
130
|
+
- `:queue` (string): named queue to put this job in, an alternative to priorities (see below)
|
131
|
+
|
132
|
+
These params can be Proc objects, allowing call-time evaluation of the value.
|
133
|
+
|
134
|
+
For example:
|
109
135
|
|
110
136
|
```ruby
|
111
137
|
class LongTasks
|
@@ -176,6 +202,27 @@ Delayed::Job.enqueue job, :queue => 'tracking'
|
|
176
202
|
handle_asynchronously :tweet_later, :queue => 'tweets'
|
177
203
|
```
|
178
204
|
|
205
|
+
You can configure default priorities for named queues:
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
Delayed::Worker.queue_attributes = {
|
209
|
+
high_priority: { priority: -10 },
|
210
|
+
low_priority: { priority: 10 }
|
211
|
+
}
|
212
|
+
```
|
213
|
+
|
214
|
+
Configured queue priorities can be overriden by passing priority to the delay method
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
object.delay(:queue => 'high_priority', priority: 0).method
|
218
|
+
```
|
219
|
+
|
220
|
+
You can start processes to only work certain queues with the `queue` and `queues`
|
221
|
+
options defined below. Processes started without specifying a queue will run jobs
|
222
|
+
from **any** queue. To effectively have a process that runs jobs where a queue is not
|
223
|
+
specified, set a default queue name with `Delayed::Worker.default_queue_name` and
|
224
|
+
have the processes run that queue.
|
225
|
+
|
179
226
|
Running Jobs
|
180
227
|
============
|
181
228
|
`script/delayed_job` can be used to manage a background process which will
|
@@ -282,6 +329,20 @@ NewsletterJob = Struct.new(:text, :emails) do
|
|
282
329
|
end
|
283
330
|
```
|
284
331
|
|
332
|
+
To set a per-job default for destroying failed jobs that overrides the Delayed::Worker.destroy_failed_jobs you can define a destroy_failed_jobs? method on the job
|
333
|
+
|
334
|
+
```ruby
|
335
|
+
NewsletterJob = Struct.new(:text, :emails) do
|
336
|
+
def perform
|
337
|
+
emails.each { |e| NewsletterMailer.deliver_text_to_email(text, e) }
|
338
|
+
end
|
339
|
+
|
340
|
+
def destroy_failed_jobs?
|
341
|
+
false
|
342
|
+
end
|
343
|
+
end
|
344
|
+
```
|
345
|
+
|
285
346
|
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
|
286
347
|
|
287
348
|
```ruby
|
@@ -314,6 +375,9 @@ Hooks
|
|
314
375
|
=====
|
315
376
|
You can define hooks on your job that will be called at different stages in the process:
|
316
377
|
|
378
|
+
|
379
|
+
**NOTE:** If you are using ActiveJob these hooks are **not** available to your jobs. You will need to use ActiveJob's callbacks. You can find details here https://guides.rubyonrails.org/active_job_basics.html#callbacks
|
380
|
+
|
317
381
|
```ruby
|
318
382
|
class ParanoidNewsletterJob < NewsletterJob
|
319
383
|
def enqueue(job)
|
@@ -367,7 +431,7 @@ end
|
|
367
431
|
|
368
432
|
On error, the job is scheduled again in 5 seconds + N ** 4, where N is the number of attempts or using the job's defined `reschedule_at` method.
|
369
433
|
|
370
|
-
The default `Worker.max_attempts` is 25. After this, the job either deleted (default), or left in the database with "failed_at" set.
|
434
|
+
The default `Worker.max_attempts` is 25. After this, the job is either deleted (default), or left in the database with "failed_at" set.
|
371
435
|
With the default of 25 attempts, the last retry will be 20 days later, with the last interval being almost 100 hours.
|
372
436
|
|
373
437
|
The default `Worker.max_run_time` is 4.hours. If your job takes longer than that, another computer could pick it up. It's up to you to
|
@@ -382,8 +446,18 @@ The default behavior is to read 5 jobs from the queue when finding an available
|
|
382
446
|
|
383
447
|
By default all jobs will be queued without a named queue. A default named queue can be specified by using `Delayed::Worker.default_queue_name`.
|
384
448
|
|
449
|
+
If no jobs are found, the worker sleeps for the amount of time specified by the sleep delay option. Set `Delayed::Worker.sleep_delay = 60` for a 60 second sleep time.
|
450
|
+
|
385
451
|
It is possible to disable delayed jobs for testing purposes. Set `Delayed::Worker.delay_jobs = false` to execute all jobs realtime.
|
386
452
|
|
453
|
+
Or `Delayed::Worker.delay_jobs` can be a Proc that decides whether to execute jobs inline on a per-job basis:
|
454
|
+
|
455
|
+
```ruby
|
456
|
+
Delayed::Worker.delay_jobs = ->(job) {
|
457
|
+
job.queue != 'inline'
|
458
|
+
}
|
459
|
+
```
|
460
|
+
|
387
461
|
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.
|
388
462
|
|
389
463
|
Here is an example of changing job parameters in Rails:
|
data/delayed_job.gemspec
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
Gem::Specification.new do |spec|
|
2
|
-
spec.add_dependency 'activesupport', ['>= 3.0', '<
|
4
|
+
spec.add_dependency 'activesupport', ['>= 3.0', '< 6.1']
|
3
5
|
spec.authors = ['Brandon Keepers', 'Brian Ryckbost', 'Chris Gaffney', 'David Genord II', 'Erik Michaels-Ober', 'Matt Griffin', 'Steve Richert', 'Tobias Lütke']
|
4
6
|
spec.description = 'Delayed_job (or DJ) encapsulates the common pattern of asynchronously executing longer tasks in the background. It is a direct extraction from Shopify where the job table is responsible for a multitude of core tasks.'
|
5
7
|
spec.email = ['brian@collectiveidea.com']
|
6
8
|
spec.files = %w[CHANGELOG.md CONTRIBUTING.md LICENSE.md README.md Rakefile delayed_job.gemspec]
|
7
|
-
spec.files
|
9
|
+
spec.files += Dir.glob('{contrib,lib,recipes,spec}/**/*') # rubocop:disable SpaceAroundOperators
|
8
10
|
spec.homepage = 'http://github.com/collectiveidea/delayed_job'
|
9
11
|
spec.licenses = ['MIT']
|
10
12
|
spec.name = 'delayed_job'
|
11
13
|
spec.require_paths = ['lib']
|
12
14
|
spec.summary = 'Database-backed asynchronous priority queue system -- Extracted from Shopify'
|
13
15
|
spec.test_files = Dir.glob('spec/**/*')
|
14
|
-
spec.version = '4.
|
16
|
+
spec.version = '4.1.8'
|
15
17
|
end
|
data/lib/delayed/backend/base.rb
CHANGED
@@ -7,32 +7,16 @@ module Delayed
|
|
7
7
|
|
8
8
|
module ClassMethods
|
9
9
|
# Add a job to the queue
|
10
|
-
def enqueue(*args)
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
21
|
-
|
22
|
-
if args.size > 0
|
23
|
-
warn '[DEPRECATION] Passing multiple arguments to `#enqueue` is deprecated. Pass a hash with :priority and :run_at.'
|
24
|
-
options[:priority] = args.first || options[:priority]
|
25
|
-
options[:run_at] = args[1]
|
26
|
-
end
|
27
|
-
|
28
|
-
unless options[:payload_object].respond_to?(:perform)
|
29
|
-
raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
|
30
|
-
end
|
10
|
+
def enqueue(*args)
|
11
|
+
job_options = Delayed::Backend::JobPreparer.new(*args).prepare
|
12
|
+
enqueue_job(job_options)
|
13
|
+
end
|
31
14
|
|
15
|
+
def enqueue_job(options)
|
32
16
|
new(options).tap do |job|
|
33
17
|
Delayed::Worker.lifecycle.run_callbacks(:enqueue, job) do
|
34
18
|
job.hook(:enqueue)
|
35
|
-
Delayed::Worker.
|
19
|
+
Delayed::Worker.delay_job?(job) ? job.save : job.invoke_job
|
36
20
|
end
|
37
21
|
end
|
38
22
|
end
|
@@ -46,16 +30,13 @@ module Delayed
|
|
46
30
|
end
|
47
31
|
|
48
32
|
# Allow the backend to attempt recovery from reserve errors
|
49
|
-
def recover_from(_error)
|
50
|
-
end
|
33
|
+
def recover_from(_error); end
|
51
34
|
|
52
35
|
# Hook method that is called before a new worker is forked
|
53
|
-
def before_fork
|
54
|
-
end
|
36
|
+
def before_fork; end
|
55
37
|
|
56
38
|
# Hook method that is called after a new worker is forked
|
57
|
-
def after_fork
|
58
|
-
end
|
39
|
+
def after_fork; end
|
59
40
|
|
60
41
|
def work_off(num = 100)
|
61
42
|
warn '[DEPRECATION] `Delayed::Job.work_off` is deprecated. Use `Delayed::Worker.new.work_off instead.'
|
@@ -63,12 +44,18 @@ module Delayed
|
|
63
44
|
end
|
64
45
|
end
|
65
46
|
|
47
|
+
attr_reader :error
|
48
|
+
def error=(error)
|
49
|
+
@error = error
|
50
|
+
self.last_error = "#{error.message}\n#{error.backtrace.join("\n")}" if respond_to?(:last_error=)
|
51
|
+
end
|
52
|
+
|
66
53
|
def failed?
|
67
54
|
!!failed_at
|
68
55
|
end
|
69
56
|
alias_method :failed, :failed?
|
70
57
|
|
71
|
-
ParseObjectFromYaml =
|
58
|
+
ParseObjectFromYaml = %r{\!ruby/\w+\:([^\s]+)} # rubocop:disable ConstantName
|
72
59
|
|
73
60
|
def name
|
74
61
|
@name ||= payload_object.respond_to?(:display_name) ? payload_object.display_name : payload_object.class.name
|
@@ -93,7 +80,7 @@ module Delayed
|
|
93
80
|
hook :before
|
94
81
|
payload_object.perform
|
95
82
|
hook :success
|
96
|
-
rescue => e
|
83
|
+
rescue Exception => e # rubocop:disable RescueException
|
97
84
|
hook :error, e
|
98
85
|
raise e
|
99
86
|
ensure
|
@@ -111,7 +98,7 @@ module Delayed
|
|
111
98
|
def hook(name, *args)
|
112
99
|
if payload_object.respond_to?(name)
|
113
100
|
method = payload_object.method(name)
|
114
|
-
method.arity
|
101
|
+
method.arity.zero? ? method.call : method.call(self, *args)
|
115
102
|
end
|
116
103
|
rescue DeserializationError # rubocop:disable HandleExceptions
|
117
104
|
end
|
@@ -139,8 +126,15 @@ module Delayed
|
|
139
126
|
end
|
140
127
|
end
|
141
128
|
|
129
|
+
def destroy_failed_jobs?
|
130
|
+
payload_object.respond_to?(:destroy_failed_jobs?) ? payload_object.destroy_failed_jobs? : Delayed::Worker.destroy_failed_jobs
|
131
|
+
rescue DeserializationError
|
132
|
+
Delayed::Worker.destroy_failed_jobs
|
133
|
+
end
|
134
|
+
|
142
135
|
def fail!
|
143
|
-
|
136
|
+
self.failed_at = self.class.db_time_now
|
137
|
+
save!
|
144
138
|
end
|
145
139
|
|
146
140
|
protected
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Delayed
|
2
|
+
module Backend
|
3
|
+
class JobPreparer
|
4
|
+
attr_reader :options, :args
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@options = args.extract_options!.dup
|
8
|
+
@args = args
|
9
|
+
end
|
10
|
+
|
11
|
+
def prepare
|
12
|
+
set_payload
|
13
|
+
set_queue_name
|
14
|
+
set_priority
|
15
|
+
handle_deprecation
|
16
|
+
options
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def set_payload
|
22
|
+
options[:payload_object] ||= args.shift
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_queue_name
|
26
|
+
if options[:queue].nil? && options[:payload_object].respond_to?(:queue_name)
|
27
|
+
options[:queue] = options[:payload_object].queue_name
|
28
|
+
else
|
29
|
+
options[:queue] ||= Delayed::Worker.default_queue_name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_priority
|
34
|
+
queue_attribute = Delayed::Worker.queue_attributes[options[:queue]]
|
35
|
+
options[:priority] ||= (queue_attribute && queue_attribute[:priority]) || Delayed::Worker.default_priority
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_deprecation
|
39
|
+
if args.size > 0
|
40
|
+
warn '[DEPRECATION] Passing multiple arguments to `#enqueue` is deprecated. Pass a hash with :priority and :run_at.'
|
41
|
+
options[:priority] = args.first || options[:priority]
|
42
|
+
options[:run_at] = args[1]
|
43
|
+
end
|
44
|
+
|
45
|
+
# rubocop:disable GuardClause
|
46
|
+
unless options[:payload_object].respond_to?(:perform)
|
47
|
+
raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
|
48
|
+
end
|
49
|
+
# rubocop:enabled GuardClause
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.expand_path('../../../../spec/sample_jobs', __FILE__)
|
2
2
|
|
3
|
-
require 'active_support/core_ext'
|
3
|
+
require 'active_support/core_ext/numeric/time'
|
4
4
|
|
5
5
|
shared_examples_for 'a delayed_job backend' do
|
6
6
|
let(:worker) { Delayed::Worker.new }
|
@@ -113,6 +113,12 @@ shared_examples_for 'a delayed_job backend' do
|
|
113
113
|
job = described_class.enqueue M::ModuleJob.new
|
114
114
|
expect { job.invoke_job }.to change { M::ModuleJob.runs }.from(0).to(1)
|
115
115
|
end
|
116
|
+
|
117
|
+
it 'does not mutate the options hash' do
|
118
|
+
options = {:priority => 1}
|
119
|
+
described_class.enqueue SimpleJob.new, options
|
120
|
+
expect(options).to eq(:priority => 1)
|
121
|
+
end
|
116
122
|
end
|
117
123
|
|
118
124
|
context 'with delay_jobs = false' do
|
@@ -161,7 +167,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
161
167
|
job = described_class.enqueue(CallbackJob.new)
|
162
168
|
expect(job.payload_object).to receive(:perform).and_raise(RuntimeError.new('fail'))
|
163
169
|
|
164
|
-
expect { job.invoke_job }.to raise_error
|
170
|
+
expect { job.invoke_job }.to raise_error(RuntimeError)
|
165
171
|
expect(CallbackJob.messages).to eq(['enqueue', 'before', 'error: RuntimeError', 'after'])
|
166
172
|
end
|
167
173
|
|
@@ -279,6 +285,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
279
285
|
after do
|
280
286
|
Delayed::Worker.max_priority = nil
|
281
287
|
Delayed::Worker.min_priority = nil
|
288
|
+
Delayed::Worker.queue_attributes = {}
|
282
289
|
end
|
283
290
|
|
284
291
|
it 'fetches jobs ordered by priority' do
|
@@ -314,6 +321,18 @@ shared_examples_for 'a delayed_job backend' do
|
|
314
321
|
end
|
315
322
|
expect(described_class.reserve(worker)).to be_nil
|
316
323
|
end
|
324
|
+
|
325
|
+
it 'sets job priority based on queue_attributes configuration' do
|
326
|
+
Delayed::Worker.queue_attributes = {'job_tracking' => {:priority => 4}}
|
327
|
+
job = described_class.enqueue :payload_object => NamedQueueJob.new
|
328
|
+
expect(job.priority).to eq(4)
|
329
|
+
end
|
330
|
+
|
331
|
+
it 'sets job priority based on the passed in priority overrideing queue_attributes configuration' do
|
332
|
+
Delayed::Worker.queue_attributes = {'job_tracking' => {:priority => 4}}
|
333
|
+
job = described_class.enqueue :payload_object => NamedQueueJob.new, :priority => 10
|
334
|
+
expect(job.priority).to eq(10)
|
335
|
+
end
|
317
336
|
end
|
318
337
|
|
319
338
|
context 'clear_locks!' do
|
@@ -450,6 +469,34 @@ shared_examples_for 'a delayed_job backend' do
|
|
450
469
|
end
|
451
470
|
end
|
452
471
|
|
472
|
+
describe 'destroy_failed_jobs' do
|
473
|
+
context 'with a SimpleJob' do
|
474
|
+
before(:each) do
|
475
|
+
@job = described_class.enqueue SimpleJob.new
|
476
|
+
end
|
477
|
+
|
478
|
+
it 'is not defined' do
|
479
|
+
expect(@job.destroy_failed_jobs?).to be true
|
480
|
+
end
|
481
|
+
|
482
|
+
it 'uses the destroy failed jobs value on the payload when defined' do
|
483
|
+
expect(@job.payload_object).to receive(:destroy_failed_jobs?).and_return(false)
|
484
|
+
expect(@job.destroy_failed_jobs?).to be false
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
context 'with a job that raises DserializationError' do
|
489
|
+
before(:each) do
|
490
|
+
@job = described_class.new :handler => '--- !ruby/struct:GoingToRaiseArgError {}'
|
491
|
+
end
|
492
|
+
|
493
|
+
it 'falls back reasonably' do
|
494
|
+
expect(YAML).to receive(:load_dj).and_raise(ArgumentError)
|
495
|
+
expect(@job.destroy_failed_jobs?).to be true
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
453
500
|
describe 'yaml serialization' do
|
454
501
|
context 'when serializing jobs' do
|
455
502
|
it 'raises error ArgumentError for new records' do
|
@@ -476,7 +523,8 @@ shared_examples_for 'a delayed_job backend' do
|
|
476
523
|
it 'reloads changed attributes' do
|
477
524
|
story = Story.create(:text => 'hello')
|
478
525
|
job = story.delay.tell
|
479
|
-
story.
|
526
|
+
story.text = 'goodbye'
|
527
|
+
story.save!
|
480
528
|
expect(job.reload.payload_object.object.text).to eq('goodbye')
|
481
529
|
end
|
482
530
|
|
@@ -500,6 +548,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
500
548
|
Delayed::Worker.max_run_time = 1.second
|
501
549
|
job = Delayed::Job.create :payload_object => LongRunningJob.new
|
502
550
|
worker.run(job)
|
551
|
+
expect(job.error).to_not be_nil
|
503
552
|
expect(job.reload.last_error).to match(/expired/)
|
504
553
|
expect(job.reload.last_error).to match(/Delayed::Worker\.max_run_time is only 1 second/)
|
505
554
|
expect(job.attempts).to eq(1)
|
@@ -513,6 +562,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
513
562
|
it 'marks the job as failed' do
|
514
563
|
Delayed::Worker.destroy_failed_jobs = false
|
515
564
|
job = described_class.create! :handler => '--- !ruby/object:JobThatDoesNotExist {}'
|
565
|
+
expect_any_instance_of(described_class).to receive(:destroy_failed_jobs?).and_return(false)
|
516
566
|
worker.work_off
|
517
567
|
job.reload
|
518
568
|
expect(job).to be_failed
|
@@ -535,6 +585,7 @@ shared_examples_for 'a delayed_job backend' do
|
|
535
585
|
Delayed::Worker.max_attempts = 1
|
536
586
|
worker.run(@job)
|
537
587
|
@job.reload
|
588
|
+
expect(@job.error).to_not be_nil
|
538
589
|
expect(@job.last_error).to match(/did not work/)
|
539
590
|
expect(@job.attempts).to eq(1)
|
540
591
|
expect(@job).to be_failed
|
@@ -617,6 +668,10 @@ shared_examples_for 'a delayed_job backend' do
|
|
617
668
|
end
|
618
669
|
|
619
670
|
context 'and we want to destroy jobs' do
|
671
|
+
after do
|
672
|
+
Delayed::Worker.destroy_failed_jobs = true
|
673
|
+
end
|
674
|
+
|
620
675
|
it_behaves_like 'any failure more than Worker.max_attempts times'
|
621
676
|
|
622
677
|
it 'is destroyed if it failed more than Worker.max_attempts times' do
|
@@ -624,6 +679,13 @@ shared_examples_for 'a delayed_job backend' do
|
|
624
679
|
Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
|
625
680
|
end
|
626
681
|
|
682
|
+
it 'is destroyed if the job has destroy failed jobs set' do
|
683
|
+
Delayed::Worker.destroy_failed_jobs = false
|
684
|
+
expect(@job).to receive(:destroy_failed_jobs?).and_return(true)
|
685
|
+
expect(@job).to receive(:destroy)
|
686
|
+
Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
|
687
|
+
end
|
688
|
+
|
627
689
|
it 'is not destroyed if failed fewer than Worker.max_attempts times' do
|
628
690
|
expect(@job).not_to receive(:destroy)
|
629
691
|
(Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
|
@@ -641,15 +703,35 @@ shared_examples_for 'a delayed_job backend' do
|
|
641
703
|
|
642
704
|
it_behaves_like 'any failure more than Worker.max_attempts times'
|
643
705
|
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
706
|
+
context 'and destroy failed jobs is false' do
|
707
|
+
it 'is failed if it failed more than Worker.max_attempts times' do
|
708
|
+
expect(@job.reload).not_to be_failed
|
709
|
+
Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
|
710
|
+
expect(@job.reload).to be_failed
|
711
|
+
end
|
712
|
+
|
713
|
+
it 'is not failed if it failed fewer than Worker.max_attempts times' do
|
714
|
+
(Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
|
715
|
+
expect(@job.reload).not_to be_failed
|
716
|
+
end
|
648
717
|
end
|
649
718
|
|
650
|
-
|
651
|
-
|
652
|
-
|
719
|
+
context 'and destroy failed jobs for job is false' do
|
720
|
+
before do
|
721
|
+
Delayed::Worker.destroy_failed_jobs = true
|
722
|
+
end
|
723
|
+
|
724
|
+
it 'is failed if it failed more than Worker.max_attempts times' do
|
725
|
+
expect(@job).to receive(:destroy_failed_jobs?).and_return(false)
|
726
|
+
expect(@job.reload).not_to be_failed
|
727
|
+
Delayed::Worker.max_attempts.times { worker.reschedule(@job) }
|
728
|
+
expect(@job.reload).to be_failed
|
729
|
+
end
|
730
|
+
|
731
|
+
it 'is not failed if it failed fewer than Worker.max_attempts times' do
|
732
|
+
(Delayed::Worker.max_attempts - 1).times { worker.reschedule(@job) }
|
733
|
+
expect(@job.reload).not_to be_failed
|
734
|
+
end
|
653
735
|
end
|
654
736
|
end
|
655
737
|
end
|