delayed_job 4.1.2 → 4.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +8 -3
- data/delayed_job.gemspec +2 -2
- data/lib/delayed/backend/base.rb +4 -7
- data/lib/delayed/backend/job_preparer.rb +3 -1
- data/lib/delayed/backend/shared_spec.rb +6 -0
- data/lib/delayed/command.rb +6 -1
- data/lib/delayed/exceptions.rb +1 -1
- data/lib/delayed/lifecycle.rb +1 -1
- data/lib/delayed/message_sending.rb +27 -25
- data/lib/delayed/performable_method.rb +2 -0
- data/lib/delayed/psych_ext.rb +1 -1
- data/lib/delayed/worker.rb +13 -3
- data/lib/delayed_job.rb +2 -1
- 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 +1 -2
- data/spec/autoloaded/struct.rb +1 -2
- data/spec/message_sending_spec.rb +9 -5
- data/spec/performable_method_spec.rb +1 -2
- data/spec/worker_spec.rb +9 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e2fe748c9a94faf96700fa8f87466b6fe7fd079
|
4
|
+
data.tar.gz: 21d55a1f49084db05123ad5573711ef7a52f7c29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cadfae1e2a548b451dc336f033ae671bd005c5b17fa311c5af3a306851b6d5642fcc88bee79ebbc9826032e9dccdf5f58eb0315de63e3357106987f496009f69
|
7
|
+
data.tar.gz: 2a07509ecd676d248ca983a3ba87062cf1b11a8bdc67f0cd63ab91a2c975e8f840b323bba8ae9596aa15db2435702484763acd7688adaf39b492910fffead165
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,14 @@
|
|
1
|
+
4.1.3 - 2017-05-26
|
2
|
+
=================
|
3
|
+
* Don't mutate the options hash (#877)
|
4
|
+
* Log an error message when a deserialization error occurs (#894)
|
5
|
+
* Adding the queue name to the log output (#917)
|
6
|
+
* Don't include ClassMethods with MessageSending (#924)
|
7
|
+
* Fix YAML deserialization error if original object is soft-deleted (#947)
|
8
|
+
* Add support for Rails 5.1 (#982)
|
9
|
+
|
1
10
|
4.1.2 - 2016-05-16
|
11
|
+
==================
|
2
12
|
* Added Delayed::Worker.queue_attributes
|
3
13
|
* Limit what we require in ActiveSupport
|
4
14
|
* Fix pid file creation when there is no tmp directory
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
**If you're viewing this at https://github.com/collectiveidea/delayed_job,
|
2
2
|
you're reading the documentation for the master branch.
|
3
3
|
[View documentation for the latest release
|
4
|
-
(4.1.
|
4
|
+
(4.1.3).](https://github.com/collectiveidea/delayed_job/tree/v4.1.3)**
|
5
5
|
|
6
6
|
Delayed::Job
|
7
7
|
============
|
@@ -37,8 +37,7 @@ multitude of core tasks. Amongst those tasks are:
|
|
37
37
|
|
38
38
|
Installation
|
39
39
|
============
|
40
|
-
delayed_job 3.0.0 only supports Rails 3.0+.
|
41
|
-
branch](https://github.com/collectiveidea/delayed_job/tree/v2.0) for Rails 2.
|
40
|
+
delayed_job 3.0.0 only supports Rails 3.0+.
|
42
41
|
|
43
42
|
delayed_job supports multiple backends for storing the job queue. [See the wiki
|
44
43
|
for other backends](https://github.com/collectiveidea/delayed_job/wiki/Backends).
|
@@ -220,6 +219,12 @@ Configured queue priorities can be overriden by passing priority to the delay me
|
|
220
219
|
object.delay(:queue => 'high_priority', priority: 0).method
|
221
220
|
```
|
222
221
|
|
222
|
+
You can start processes to only work certain queues with the `queue` and `queues`
|
223
|
+
options defined below. Processes started without specifying a queue will run jobs
|
224
|
+
from **any** queue. To effectively have a process that runs jobs where a queue is not
|
225
|
+
specified, set a default queue name with `Delayed::Worker.default_queue_name` and
|
226
|
+
have the processes run that queue.
|
227
|
+
|
223
228
|
Running Jobs
|
224
229
|
============
|
225
230
|
`script/delayed_job` can be used to manage a background process which will
|
data/delayed_job.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
|
-
spec.add_dependency 'activesupport', ['>= 3.0', '< 5.
|
4
|
+
spec.add_dependency 'activesupport', ['>= 3.0', '< 5.2']
|
5
5
|
spec.authors = ['Brandon Keepers', 'Brian Ryckbost', 'Chris Gaffney', 'David Genord II', 'Erik Michaels-Ober', 'Matt Griffin', 'Steve Richert', 'Tobias Lütke']
|
6
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.'
|
7
7
|
spec.email = ['brian@collectiveidea.com']
|
@@ -13,5 +13,5 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.require_paths = ['lib']
|
14
14
|
spec.summary = 'Database-backed asynchronous priority queue system -- Extracted from Shopify'
|
15
15
|
spec.test_files = Dir.glob('spec/**/*')
|
16
|
-
spec.version = '4.1.
|
16
|
+
spec.version = '4.1.3'
|
17
17
|
end
|
data/lib/delayed/backend/base.rb
CHANGED
@@ -30,16 +30,13 @@ module Delayed
|
|
30
30
|
end
|
31
31
|
|
32
32
|
# Allow the backend to attempt recovery from reserve errors
|
33
|
-
def recover_from(_error)
|
34
|
-
end
|
33
|
+
def recover_from(_error); end
|
35
34
|
|
36
35
|
# Hook method that is called before a new worker is forked
|
37
|
-
def before_fork
|
38
|
-
end
|
36
|
+
def before_fork; end
|
39
37
|
|
40
38
|
# Hook method that is called after a new worker is forked
|
41
|
-
def after_fork
|
42
|
-
end
|
39
|
+
def after_fork; end
|
43
40
|
|
44
41
|
def work_off(num = 100)
|
45
42
|
warn '[DEPRECATION] `Delayed::Job.work_off` is deprecated. Use `Delayed::Worker.new.work_off instead.'
|
@@ -101,7 +98,7 @@ module Delayed
|
|
101
98
|
def hook(name, *args)
|
102
99
|
if payload_object.respond_to?(name)
|
103
100
|
method = payload_object.method(name)
|
104
|
-
method.arity
|
101
|
+
method.arity.zero? ? method.call : method.call(self, *args)
|
105
102
|
end
|
106
103
|
rescue DeserializationError # rubocop:disable HandleExceptions
|
107
104
|
end
|
@@ -4,7 +4,7 @@ module Delayed
|
|
4
4
|
attr_reader :options, :args
|
5
5
|
|
6
6
|
def initialize(*args)
|
7
|
-
@options = args.extract_options
|
7
|
+
@options = args.extract_options!.dup
|
8
8
|
@args = args
|
9
9
|
end
|
10
10
|
|
@@ -42,9 +42,11 @@ module Delayed
|
|
42
42
|
options[:run_at] = args[1]
|
43
43
|
end
|
44
44
|
|
45
|
+
# rubocop:disable GuardClause
|
45
46
|
unless options[:payload_object].respond_to?(:perform)
|
46
47
|
raise ArgumentError, 'Cannot enqueue items which do not respond to perform'
|
47
48
|
end
|
49
|
+
# rubocop:enabled GuardClause
|
48
50
|
end
|
49
51
|
end
|
50
52
|
end
|
@@ -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
|
data/lib/delayed/command.rb
CHANGED
@@ -77,8 +77,11 @@ module Delayed
|
|
77
77
|
opt.on('--exit-on-complete', 'Exit when no more jobs are available to run. This will exit if all jobs are scheduled to run in the future.') do
|
78
78
|
@options[:exit_on_complete] = true
|
79
79
|
end
|
80
|
+
opt.on('--daemon-options a, b, c', Array, 'options to be passed through to daemons gem') do |daemon_options|
|
81
|
+
@daemon_options = daemon_options
|
82
|
+
end
|
80
83
|
end
|
81
|
-
@args = opts.parse!(args)
|
84
|
+
@args = opts.parse!(args) + (@daemon_options || [])
|
82
85
|
end
|
83
86
|
|
84
87
|
def daemonize # rubocop:disable PerceivedComplexity
|
@@ -88,11 +91,13 @@ module Delayed
|
|
88
91
|
if worker_pools
|
89
92
|
setup_pools
|
90
93
|
elsif @options[:identifier]
|
94
|
+
# rubocop:disable GuardClause
|
91
95
|
if worker_count > 1
|
92
96
|
raise ArgumentError, 'Cannot specify both --number-of-workers and --identifier'
|
93
97
|
else
|
94
98
|
run_process("delayed_job.#{@options[:identifier]}", @options)
|
95
99
|
end
|
100
|
+
# rubocop:enable GuardClause
|
96
101
|
else
|
97
102
|
worker_count.times do |worker_index|
|
98
103
|
process_name = worker_count == 1 ? 'delayed_job' : "delayed_job.#{worker_index}"
|
data/lib/delayed/exceptions.rb
CHANGED
data/lib/delayed/lifecycle.rb
CHANGED
@@ -6,9 +6,11 @@ module Delayed
|
|
6
6
|
@options = options
|
7
7
|
end
|
8
8
|
|
9
|
+
# rubocop:disable MethodMissing
|
9
10
|
def method_missing(method, *args)
|
10
11
|
Job.enqueue({:payload_object => @payload_class.new(@target, method.to_sym, args)}.merge(@options))
|
11
12
|
end
|
13
|
+
# rubocop:enable MethodMissing
|
12
14
|
end
|
13
15
|
|
14
16
|
module MessageSending
|
@@ -26,36 +28,36 @@ module Delayed
|
|
26
28
|
warn '[DEPRECATION] `object.send_at(time, :method)` is deprecated. Use `object.delay(:run_at => time).method'
|
27
29
|
__delay__(:run_at => time).__send__(method, *args)
|
28
30
|
end
|
31
|
+
end
|
29
32
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
33
|
+
module MessageSendingClassMethods
|
34
|
+
def handle_asynchronously(method, opts = {}) # rubocop:disable PerceivedComplexity
|
35
|
+
aliased_method = method.to_s.sub(/([?!=])$/, '')
|
36
|
+
punctuation = $1 # rubocop:disable PerlBackrefs
|
37
|
+
with_method = "#{aliased_method}_with_delay#{punctuation}"
|
38
|
+
without_method = "#{aliased_method}_without_delay#{punctuation}"
|
39
|
+
define_method(with_method) do |*args|
|
40
|
+
curr_opts = opts.clone
|
41
|
+
curr_opts.each_key do |key|
|
42
|
+
next unless (val = curr_opts[key]).is_a?(Proc)
|
43
|
+
curr_opts[key] = if val.arity == 1
|
44
|
+
val.call(self)
|
45
|
+
else
|
46
|
+
val.call
|
45
47
|
end
|
46
|
-
delay(curr_opts).__send__(without_method, *args)
|
47
48
|
end
|
49
|
+
delay(curr_opts).__send__(without_method, *args)
|
50
|
+
end
|
48
51
|
|
49
|
-
|
50
|
-
|
52
|
+
alias_method without_method, method
|
53
|
+
alias_method method, with_method
|
51
54
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end
|
55
|
+
if public_method_defined?(without_method)
|
56
|
+
public method
|
57
|
+
elsif protected_method_defined?(without_method)
|
58
|
+
protected method
|
59
|
+
elsif private_method_defined?(without_method)
|
60
|
+
private method
|
59
61
|
end
|
60
62
|
end
|
61
63
|
end
|
@@ -30,9 +30,11 @@ module Delayed
|
|
30
30
|
object.method(sym)
|
31
31
|
end
|
32
32
|
|
33
|
+
# rubocop:disable MethodMissing
|
33
34
|
def method_missing(symbol, *args)
|
34
35
|
object.send(symbol, *args)
|
35
36
|
end
|
37
|
+
# rubocop:enable MethodMissing
|
36
38
|
|
37
39
|
def respond_to?(symbol, include_private = false)
|
38
40
|
super || object.respond_to?(symbol, include_private)
|
data/lib/delayed/psych_ext.rb
CHANGED
@@ -37,7 +37,7 @@ module Delayed
|
|
37
37
|
klass = result.class
|
38
38
|
id = result[klass.primary_key]
|
39
39
|
begin
|
40
|
-
klass.find(id)
|
40
|
+
klass.unscoped.find(id)
|
41
41
|
rescue ActiveRecord::RecordNotFound => error # rubocop:disable BlockNesting
|
42
42
|
raise Delayed::DeserializationError, "ActiveRecord::RecordNotFound, class: #{klass}, primary key: #{id} (#{error.message})"
|
43
43
|
end
|
data/lib/delayed/worker.rb
CHANGED
@@ -233,6 +233,8 @@ module Delayed
|
|
233
233
|
job_say job, format('COMPLETED after %.4f', runtime)
|
234
234
|
return true # did work
|
235
235
|
rescue DeserializationError => error
|
236
|
+
job_say job, "FAILED permanently with #{error.class.name}: #{error.message}", 'error'
|
237
|
+
|
236
238
|
job.error = error
|
237
239
|
failed(job)
|
238
240
|
rescue Exception => error # rubocop:disable RescueException
|
@@ -268,7 +270,7 @@ module Delayed
|
|
268
270
|
end
|
269
271
|
|
270
272
|
def job_say(job, text, level = default_log_level)
|
271
|
-
text = "Job #{job.name} (id=#{job.id}) #{text}"
|
273
|
+
text = "Job #{job.name} (id=#{job.id})#{say_queue(job.queue)} #{text}"
|
272
274
|
say text, level
|
273
275
|
end
|
274
276
|
|
@@ -293,6 +295,10 @@ module Delayed
|
|
293
295
|
|
294
296
|
protected
|
295
297
|
|
298
|
+
def say_queue(queue)
|
299
|
+
" (queue=#{queue})" if queue
|
300
|
+
end
|
301
|
+
|
296
302
|
def handle_failed_job(job, error)
|
297
303
|
job.error = error
|
298
304
|
job_say job, "FAILED (#{job.attempts} prior attempts) with #{error.class.name}: #{error.message}", 'error'
|
@@ -320,8 +326,12 @@ module Delayed
|
|
320
326
|
|
321
327
|
def reload!
|
322
328
|
return unless self.class.reload_app?
|
323
|
-
|
324
|
-
|
329
|
+
if defined?(ActiveSupport::Reloader)
|
330
|
+
Rails.application.reloader.reload!
|
331
|
+
else
|
332
|
+
ActionDispatch::Reloader.cleanup!
|
333
|
+
ActionDispatch::Reloader.prepare!
|
334
|
+
end
|
325
335
|
end
|
326
336
|
end
|
327
337
|
end
|
data/lib/delayed_job.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'active_support'
|
1
2
|
require 'delayed/compatibility'
|
2
3
|
require 'delayed/exceptions'
|
3
4
|
require 'delayed/message_sending'
|
@@ -19,4 +20,4 @@ require 'delayed/deserialization_error'
|
|
19
20
|
require 'delayed/railtie' if defined?(Rails::Railtie)
|
20
21
|
|
21
22
|
Object.send(:include, Delayed::MessageSending)
|
22
|
-
Module.send(:include, Delayed::
|
23
|
+
Module.send(:include, Delayed::MessageSendingClassMethods)
|
@@ -6,6 +6,6 @@ class DelayedJobGenerator < Rails::Generators::Base
|
|
6
6
|
|
7
7
|
def create_executable_file
|
8
8
|
template 'script', "#{Delayed::Compatibility.executable_prefix}/delayed_job"
|
9
|
-
chmod "#{Delayed::Compatibility.executable_prefix}/delayed_job",
|
9
|
+
chmod "#{Delayed::Compatibility.executable_prefix}/delayed_job", 0o755
|
10
10
|
end
|
11
11
|
end
|
data/spec/autoloaded/clazz.rb
CHANGED
data/spec/autoloaded/struct.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
describe Delayed::MessageSending do
|
4
|
+
it 'does not include ClassMethods along with MessageSending' do
|
5
|
+
expect { ClassMethods }.to raise_error(NameError)
|
6
|
+
expect(defined?(String::ClassMethods)).to eq(nil)
|
7
|
+
end
|
8
|
+
|
4
9
|
describe 'handle_asynchronously' do
|
5
10
|
class Story
|
6
11
|
def tell!(_arg); end
|
@@ -19,7 +24,7 @@ describe Delayed::MessageSending do
|
|
19
24
|
expect(job.payload_object.class).to eq(Delayed::PerformableMethod)
|
20
25
|
expect(job.payload_object.method_name).to eq(:tell_without_delay!)
|
21
26
|
expect(job.payload_object.args).to eq([1])
|
22
|
-
end.to
|
27
|
+
end.to(change { Delayed::Job.count })
|
23
28
|
end
|
24
29
|
|
25
30
|
describe 'with options' do
|
@@ -42,8 +47,7 @@ describe Delayed::MessageSending do
|
|
42
47
|
describe 'using a proc with parameters' do
|
43
48
|
class Yarn
|
44
49
|
attr_accessor :importance
|
45
|
-
def spin
|
46
|
-
end
|
50
|
+
def spin; end
|
47
51
|
handle_asynchronously :spin, :priority => proc { |y| y.importance }
|
48
52
|
end
|
49
53
|
|
@@ -107,7 +111,7 @@ describe Delayed::MessageSending do
|
|
107
111
|
expect do
|
108
112
|
fairy_tail.delay.tell
|
109
113
|
end.to change(fairy_tail, :happy_ending).from(nil).to(true)
|
110
|
-
end.not_to
|
114
|
+
end.not_to(change { Delayed::Job.count })
|
111
115
|
end
|
112
116
|
|
113
117
|
it 'does delay the job when delay_jobs is true' do
|
@@ -137,7 +141,7 @@ describe Delayed::MessageSending do
|
|
137
141
|
expect do
|
138
142
|
fairy_tail.delay.tell
|
139
143
|
end.to change(fairy_tail, :happy_ending).from(nil).to(true)
|
140
|
-
end.not_to
|
144
|
+
end.not_to(change { Delayed::Job.count })
|
141
145
|
end
|
142
146
|
end
|
143
147
|
end
|
@@ -30,8 +30,7 @@ describe Delayed::PerformableMethod do
|
|
30
30
|
|
31
31
|
it 'does not raise NoMethodError if target method is private' do
|
32
32
|
clazz = Class.new do
|
33
|
-
def private_method
|
34
|
-
end
|
33
|
+
def private_method; end
|
35
34
|
private :private_method
|
36
35
|
end
|
37
36
|
expect { Delayed::PerformableMethod.new(clazz.new, :private_method, []) }.not_to raise_error
|
data/spec/worker_spec.rb
CHANGED
@@ -24,15 +24,23 @@ describe Delayed::Worker do
|
|
24
24
|
describe 'job_say' do
|
25
25
|
before do
|
26
26
|
@worker = Delayed::Worker.new
|
27
|
-
@job = double('job', :id => 123, :name => 'ExampleJob')
|
27
|
+
@job = double('job', :id => 123, :name => 'ExampleJob', :queue => nil)
|
28
28
|
end
|
29
29
|
|
30
30
|
it 'logs with job name and id' do
|
31
|
+
expect(@job).to receive(:queue)
|
31
32
|
expect(@worker).to receive(:say).
|
32
33
|
with('Job ExampleJob (id=123) message', Delayed::Worker.default_log_level)
|
33
34
|
@worker.job_say(@job, 'message')
|
34
35
|
end
|
35
36
|
|
37
|
+
it 'logs with job name, queue and id' do
|
38
|
+
expect(@job).to receive(:queue).and_return('test')
|
39
|
+
expect(@worker).to receive(:say).
|
40
|
+
with('Job ExampleJob (id=123) (queue=test) message', Delayed::Worker.default_log_level)
|
41
|
+
@worker.job_say(@job, 'message')
|
42
|
+
end
|
43
|
+
|
36
44
|
it 'has a configurable default log level' do
|
37
45
|
Delayed::Worker.default_log_level = 'error'
|
38
46
|
|
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.1.
|
4
|
+
version: 4.1.3
|
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:
|
18
|
+
date: 2017-05-26 00:00:00.000000000 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: activesupport
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
version: '3.0'
|
27
27
|
- - "<"
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '5.
|
29
|
+
version: '5.2'
|
30
30
|
type: :runtime
|
31
31
|
prerelease: false
|
32
32
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -36,7 +36,7 @@ dependencies:
|
|
36
36
|
version: '3.0'
|
37
37
|
- - "<"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '5.
|
39
|
+
version: '5.2'
|
40
40
|
description: Delayed_job (or DJ) encapsulates the common pattern of asynchronously
|
41
41
|
executing longer tasks in the background. It is a direct extraction from Shopify
|
42
42
|
where the job table is responsible for a multitude of core tasks.
|
@@ -119,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
119
|
version: '0'
|
120
120
|
requirements: []
|
121
121
|
rubyforge_project:
|
122
|
-
rubygems_version: 2.
|
122
|
+
rubygems_version: 2.6.11
|
123
123
|
signing_key:
|
124
124
|
specification_version: 4
|
125
125
|
summary: Database-backed asynchronous priority queue system -- Extracted from Shopify
|