sidekiq 2.15.2 → 2.16.0

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.

@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 08f16a145d63e3e2a04ec513885c855f1a0b12d7
4
+ data.tar.gz: 5596a8f758154a052f1b0e2eedfbe837bc801f2e
5
+ SHA512:
6
+ metadata.gz: 5867e076904084e5b63c9f7a3dbe9358173405b48c3903954d7cf75129d2afe7d92aac884da37fa492d4542e82233d867d9927c3467e4cd11496abc123a965cc
7
+ data.tar.gz: fe962090ddb489623737ccc89279f6cd2bf9e36d53dadcc9a7fdd9f41bca067e1743d04a98ce5afe49c83f3e467819695221ac8bd5338bbb5b2f89c9294d6afe
data/Changes.md CHANGED
@@ -1,3 +1,17 @@
1
+ 2.16.0
2
+ -----------
3
+
4
+ - Deprecate `Sidekiq::Client.registered_workers` and `Sidekiq::Client.registered_queues`
5
+ - Refactor Sidekiq::Client to be instance-based [#1279]
6
+ - Pass all Redis options to the Redis driver so Unix sockets
7
+ can be fully configured. [#1270, salimane]
8
+ - Allow sidekiq-web extensions to add locale paths so extensions
9
+ can be localized. [#1261, ondrejbartas]
10
+ - Capistrano 3 support [#1254, phallstrom]
11
+ - Use Ruby's `resolv-replace` to enable pure Ruby DNS lookups.
12
+ This ensures that any DNS resolution that takes place in worker
13
+ threads won't lock up the entire VM on MRI. [#1258]
14
+
1
15
  2.15.2
2
16
  -----------
3
17
 
@@ -13,9 +13,7 @@ If you're interested in helping or contributing to Sidekiq, here's
13
13
  some known areas which need improvement:
14
14
 
15
15
  * The Web UI and sidekiq.org website could always use more polish. If you have an eye for design and an idea for improvement, please open an issue and let us know.
16
- * The Sidekiq API has a serious issue: deleting elements and iterating
17
- at the same time is broken, see #866, #1060.
18
- * Make normal testing and inline testing dynamic: #1053
16
+ * The [Sidekiq issue tracker](https://github.com/mperham/sidekiq/issues) usually has a few ideas
19
17
  * Your brilliant idea which I haven't listed!
20
18
 
21
19
  It's always best to open an issue before investing a lot of time into a
@@ -3,6 +3,13 @@ Sidekiq Pro Changelog
3
3
 
4
4
  Please see http://sidekiq.org/pro for more details and how to buy.
5
5
 
6
+ 1.2.5
7
+ -----------
8
+
9
+ - Convert Batch UI to use Sidekiq 2.16's support for extension
10
+ localization.
11
+ - Pro now requires Sidekiq 2.16.0
12
+
6
13
  1.2.4
7
14
  -----------
8
15
 
@@ -78,7 +78,7 @@ module Sidekiq
78
78
  end
79
79
 
80
80
  def self.client_middleware
81
- @client_chain ||= Client.default_middleware
81
+ @client_chain ||= Middleware::Chain.new
82
82
  yield @client_chain if block_given?
83
83
  @client_chain
84
84
  end
@@ -257,6 +257,7 @@ module Sidekiq
257
257
 
258
258
  def initialize(name)
259
259
  @zset = name
260
+ @_size = size
260
261
  end
261
262
 
262
263
  def size
@@ -270,14 +271,14 @@ module Sidekiq
270
271
  end
271
272
 
272
273
  def each(&block)
273
- initial_size = size
274
- deleted_size = 0
274
+ initial_size = @_size
275
+ offset_size = 0
275
276
  page = -1
276
277
  page_size = 50
277
278
 
278
279
  loop do
279
- range_start = page * page_size + deleted_size
280
- range_end = page * page_size + deleted_size + (page_size - 1)
280
+ range_start = page * page_size + offset_size
281
+ range_end = page * page_size + offset_size + (page_size - 1)
281
282
  elements = Sidekiq.redis do |conn|
282
283
  conn.zrange @zset, range_start, range_end, :with_scores => true
283
284
  end
@@ -286,7 +287,7 @@ module Sidekiq
286
287
  elements.each do |element, score|
287
288
  block.call SortedEntry.new(self, score, element)
288
289
  end
289
- deleted_size = initial_size - size
290
+ offset_size = initial_size - @_size
290
291
  end
291
292
  end
292
293
 
@@ -320,13 +321,21 @@ module Sidekiq
320
321
  message = Sidekiq.load_json(element)
321
322
 
322
323
  if message["jid"] == jid
323
- Sidekiq.redis { |conn| conn.zrem(@zset, element) }
324
+ _, @_size = Sidekiq.redis do |conn|
325
+ conn.multi do
326
+ conn.zrem(@zset, element)
327
+ conn.zcard @zset
328
+ end
329
+ end
324
330
  end
325
331
  end
326
332
  elements_with_jid.count != 0
327
333
  else
328
- count = Sidekiq.redis do |conn|
329
- conn.zremrangebyscore(@zset, score, score)
334
+ count, @_size = Sidekiq.redis do |conn|
335
+ conn.multi do
336
+ conn.zremrangebyscore(@zset, score, score)
337
+ conn.zcard @zset
338
+ end
330
339
  end
331
340
  count != 0
332
341
  end
@@ -1,54 +1,5 @@
1
- Capistrano::Configuration.instance.load do
2
-
3
- _cset(:sidekiq_default_hooks) { true }
4
- _cset(:sidekiq_cmd) { "#{fetch(:bundle_cmd, "bundle")} exec sidekiq" }
5
- _cset(:sidekiqctl_cmd) { "#{fetch(:bundle_cmd, "bundle")} exec sidekiqctl" }
6
- _cset(:sidekiq_timeout) { 10 }
7
- _cset(:sidekiq_role) { :app }
8
- _cset(:sidekiq_pid) { "#{current_path}/tmp/pids/sidekiq.pid" }
9
- _cset(:sidekiq_processes) { 1 }
10
-
11
- if fetch(:sidekiq_default_hooks)
12
- before "deploy:update_code", "sidekiq:quiet"
13
- after "deploy:stop", "sidekiq:stop"
14
- after "deploy:start", "sidekiq:start"
15
- before "deploy:restart", "sidekiq:restart"
16
- end
17
-
18
- namespace :sidekiq do
19
- def for_each_process(&block)
20
- fetch(:sidekiq_processes).times do |idx|
21
- yield((idx == 0 ? "#{fetch(:sidekiq_pid)}" : "#{fetch(:sidekiq_pid)}-#{idx}"), idx)
22
- end
23
- end
24
-
25
- desc "Quiet sidekiq (stop accepting new work)"
26
- task :quiet, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
27
- for_each_process do |pid_file, idx|
28
- run "if [ -d #{current_path} ] && [ -f #{pid_file} ] && kill -0 `cat #{pid_file}`> /dev/null 2>&1; then cd #{current_path} && #{fetch(:sidekiqctl_cmd)} quiet #{pid_file} ; else echo 'Sidekiq is not running'; fi"
29
- end
30
- end
31
-
32
- desc "Stop sidekiq"
33
- task :stop, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
34
- for_each_process do |pid_file, idx|
35
- run "if [ -d #{current_path} ] && [ -f #{pid_file} ] && kill -0 `cat #{pid_file}`> /dev/null 2>&1; then cd #{current_path} && #{fetch(:sidekiqctl_cmd)} stop #{pid_file} #{fetch :sidekiq_timeout} ; else echo 'Sidekiq is not running'; fi"
36
- end
37
- end
38
-
39
- desc "Start sidekiq"
40
- task :start, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
41
- rails_env = fetch(:rails_env, "production")
42
- for_each_process do |pid_file, idx|
43
- run "cd #{current_path} ; nohup #{fetch(:sidekiq_cmd)} -e #{rails_env} -C #{current_path}/config/sidekiq.yml -i #{idx} -P #{pid_file} >> #{current_path}/log/sidekiq.log 2>&1 &", :pty => false
44
- end
45
- end
46
-
47
- desc "Restart sidekiq"
48
- task :restart, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
49
- stop
50
- start
51
- end
52
-
53
- end
1
+ if Gem::Version.new(Capistrano::VERSION).release >= Gem::Version.new('3.0.0')
2
+ load File.expand_path("../tasks/sidekiq.rake", __FILE__)
3
+ else
4
+ require_relative 'capistrano2'
54
5
  end
@@ -0,0 +1,54 @@
1
+ Capistrano::Configuration.instance.load do
2
+
3
+ _cset(:sidekiq_default_hooks) { true }
4
+ _cset(:sidekiq_cmd) { "#{fetch(:bundle_cmd, "bundle")} exec sidekiq" }
5
+ _cset(:sidekiqctl_cmd) { "#{fetch(:bundle_cmd, "bundle")} exec sidekiqctl" }
6
+ _cset(:sidekiq_timeout) { 10 }
7
+ _cset(:sidekiq_role) { :app }
8
+ _cset(:sidekiq_pid) { "#{current_path}/tmp/pids/sidekiq.pid" }
9
+ _cset(:sidekiq_processes) { 1 }
10
+
11
+ if fetch(:sidekiq_default_hooks)
12
+ before "deploy:update_code", "sidekiq:quiet"
13
+ after "deploy:stop", "sidekiq:stop"
14
+ after "deploy:start", "sidekiq:start"
15
+ before "deploy:restart", "sidekiq:restart"
16
+ end
17
+
18
+ namespace :sidekiq do
19
+ def for_each_process(&block)
20
+ fetch(:sidekiq_processes).times do |idx|
21
+ yield((idx == 0 ? "#{fetch(:sidekiq_pid)}" : "#{fetch(:sidekiq_pid)}-#{idx}"), idx)
22
+ end
23
+ end
24
+
25
+ desc "Quiet sidekiq (stop accepting new work)"
26
+ task :quiet, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
27
+ for_each_process do |pid_file, idx|
28
+ run "if [ -d #{current_path} ] && [ -f #{pid_file} ] && kill -0 `cat #{pid_file}`> /dev/null 2>&1; then cd #{current_path} && #{fetch(:sidekiqctl_cmd)} quiet #{pid_file} ; else echo 'Sidekiq is not running'; fi"
29
+ end
30
+ end
31
+
32
+ desc "Stop sidekiq"
33
+ task :stop, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
34
+ for_each_process do |pid_file, idx|
35
+ run "if [ -d #{current_path} ] && [ -f #{pid_file} ] && kill -0 `cat #{pid_file}`> /dev/null 2>&1; then cd #{current_path} && #{fetch(:sidekiqctl_cmd)} stop #{pid_file} #{fetch :sidekiq_timeout} ; else echo 'Sidekiq is not running'; fi"
36
+ end
37
+ end
38
+
39
+ desc "Start sidekiq"
40
+ task :start, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
41
+ rails_env = fetch(:rails_env, "production")
42
+ for_each_process do |pid_file, idx|
43
+ run "cd #{current_path} ; nohup #{fetch(:sidekiq_cmd)} -e #{rails_env} -C #{current_path}/config/sidekiq.yml -i #{idx} -P #{pid_file} >> #{current_path}/log/sidekiq.log 2>&1 &", :pty => false
44
+ end
45
+ end
46
+
47
+ desc "Restart sidekiq"
48
+ task :restart, :roles => lambda { fetch(:sidekiq_role) }, :on_no_matching_servers => :continue do
49
+ stop
50
+ start
51
+ end
52
+
53
+ end
54
+ end
@@ -1,5 +1,6 @@
1
1
  $stdout.sync = true
2
2
 
3
+ require 'resolv-replace'
3
4
  require 'yaml'
4
5
  require 'singleton'
5
6
  require 'optparse'
@@ -1,73 +1,104 @@
1
1
  require 'securerandom'
2
-
3
2
  require 'sidekiq/middleware/chain'
4
3
 
5
4
  module Sidekiq
6
5
  class Client
7
- class << self
8
6
 
9
- def default_middleware
10
- Middleware::Chain.new do
11
- end
7
+ ##
8
+ # Define client-side middleware:
9
+ #
10
+ # client = Sidekiq::Client.new
11
+ # client.middleware do |chain|
12
+ # chain.use MyClientMiddleware
13
+ # end
14
+ # client.push('class' => 'SomeWorker', 'args' => [1,2,3])
15
+ #
16
+ # All client instances default to the globally-defined
17
+ # Sidekiq.client_middleware but you can change as necessary.
18
+ #
19
+ def middleware(&block)
20
+ @chain ||= Sidekiq.client_middleware
21
+ if block_given?
22
+ @chain = @chain.dup
23
+ yield @chain
12
24
  end
25
+ @chain
26
+ end
27
+
28
+ ##
29
+ # The main method used to push a job to Redis. Accepts a number of options:
30
+ #
31
+ # queue - the named queue to use, default 'default'
32
+ # class - the worker class to call, required
33
+ # args - an array of simple arguments to the perform method, must be JSON-serializable
34
+ # retry - whether to retry this job if it fails, true or false, default true
35
+ # backtrace - whether to save any error backtrace, default false
36
+ #
37
+ # All options must be strings, not symbols. NB: because we are serializing to JSON, all
38
+ # symbols in 'args' will be converted to strings.
39
+ #
40
+ # Returns nil if not pushed to Redis or a unique Job ID if pushed.
41
+ #
42
+ # Example:
43
+ # push('queue' => 'my_queue', 'class' => MyWorker, 'args' => ['foo', 1, :bat => 'bar'])
44
+ #
45
+ def push(item)
46
+ normed = normalize_item(item)
47
+ payload = process_single(item['class'], normed)
48
+
49
+ pushed = false
50
+ pushed = raw_push([payload]) if payload
51
+ pushed ? payload['jid'] : nil
52
+ end
53
+
54
+ ##
55
+ # Push a large number of jobs to Redis. In practice this method is only
56
+ # useful if you are pushing tens of thousands of jobs or more. This method
57
+ # basically cuts down on the redis round trip latency.
58
+ #
59
+ # Takes the same arguments as #push except that args is expected to be
60
+ # an Array of Arrays. All other keys are duplicated for each job. Each job
61
+ # is run through the client middleware pipeline and each job gets its own Job ID
62
+ # as normal.
63
+ #
64
+ # Returns the number of jobs pushed or nil if the pushed failed. The number of jobs
65
+ # pushed can be less than the number given if the middleware stopped processing for one
66
+ # or more jobs.
67
+ def push_bulk(items)
68
+ normed = normalize_item(items)
69
+ payloads = items['args'].map do |args|
70
+ raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" if !args.is_a?(Array)
71
+ process_single(items['class'], normed.merge('args' => args, 'jid' => SecureRandom.hex(12), 'enqueued_at' => Time.now.to_f))
72
+ end.compact
73
+
74
+ pushed = false
75
+ pushed = raw_push(payloads) if !payloads.empty?
76
+ pushed ? payloads.size : nil
77
+ end
13
78
 
79
+ class << self
80
+ def default
81
+ @default ||= new
82
+ end
83
+
84
+ # deprecated
14
85
  def registered_workers
86
+ puts "registered_workers is deprecated, please use Sidekiq::Workers.new"
15
87
  Sidekiq.redis { |x| x.smembers('workers') }
16
88
  end
17
89
 
90
+ # deprecated
18
91
  def registered_queues
19
- Sidekiq.redis { |x| x.smembers('queues') }
92
+ puts "registered_queues is deprecated, please use Sidekiq::Queue.all"
93
+ Sidekiq::Queue.all.map(&:name)
20
94
  end
21
95
 
22
- ##
23
- # The main method used to push a job to Redis. Accepts a number of options:
24
- #
25
- # queue - the named queue to use, default 'default'
26
- # class - the worker class to call, required
27
- # args - an array of simple arguments to the perform method, must be JSON-serializable
28
- # retry - whether to retry this job if it fails, true or false, default true
29
- # backtrace - whether to save any error backtrace, default false
30
- #
31
- # All options must be strings, not symbols. NB: because we are serializing to JSON, all
32
- # symbols in 'args' will be converted to strings.
33
- #
34
- # Returns nil if not pushed to Redis or a unique Job ID if pushed.
35
- #
36
- # Example:
37
- # Sidekiq::Client.push('queue' => 'my_queue', 'class' => MyWorker, 'args' => ['foo', 1, :bat => 'bar'])
38
- #
39
96
  def push(item)
40
- normed = normalize_item(item)
41
- payload = process_single(item['class'], normed)
42
-
43
- pushed = false
44
- pushed = raw_push([payload]) if payload
45
- pushed ? payload['jid'] : nil
97
+ default.push(item)
46
98
  end
47
99
 
48
- ##
49
- # Push a large number of jobs to Redis. In practice this method is only
50
- # useful if you are pushing tens of thousands of jobs or more. This method
51
- # basically cuts down on the redis round trip latency.
52
- #
53
- # Takes the same arguments as Client.push except that args is expected to be
54
- # an Array of Arrays. All other keys are duplicated for each job. Each job
55
- # is run through the client middleware pipeline and each job gets its own Job ID
56
- # as normal.
57
- #
58
- # Returns the number of jobs pushed or nil if the pushed failed. The number of jobs
59
- # pushed can be less than the number given if the middleware stopped processing for one
60
- # or more jobs.
61
100
  def push_bulk(items)
62
- normed = normalize_item(items)
63
- payloads = items['args'].map do |args|
64
- raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" if !args.is_a?(Array)
65
- process_single(items['class'], normed.merge('args' => args, 'jid' => SecureRandom.hex(12), 'enqueued_at' => Time.now.to_f))
66
- end.compact
67
-
68
- pushed = false
69
- pushed = raw_push(payloads) if !payloads.empty?
70
- pushed ? payloads.size : nil
101
+ default.push_bulk(items)
71
102
  end
72
103
 
73
104
  # Resque compatibility helpers. Note all helpers
@@ -109,56 +140,56 @@ module Sidekiq
109
140
  def enqueue_in(interval, klass, *args)
110
141
  klass.perform_in(interval, *args)
111
142
  end
143
+ end
144
+
145
+ private
112
146
 
113
- private
114
-
115
- def raw_push(payloads)
116
- pushed = false
117
- Sidekiq.redis do |conn|
118
- if payloads.first['at']
119
- pushed = conn.zadd('schedule', payloads.map do |hash|
120
- at = hash.delete('at').to_s
121
- [at, Sidekiq.dump_json(hash)]
122
- end)
123
- else
124
- q = payloads.first['queue']
125
- to_push = payloads.map { |entry| Sidekiq.dump_json(entry) }
126
- _, pushed = conn.multi do
127
- conn.sadd('queues', q)
128
- conn.lpush("queue:#{q}", to_push)
129
- end
147
+ def raw_push(payloads)
148
+ pushed = false
149
+ Sidekiq.redis do |conn|
150
+ if payloads.first['at']
151
+ pushed = conn.zadd('schedule', payloads.map do |hash|
152
+ at = hash.delete('at').to_s
153
+ [at, Sidekiq.dump_json(hash)]
154
+ end)
155
+ else
156
+ q = payloads.first['queue']
157
+ to_push = payloads.map { |entry| Sidekiq.dump_json(entry) }
158
+ _, pushed = conn.multi do
159
+ conn.sadd('queues', q)
160
+ conn.lpush("queue:#{q}", to_push)
130
161
  end
131
162
  end
132
- pushed
133
163
  end
164
+ pushed
165
+ end
134
166
 
135
- def process_single(worker_class, item)
136
- queue = item['queue']
167
+ def process_single(worker_class, item)
168
+ queue = item['queue']
137
169
 
138
- Sidekiq.client_middleware.invoke(worker_class, item, queue) do
139
- item
140
- end
170
+ middleware.invoke(worker_class, item, queue) do
171
+ item
141
172
  end
173
+ end
142
174
 
143
- def normalize_item(item)
144
- raise(ArgumentError, "Message must be a Hash of the form: { 'class' => SomeWorker, 'args' => ['bob', 1, :foo => 'bar'] }") unless item.is_a?(Hash)
145
- raise(ArgumentError, "Message must include a class and set of arguments: #{item.inspect}") if !item['class'] || !item['args']
146
- raise(ArgumentError, "Message args must be an Array") unless item['args'].is_a?(Array)
147
- 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)
148
-
149
- if item['class'].is_a?(Class)
150
- raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item['class'].ancestors.inspect}") if !item['class'].respond_to?('get_sidekiq_options')
151
- normalized_item = item['class'].get_sidekiq_options.merge(item)
152
- normalized_item['class'] = normalized_item['class'].to_s
153
- else
154
- normalized_item = Sidekiq.default_worker_options.merge(item)
155
- end
156
-
157
- normalized_item['jid'] ||= SecureRandom.hex(12)
158
- normalized_item['enqueued_at'] ||= Time.now.to_f
159
- normalized_item
175
+ def normalize_item(item)
176
+ raise(ArgumentError, "Message must be a Hash of the form: { 'class' => SomeWorker, 'args' => ['bob', 1, :foo => 'bar'] }") unless item.is_a?(Hash)
177
+ raise(ArgumentError, "Message must include a class and set of arguments: #{item.inspect}") if !item['class'] || !item['args']
178
+ raise(ArgumentError, "Message args must be an Array") unless item['args'].is_a?(Array)
179
+ 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)
180
+
181
+ if item['class'].is_a?(Class)
182
+ raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item['class'].ancestors.inspect}") if !item['class'].respond_to?('get_sidekiq_options')
183
+ normalized_item = item['class'].get_sidekiq_options.merge(item)
184
+ normalized_item['class'] = normalized_item['class'].to_s
185
+ else
186
+ normalized_item = Sidekiq.default_worker_options.merge(item)
160
187
  end
161
188
 
189
+ normalized_item['jid'] ||= SecureRandom.hex(12)
190
+ normalized_item['enqueued_at'] ||= Time.now.to_f
191
+ normalized_item
162
192
  end
193
+
163
194
  end
164
195
  end