delayed_job 4.0.6 → 4.1.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Gem Version](https://badge.fury.io/rb/delayed_job.
|
4
|
-
[![Build Status](https://travis-ci.org/collectiveidea/delayed_job.
|
5
|
-
[![
|
6
|
-
[![
|
7
|
-
[![Coverage Status](https://coveralls.io/repos/collectiveidea/delayed_job/badge.png?branch=master)][coveralls]
|
8
|
+
[![Gem Version](https://badge.fury.io/rb/delayed_job.svg)][gem]
|
9
|
+
[![Build Status](https://travis-ci.org/collectiveidea/delayed_job.svg?branch=master)][travis]
|
10
|
+
[![Code Climate](https://codeclimate.com/github/collectiveidea/delayed_job.svg)][codeclimate]
|
11
|
+
[![Coverage Status](https://coveralls.io/repos/collectiveidea/delayed_job/badge.svg?branch=master)][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
|