sidekiq 2.6.2 → 2.6.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sidekiq might be problematic. Click here for more details.
- data/Changes.md +6 -0
- data/bin/sidekiqctl +11 -7
- data/lib/sidekiq/cli.rb +4 -0
- data/lib/sidekiq/client.rb +2 -1
- data/lib/sidekiq/fetch.rb +35 -10
- data/lib/sidekiq/manager.rb +5 -7
- data/lib/sidekiq/processor.rb +5 -1
- data/lib/sidekiq/version.rb +1 -1
- data/test/test_cli.rb +5 -0
- data/test/test_client.rb +14 -7
- data/test/test_fetch.rb +24 -6
- data/test/test_middleware.rb +2 -2
- data/test/test_processor.rb +10 -6
- metadata +2 -2
data/Changes.md
CHANGED
data/bin/sidekiqctl
CHANGED
@@ -11,28 +11,32 @@ class Sidekiqctl
|
|
11
11
|
@pidfile = pidfile
|
12
12
|
@timeout = timeout
|
13
13
|
|
14
|
-
done
|
15
|
-
done
|
16
|
-
done
|
14
|
+
done('No pidfile given', :error) if !pidfile
|
15
|
+
done("Pidfile #{pidfile} does not exist", :error) if !File.exist?(pidfile)
|
16
|
+
done('Invalid pidfile content', :error) if pid == 0
|
17
17
|
|
18
18
|
fetch_process
|
19
19
|
|
20
20
|
begin
|
21
21
|
send(stage)
|
22
22
|
rescue NoMethodError
|
23
|
-
done 'Invalid control command'
|
23
|
+
done 'Invalid control command', :error
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
27
|
def fetch_process
|
28
28
|
Process.getpgid(pid)
|
29
29
|
rescue Errno::ESRCH
|
30
|
-
done "Process doesn't exist"
|
30
|
+
done "Process doesn't exist", :error
|
31
31
|
end
|
32
32
|
|
33
|
-
def done(msg)
|
33
|
+
def done(msg, error = nil)
|
34
34
|
puts msg
|
35
|
-
exit(
|
35
|
+
exit(exit_signal(error))
|
36
|
+
end
|
37
|
+
|
38
|
+
def exit_signal(error)
|
39
|
+
(error == :error) ? 1 : 0
|
36
40
|
end
|
37
41
|
|
38
42
|
def pid
|
data/lib/sidekiq/cli.rb
CHANGED
@@ -192,6 +192,10 @@ module Sidekiq
|
|
192
192
|
opts[:require] = arg
|
193
193
|
end
|
194
194
|
|
195
|
+
o.on '-i', '--index INT', "unique process index on this machine" do |arg|
|
196
|
+
opts[:index] = Integer(arg)
|
197
|
+
end
|
198
|
+
|
195
199
|
o.on '-c', '--concurrency INT', "processor threads to use" do |arg|
|
196
200
|
opts[:concurrency] = Integer(arg)
|
197
201
|
end
|
data/lib/sidekiq/client.rb
CHANGED
@@ -99,7 +99,7 @@ module Sidekiq
|
|
99
99
|
else
|
100
100
|
_, pushed = conn.multi do
|
101
101
|
conn.sadd('queues', normed['queue'])
|
102
|
-
conn.
|
102
|
+
conn.lpush("queue:#{normed['queue']}", payload)
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
@@ -118,6 +118,7 @@ module Sidekiq
|
|
118
118
|
def self.normalize_item(item)
|
119
119
|
raise(ArgumentError, "Message must be a Hash of the form: { 'class' => SomeWorker, 'args' => ['bob', 1, :foo => 'bar'] }") unless item.is_a?(Hash)
|
120
120
|
raise(ArgumentError, "Message must include a class and set of arguments: #{item.inspect}") if !item['class'] || !item['args']
|
121
|
+
raise(ArgumentError, "Message args must be an Array") unless item['args'].is_a?(Array)
|
121
122
|
raise(ArgumentError, "Message class must be either a Class or String representation of the class name") unless item['class'].is_a?(Class) || item['class'].is_a?(String)
|
122
123
|
|
123
124
|
if item['class'].is_a?(Class)
|
data/lib/sidekiq/fetch.rb
CHANGED
@@ -12,11 +12,10 @@ module Sidekiq
|
|
12
12
|
|
13
13
|
TIMEOUT = 1
|
14
14
|
|
15
|
-
def initialize(mgr,
|
15
|
+
def initialize(mgr, options)
|
16
|
+
klass = Sidekiq.options[:fetch] || BasicFetch
|
16
17
|
@mgr = mgr
|
17
|
-
@
|
18
|
-
@queues = queues.map { |q| "queue:#{q}" }
|
19
|
-
@unique_queues = @queues.uniq
|
18
|
+
@strategy = klass.new(options)
|
20
19
|
end
|
21
20
|
|
22
21
|
# Fetching is straightforward: the Manager makes a fetch
|
@@ -32,10 +31,10 @@ module Sidekiq
|
|
32
31
|
return if Sidekiq::Fetcher.done?
|
33
32
|
|
34
33
|
begin
|
35
|
-
|
34
|
+
work = @strategy.retrieve_work
|
36
35
|
|
37
|
-
if
|
38
|
-
@mgr.async.assign(
|
36
|
+
if work
|
37
|
+
@mgr.async.assign(work)
|
39
38
|
else
|
40
39
|
after(0) { fetch }
|
41
40
|
end
|
@@ -58,8 +57,34 @@ module Sidekiq
|
|
58
57
|
def self.done?
|
59
58
|
@done
|
60
59
|
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class BasicFetch
|
63
|
+
def initialize(options)
|
64
|
+
@strictly_ordered_queues = !!options[:strict]
|
65
|
+
@queues = options[:queues].map { |q| "queue:#{q}" }
|
66
|
+
@unique_queues = @queues.uniq
|
67
|
+
end
|
68
|
+
|
69
|
+
def retrieve_work
|
70
|
+
UnitOfWork.new(*Sidekiq.redis { |conn| conn.brpop(*queues_cmd) })
|
71
|
+
end
|
61
72
|
|
62
|
-
|
73
|
+
UnitOfWork = Struct.new(:queue, :message) do
|
74
|
+
def acknowledge
|
75
|
+
# nothing to do
|
76
|
+
end
|
77
|
+
|
78
|
+
def queue_name
|
79
|
+
queue.gsub(/.*queue:/, '')
|
80
|
+
end
|
81
|
+
|
82
|
+
def requeue
|
83
|
+
Sidekiq.redis do |conn|
|
84
|
+
conn.rpush(queue, message)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
63
88
|
|
64
89
|
# Creating the Redis#blpop command takes into account any
|
65
90
|
# configured queue weights. By default Redis#blpop returns
|
@@ -67,10 +92,10 @@ module Sidekiq
|
|
67
92
|
# recreate the queue command each time we invoke Redis#blpop
|
68
93
|
# to honor weights and avoid queue starvation.
|
69
94
|
def queues_cmd
|
70
|
-
return @unique_queues.dup << TIMEOUT if @strictly_ordered_queues
|
95
|
+
return @unique_queues.dup << Sidekiq::Fetcher::TIMEOUT if @strictly_ordered_queues
|
71
96
|
queues = @queues.sample(@unique_queues.size).uniq
|
72
97
|
queues.concat(@unique_queues - queues)
|
73
|
-
queues << TIMEOUT
|
98
|
+
queues << Sidekiq::Fetcher::TIMEOUT
|
74
99
|
end
|
75
100
|
end
|
76
101
|
end
|
data/lib/sidekiq/manager.rb
CHANGED
@@ -25,7 +25,7 @@ module Sidekiq
|
|
25
25
|
@in_progress = {}
|
26
26
|
@done = false
|
27
27
|
@busy = []
|
28
|
-
@fetcher = Fetcher.new(current_actor, options
|
28
|
+
@fetcher = Fetcher.new(current_actor, options)
|
29
29
|
@ready = @count.times.map { Processor.new_link(current_actor) }
|
30
30
|
procline(options[:tag] ? "#{options[:tag]} " : '')
|
31
31
|
end
|
@@ -86,21 +86,19 @@ module Sidekiq
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
def assign(
|
89
|
+
def assign(work)
|
90
90
|
watchdog("Manager#assign died") do
|
91
91
|
if stopped?
|
92
92
|
# Race condition between Manager#stop if Fetcher
|
93
93
|
# is blocked on redis and gets a message after
|
94
94
|
# all the ready Processors have been stopped.
|
95
95
|
# Push the message back to redis.
|
96
|
-
|
97
|
-
conn.lpush("queue:#{queue}", msg)
|
98
|
-
end
|
96
|
+
work.requeue
|
99
97
|
else
|
100
98
|
processor = @ready.pop
|
101
|
-
@in_progress[processor.object_id] =
|
99
|
+
@in_progress[processor.object_id] = work
|
102
100
|
@busy << processor
|
103
|
-
processor.async.process(
|
101
|
+
processor.async.process(work)
|
104
102
|
end
|
105
103
|
end
|
106
104
|
end
|
data/lib/sidekiq/processor.rb
CHANGED
@@ -30,7 +30,9 @@ module Sidekiq
|
|
30
30
|
@boss = boss
|
31
31
|
end
|
32
32
|
|
33
|
-
def process(
|
33
|
+
def process(work)
|
34
|
+
msgstr = work.message
|
35
|
+
queue = work.queue_name
|
34
36
|
defer do
|
35
37
|
begin
|
36
38
|
msg = Sidekiq.load_json(msgstr)
|
@@ -46,6 +48,8 @@ module Sidekiq
|
|
46
48
|
rescue Exception => ex
|
47
49
|
handle_exception(ex, msg || { :message => msgstr })
|
48
50
|
raise
|
51
|
+
ensure
|
52
|
+
work.acknowledge
|
49
53
|
end
|
50
54
|
end
|
51
55
|
@boss.async.processor_done(current_actor)
|
data/lib/sidekiq/version.rb
CHANGED
data/test/test_cli.rb
CHANGED
@@ -40,6 +40,11 @@ class TestCli < MiniTest::Unit::TestCase
|
|
40
40
|
assert_equal ['foo'], Sidekiq.options[:queues]
|
41
41
|
end
|
42
42
|
|
43
|
+
it 'accepts a process index' do
|
44
|
+
@cli.parse(['sidekiq', '-i', '7', '-r', './test/fake_env.rb'])
|
45
|
+
assert_equal 7, Sidekiq.options[:index]
|
46
|
+
end
|
47
|
+
|
43
48
|
it 'sets strictly ordered queues if weights are not present' do
|
44
49
|
@cli.parse(['sidekiq', '-q', 'foo,bar', '-r', './test/fake_env.rb'])
|
45
50
|
assert_equal true, !!Sidekiq.options[:strict]
|
data/test/test_client.rb
CHANGED
@@ -39,10 +39,14 @@ class TestClient < MiniTest::Unit::TestCase
|
|
39
39
|
Sidekiq::Client.push('queue' => 'foo', 'class' => 42, 'args' => [1, 2])
|
40
40
|
end
|
41
41
|
|
42
|
+
assert_raises ArgumentError do
|
43
|
+
Sidekiq::Client.push('queue' => 'foo', 'class' => MyWorker, 'args' => 1)
|
44
|
+
end
|
45
|
+
|
42
46
|
end
|
43
47
|
|
44
48
|
it 'pushes messages to redis' do
|
45
|
-
@redis.expect :
|
49
|
+
@redis.expect :lpush, 1, ['queue:foo', String]
|
46
50
|
pushed = Sidekiq::Client.push('queue' => 'foo', 'class' => MyWorker, 'args' => [1, 2])
|
47
51
|
assert pushed
|
48
52
|
assert_equal 24, pushed.size
|
@@ -50,7 +54,7 @@ class TestClient < MiniTest::Unit::TestCase
|
|
50
54
|
end
|
51
55
|
|
52
56
|
it 'pushes messages to redis using a String class' do
|
53
|
-
@redis.expect :
|
57
|
+
@redis.expect :lpush, 1, ['queue:foo', String]
|
54
58
|
pushed = Sidekiq::Client.push('queue' => 'foo', 'class' => 'MyWorker', 'args' => [1, 2])
|
55
59
|
assert pushed
|
56
60
|
assert_equal 24, pushed.size
|
@@ -66,28 +70,28 @@ class TestClient < MiniTest::Unit::TestCase
|
|
66
70
|
end
|
67
71
|
|
68
72
|
it 'handles perform_async' do
|
69
|
-
@redis.expect :
|
73
|
+
@redis.expect :lpush, 1, ['queue:default', String]
|
70
74
|
pushed = MyWorker.perform_async(1, 2)
|
71
75
|
assert pushed
|
72
76
|
@redis.verify
|
73
77
|
end
|
74
78
|
|
75
79
|
it 'handles perform_async on failure' do
|
76
|
-
@redis.expect :
|
80
|
+
@redis.expect :lpush, nil, ['queue:default', String]
|
77
81
|
pushed = MyWorker.perform_async(1, 2)
|
78
82
|
refute pushed
|
79
83
|
@redis.verify
|
80
84
|
end
|
81
85
|
|
82
86
|
it 'enqueues messages to redis' do
|
83
|
-
@redis.expect :
|
87
|
+
@redis.expect :lpush, 1, ['queue:default', String]
|
84
88
|
pushed = Sidekiq::Client.enqueue(MyWorker, 1, 2)
|
85
89
|
assert pushed
|
86
90
|
@redis.verify
|
87
91
|
end
|
88
92
|
|
89
93
|
it 'enqueues messages to redis' do
|
90
|
-
@redis.expect :
|
94
|
+
@redis.expect :lpush, 1, ['queue:custom_queue', String]
|
91
95
|
pushed = Sidekiq::Client.enqueue_to(:custom_queue, MyWorker, 1, 2)
|
92
96
|
assert pushed
|
93
97
|
@redis.verify
|
@@ -99,7 +103,7 @@ class TestClient < MiniTest::Unit::TestCase
|
|
99
103
|
end
|
100
104
|
|
101
105
|
it 'enqueues to the named queue' do
|
102
|
-
@redis.expect :
|
106
|
+
@redis.expect :lpush, 1, ['queue:flimflam', String]
|
103
107
|
pushed = QueuedWorker.perform_async(1, 2)
|
104
108
|
assert pushed
|
105
109
|
@redis.verify
|
@@ -117,6 +121,9 @@ class TestClient < MiniTest::Unit::TestCase
|
|
117
121
|
end
|
118
122
|
|
119
123
|
describe 'bulk' do
|
124
|
+
after do
|
125
|
+
Sidekiq::Queue.new.clear
|
126
|
+
end
|
120
127
|
it 'can push a large set of jobs at once' do
|
121
128
|
a = Time.now
|
122
129
|
count = Sidekiq::Client.push_bulk('class' => QueuedWorker, 'args' => (1..1_000).to_a.map { |x| Array(x) })
|
data/test/test_fetch.rb
CHANGED
@@ -2,12 +2,30 @@ require 'helper'
|
|
2
2
|
require 'sidekiq/fetch'
|
3
3
|
|
4
4
|
class TestFetcher < MiniTest::Unit::TestCase
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
5
|
+
|
6
|
+
def setup
|
7
|
+
Sidekiq.redis do |conn|
|
8
|
+
conn.del('queue:basic')
|
9
|
+
conn.rpush('queue:basic', 'msg')
|
11
10
|
end
|
12
11
|
end
|
12
|
+
|
13
|
+
def test_basic_fetch_retrieve
|
14
|
+
fetch = Sidekiq::BasicFetch.new(:queues => ['basic', 'bar'])
|
15
|
+
uow = fetch.retrieve_work
|
16
|
+
refute_nil uow
|
17
|
+
assert_equal 'basic', uow.queue_name
|
18
|
+
assert_equal 'msg', uow.message
|
19
|
+
q = Sidekiq::Queue.new('basic')
|
20
|
+
assert_equal 0, q.size
|
21
|
+
uow.requeue
|
22
|
+
assert_equal 1, q.size
|
23
|
+
assert_nil uow.acknowledge
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_basic_fetch_strict_retrieve
|
27
|
+
fetch = Sidekiq::BasicFetch.new(:queues => ['basic', 'bar', 'bar'], :strict => true)
|
28
|
+
cmd = fetch.queues_cmd
|
29
|
+
assert_equal cmd, ['queue:basic', 'queue:bar', 1]
|
30
|
+
end
|
13
31
|
end
|
data/test/test_middleware.rb
CHANGED
@@ -41,7 +41,7 @@ class TestMiddleware < MiniTest::Unit::TestCase
|
|
41
41
|
def call(*args)
|
42
42
|
end
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
class AnotherCustomMiddleware
|
46
46
|
def initialize(name, recorder)
|
47
47
|
@name = name
|
@@ -83,7 +83,7 @@ class TestMiddleware < MiniTest::Unit::TestCase
|
|
83
83
|
actor = MiniTest::Mock.new
|
84
84
|
actor.expect(:processor_done, nil, [processor])
|
85
85
|
boss.expect(:async, actor, [])
|
86
|
-
processor.process(
|
86
|
+
processor.process(Sidekiq::BasicFetch::UnitOfWork.new('queue:default', msg))
|
87
87
|
assert_equal %w(2 before 3 before 0 before work_performed 0 after 3 after 2 after), $recorder.flatten
|
88
88
|
end
|
89
89
|
|
data/test/test_processor.rb
CHANGED
@@ -23,12 +23,16 @@ class TestProcessor < MiniTest::Unit::TestCase
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
def work(msg, queue='queue:default')
|
27
|
+
Sidekiq::BasicFetch::UnitOfWork.new(queue, msg)
|
28
|
+
end
|
29
|
+
|
26
30
|
it 'processes as expected' do
|
27
31
|
msg = Sidekiq.dump_json({ 'class' => MockWorker.to_s, 'args' => ['myarg'] })
|
28
32
|
actor = MiniTest::Mock.new
|
29
33
|
actor.expect(:processor_done, nil, [@processor])
|
30
34
|
@boss.expect(:async, actor, [])
|
31
|
-
@processor.process(msg
|
35
|
+
@processor.process(work(msg))
|
32
36
|
@boss.verify
|
33
37
|
assert_equal 1, $invokes
|
34
38
|
end
|
@@ -36,7 +40,7 @@ class TestProcessor < MiniTest::Unit::TestCase
|
|
36
40
|
it 'passes exceptions to ExceptionHandler' do
|
37
41
|
msg = Sidekiq.dump_json({ 'class' => MockWorker.to_s, 'args' => ['boom'] })
|
38
42
|
begin
|
39
|
-
@processor.process(msg
|
43
|
+
@processor.process(work(msg))
|
40
44
|
flunk "Expected #process to raise exception"
|
41
45
|
rescue TestException
|
42
46
|
end
|
@@ -49,7 +53,7 @@ class TestProcessor < MiniTest::Unit::TestCase
|
|
49
53
|
re_raise = false
|
50
54
|
|
51
55
|
begin
|
52
|
-
@processor.process(msg
|
56
|
+
@processor.process(work(msg))
|
53
57
|
rescue TestException
|
54
58
|
re_raise = true
|
55
59
|
end
|
@@ -64,7 +68,7 @@ class TestProcessor < MiniTest::Unit::TestCase
|
|
64
68
|
actor = MiniTest::Mock.new
|
65
69
|
actor.expect(:processor_done, nil, [processor])
|
66
70
|
@boss.expect(:async, actor, [])
|
67
|
-
processor.process(msgstr
|
71
|
+
processor.process(work(msgstr))
|
68
72
|
assert_equal [['myarg']], msg['args']
|
69
73
|
end
|
70
74
|
|
@@ -79,7 +83,7 @@ class TestProcessor < MiniTest::Unit::TestCase
|
|
79
83
|
actor = MiniTest::Mock.new
|
80
84
|
actor.expect(:processor_done, nil, [@processor])
|
81
85
|
@boss.expect(:async, actor, [])
|
82
|
-
@processor.process(msg
|
86
|
+
@processor.process(work(msg))
|
83
87
|
end
|
84
88
|
|
85
89
|
it 'increments processed stat' do
|
@@ -100,7 +104,7 @@ class TestProcessor < MiniTest::Unit::TestCase
|
|
100
104
|
def failed_job
|
101
105
|
msg = Sidekiq.dump_json({ 'class' => MockWorker.to_s, 'args' => ['boom'] })
|
102
106
|
begin
|
103
|
-
@processor.process(msg
|
107
|
+
@processor.process(work(msg))
|
104
108
|
rescue TestException
|
105
109
|
end
|
106
110
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.6.
|
4
|
+
version: 2.6.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|