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 CHANGED
@@ -1,3 +1,9 @@
1
+ 2.6.3
2
+ -----------
3
+
4
+ - sidekiqctl exits with non-zero exit code upon error [jmazzi]
5
+ - better argument validation in Sidekiq::Client [karlfreeman]
6
+
1
7
  2.6.2
2
8
  -----------
3
9
 
@@ -11,28 +11,32 @@ class Sidekiqctl
11
11
  @pidfile = pidfile
12
12
  @timeout = timeout
13
13
 
14
- done 'No pidfile given' if !pidfile
15
- done "Pidfile #{pidfile} does not exist" if !File.exist?(pidfile)
16
- done 'Invalid pidfile content' if pid == 0
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(0)
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
@@ -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
@@ -99,7 +99,7 @@ module Sidekiq
99
99
  else
100
100
  _, pushed = conn.multi do
101
101
  conn.sadd('queues', normed['queue'])
102
- conn.rpush("queue:#{normed['queue']}", payload)
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)
@@ -12,11 +12,10 @@ module Sidekiq
12
12
 
13
13
  TIMEOUT = 1
14
14
 
15
- def initialize(mgr, queues, strict)
15
+ def initialize(mgr, options)
16
+ klass = Sidekiq.options[:fetch] || BasicFetch
16
17
  @mgr = mgr
17
- @strictly_ordered_queues = strict
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
- queue, msg = Sidekiq.redis { |conn| conn.blpop(*queues_cmd) }
34
+ work = @strategy.retrieve_work
36
35
 
37
- if msg
38
- @mgr.async.assign(msg, queue.gsub(/.*queue:/, ''))
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
- private
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
@@ -25,7 +25,7 @@ module Sidekiq
25
25
  @in_progress = {}
26
26
  @done = false
27
27
  @busy = []
28
- @fetcher = Fetcher.new(current_actor, options[:queues], !!options[:strict])
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(msg, queue)
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
- Sidekiq.redis do |conn|
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] = [msg, queue]
99
+ @in_progress[processor.object_id] = work
102
100
  @busy << processor
103
- processor.async.process(msg, queue)
101
+ processor.async.process(work)
104
102
  end
105
103
  end
106
104
  end
@@ -30,7 +30,9 @@ module Sidekiq
30
30
  @boss = boss
31
31
  end
32
32
 
33
- def process(msgstr, queue)
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)
@@ -1,3 +1,3 @@
1
1
  module Sidekiq
2
- VERSION = "2.6.2"
2
+ VERSION = "2.6.3"
3
3
  end
@@ -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]
@@ -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 :rpush, 1, ['queue:foo', String]
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 :rpush, 1, ['queue:foo', String]
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 :rpush, 1, ['queue:default', String]
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 :rpush, nil, ['queue:default', String]
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 :rpush, 1, ['queue:default', String]
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 :rpush, 1, ['queue:custom_queue', String]
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 :rpush, 1, ['queue:flimflam', String]
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) })
@@ -2,12 +2,30 @@ require 'helper'
2
2
  require 'sidekiq/fetch'
3
3
 
4
4
  class TestFetcher < MiniTest::Unit::TestCase
5
- describe 'Fetcher#queues_cmd' do
6
- describe 'when queues are strictly ordered' do
7
- it 'returns the unique ordered queues properly based on priority and order they were passed in' do
8
- fetcher = Sidekiq::Fetcher.new nil, %w[high medium low default], true
9
- assert_equal (%w[queue:high queue:medium queue:low queue:default] << 1), fetcher._send_(:queues_cmd)
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
@@ -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(msg, 'default')
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
 
@@ -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, 'default')
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, 'default')
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, 'default')
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, 'default')
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, 'default')
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, 'default')
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.2
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-03 00:00:00.000000000 Z
12
+ date: 2013-01-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis