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.

@@ -53,6 +53,7 @@ end
53
53
 
54
54
  begin
55
55
  require 'active_support/core_ext/hash/keys'
56
+ require 'active_support/core_ext/hash/deep_merge'
56
57
  rescue LoadError
57
58
  class Hash
58
59
  def stringify_keys
@@ -68,6 +69,22 @@ rescue LoadError
68
69
  end
69
70
  self
70
71
  end if !{}.respond_to?(:symbolize_keys)
72
+
73
+ def deep_merge(other_hash, &block)
74
+ dup.deep_merge!(other_hash, &block)
75
+ end if !{}.respond_to?(:deep_merge)
76
+
77
+ def deep_merge!(other_hash, &block)
78
+ other_hash.each_pair do |k,v|
79
+ tv = self[k]
80
+ if tv.is_a?(Hash) && v.is_a?(Hash)
81
+ self[k] = tv.deep_merge(v, &block)
82
+ else
83
+ self[k] = block && tv ? block.call(k, tv, v) : v
84
+ end
85
+ end
86
+ self
87
+ end if !{}.respond_to?(:deep_merge!)
71
88
  end
72
89
  end
73
90
 
@@ -64,6 +64,10 @@ module Sidekiq
64
64
  include Enumerable
65
65
  attr_reader :entries
66
66
 
67
+ def initialize_copy(copy)
68
+ copy.instance_variable_set(:@entries, entries.dup)
69
+ end
70
+
67
71
  def each(&block)
68
72
  entries.each(&block)
69
73
  end
@@ -46,7 +46,7 @@ module Sidekiq
46
46
  #
47
47
  # Sidekiq.configure_server do |config|
48
48
  # config.server_middleware do |chain|
49
- # chain.add Middleware::Server::RetryJobs, {:max_retries => 7}
49
+ # chain.add Middleware::Server::RetryJobs, :max_retries => 7
50
50
  # end
51
51
  # end
52
52
  class RetryJobs
@@ -106,6 +106,8 @@ module Sidekiq
106
106
  raise e
107
107
  end
108
108
 
109
+ private
110
+
109
111
  def retries_exhausted(worker, msg)
110
112
  logger.debug { "Dropping message after hitting the retry maximum: #{msg}" }
111
113
  if worker.respond_to?(:retries_exhausted)
@@ -140,7 +142,7 @@ module Sidekiq
140
142
  begin
141
143
  worker.sidekiq_retry_in_block.call(count)
142
144
  rescue Exception => e
143
- logger.error { "Failure scheduling retry using the defined `sidekiq_retry_in` in #{worker.class.name}, falling back to default: #{e.message}"}
145
+ handle_exception(e, { :context => "Failure scheduling retry using the defined `sidekiq_retry_in` in #{worker.class.name}, falling back to default" })
144
146
  nil
145
147
  end
146
148
  end
@@ -6,22 +6,28 @@ module Sidekiq
6
6
  class << self
7
7
 
8
8
  def create(options={})
9
- url = options[:url] || determine_redis_provider || 'redis://localhost:6379/0'
9
+ url = options[:url] || determine_redis_provider
10
+ if url
11
+ options[:url] = url
12
+ end
13
+
10
14
  # need a connection for Fetcher and Retry
11
15
  size = options[:size] || (Sidekiq.server? ? (Sidekiq.options[:concurrency] + 2) : 5)
12
16
  pool_timeout = options[:pool_timeout] || 1
13
17
 
14
- log_info(url, options)
18
+ log_info(options)
15
19
 
16
20
  ConnectionPool.new(:timeout => pool_timeout, :size => size) do
17
- build_client(url, options[:namespace], options[:driver] || 'ruby', options[:network_timeout])
21
+ build_client(options)
18
22
  end
19
23
  end
20
24
 
21
25
  private
22
26
 
23
- def build_client(url, namespace, driver, network_timeout)
24
- client = Redis.new client_opts(url, driver, network_timeout)
27
+ def build_client(options)
28
+ namespace = options[:namespace]
29
+
30
+ client = Redis.new client_opts(options)
25
31
  if namespace
26
32
  require 'redis/namespace'
27
33
  Redis::Namespace.new(namespace, :redis => client)
@@ -30,21 +36,27 @@ module Sidekiq
30
36
  end
31
37
  end
32
38
 
33
- def client_opts(url, driver, timeout)
34
- if timeout
35
- { :url => url, :driver => driver, :timeout => timeout }
36
- else
37
- { :url => url, :driver => driver }
39
+ def client_opts(options)
40
+ opts = options.dup
41
+ if opts[:namespace]
42
+ opts.delete(:namespace)
38
43
  end
44
+
45
+ if opts[:network_timeout]
46
+ opts[:timeout] = opts[:network_timeout]
47
+ opts.delete(:network_timeout)
48
+ end
49
+
50
+ opts[:driver] = opts[:driver] || 'ruby'
51
+
52
+ opts
39
53
  end
40
54
 
41
- def log_info(url, options)
42
- opts = options.dup
43
- opts.delete(:url)
55
+ def log_info(options)
44
56
  if Sidekiq.server?
45
- Sidekiq.logger.info("Booting Sidekiq #{Sidekiq::VERSION} using #{url} with options #{opts}")
57
+ Sidekiq.logger.info("Booting Sidekiq #{Sidekiq::VERSION} with redis options #{options}")
46
58
  else
47
- Sidekiq.logger.info("#{Sidekiq::NAME} client using #{url} with options #{opts}")
59
+ Sidekiq.logger.info("#{Sidekiq::NAME} client with redis options #{options}")
48
60
  end
49
61
  end
50
62
 
@@ -0,0 +1,92 @@
1
+ namespace :load do
2
+ task :defaults do
3
+
4
+ # If you need a special boot commands
5
+ #
6
+ # set :sidekiq_cmd, ->{ "bundle exec sidekiq" }
7
+ # set :sidekiqctl_cmd, ->{ "bundle exec sidekiqctl" }
8
+ set :sidekiq_cmd, ->{ }
9
+ set :sidekiqctl_cmd, ->{ }
10
+
11
+ # must be relative to Rails.root. If this changes, you'll need to manually
12
+ # stop the existing sidekiq process.
13
+ set :sidekiq_pid, ->{ "tmp/sidekiq.pid" }
14
+
15
+ # "-d -i INT -P PATH" are added automatically.
16
+ set :sidekiq_options, ->{ "-e #{fetch(:rails_env, 'production')} -C #{current_path}/config/sidekiq.yml -L #{current_path}/log/sidekiq.log" }
17
+
18
+ set :sidekiq_timeout, ->{ 10 }
19
+ set :sidekiq_role, ->{ :app }
20
+ set :sidekiq_processes, ->{ 1 }
21
+ end
22
+ end
23
+
24
+ namespace :sidekiq do
25
+ def for_each_process(&block)
26
+ fetch(:sidekiq_processes).times do |idx|
27
+ yield((idx == 0 ? "#{fetch(:sidekiq_pid)}" : "#{fetch(:sidekiq_pid)}-#{idx}"), idx)
28
+ end
29
+ end
30
+
31
+ desc "Quiet sidekiq (stop accepting new work)"
32
+ task :quiet do
33
+ on roles fetch(:sidekiq_role) do
34
+ within current_path do
35
+ for_each_process do |pid_file, idx|
36
+ if test "[ -f #{current_path}/#{pid_file} ]"
37
+ if fetch(:sidekiqctl_cmd)
38
+ execute fetch(:sidekiqctl_cmd), 'quiet', "#{current_path}/#{pid_file}"
39
+ else
40
+ execute :bundle, :exec, :sidekiqctl, 'quiet', "#{current_path}/#{pid_file}"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ desc "Stop sidekiq"
49
+ task :stop do
50
+ on roles fetch(:sidekiq_role) do
51
+ within current_path do
52
+ for_each_process do |pid_file, idx|
53
+ if test "[ -f #{current_path}/#{pid_file} ]"
54
+ if fetch(:sidekiqctl_cmd)
55
+ execute fetch(:sidekiqctl_cmd), 'stop', "#{current_path}/#{pid_file}", fetch(:sidekiq_timeout)
56
+ else
57
+ execute :bundle, :exec, :sidekiqctl, 'stop', "#{current_path}/#{pid_file}", fetch(:sidekiq_timeout)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ desc "Start sidekiq"
66
+ task :start do
67
+ on roles fetch(:sidekiq_role) do
68
+ rails_env = fetch(:rails_env, "production")
69
+ within current_path do
70
+ for_each_process do |pid_file, idx|
71
+ if fetch(:sidekiq_cmd)
72
+ execute fetch(:sidekiq_cmd), "-d -i #{idx} -P #{pid_file} #{fetch(:sidekiq_options)}"
73
+ else
74
+ execute :bundle, :exec, :sidekiq, "-d -i #{idx} -P #{pid_file} #{fetch(:sidekiq_options)}"
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ desc "Restart sidekiq"
82
+ task :restart do
83
+ invoke 'sidekiq:stop'
84
+ invoke 'sidekiq:start'
85
+ end
86
+
87
+ after 'deploy:starting', 'sidekiq:quiet'
88
+ after 'deploy:updated', 'sidekiq:stop'
89
+ after 'deploy:reverted', 'sidekiq:stop'
90
+ after 'deploy:published', 'sidekiq:start'
91
+
92
+ end
@@ -54,24 +54,22 @@ module Sidekiq
54
54
  class EmptyQueueError < RuntimeError; end
55
55
 
56
56
  class Client
57
- class << self
58
- alias_method :raw_push_real, :raw_push
57
+ alias_method :raw_push_real, :raw_push
59
58
 
60
- def raw_push(payloads)
61
- if Sidekiq::Testing.fake?
62
- payloads.each do |job|
63
- job['class'].constantize.jobs << Sidekiq.load_json(Sidekiq.dump_json(job))
64
- end
65
- true
66
- elsif Sidekiq::Testing.inline?
67
- payloads.each do |item|
68
- marshalled = Sidekiq.load_json(Sidekiq.dump_json(item))
69
- marshalled['class'].constantize.new.perform(*marshalled['args'])
70
- end
71
- true
72
- else
73
- raw_push_real(payloads)
59
+ def raw_push(payloads)
60
+ if Sidekiq::Testing.fake?
61
+ payloads.each do |job|
62
+ job['class'].constantize.jobs << Sidekiq.load_json(Sidekiq.dump_json(job))
63
+ end
64
+ true
65
+ elsif Sidekiq::Testing.inline?
66
+ payloads.each do |item|
67
+ marshalled = Sidekiq.load_json(Sidekiq.dump_json(item))
68
+ marshalled['class'].constantize.new.perform(*marshalled['args'])
74
69
  end
70
+ true
71
+ else
72
+ raw_push_real(payloads)
75
73
  end
76
74
  end
77
75
  end
@@ -1,3 +1,3 @@
1
1
  module Sidekiq
2
- VERSION = "2.15.2"
2
+ VERSION = "2.16.0"
3
3
  end
@@ -14,7 +14,7 @@ module Sidekiq
14
14
  set :root, File.expand_path(File.dirname(__FILE__) + "/../../web")
15
15
  set :public_folder, Proc.new { "#{root}/assets" }
16
16
  set :views, Proc.new { "#{root}/views" }
17
- set :locales, Proc.new { "#{root}/locales" }
17
+ set :locales, ["#{root}/locales"]
18
18
 
19
19
  helpers WebHelpers
20
20
 
@@ -5,8 +5,13 @@ module Sidekiq
5
5
  module WebHelpers
6
6
  def strings
7
7
  @@strings ||= begin
8
- Dir["#{settings.locales}/*.yml"].inject({}) do |memo, file|
9
- memo.merge(YAML.load(File.open(file)))
8
+ # Allow sidekiq-web extensions to add locale paths
9
+ # so extensions can be localized
10
+ settings.locales.each_with_object({}) do |path,global|
11
+ Dir["#{path}/*.yml"].each_with_object(global) do |file,hash|
12
+ strs = YAML.load(File.open(file))
13
+ hash.deep_merge!(strs)
14
+ end
10
15
  end
11
16
  end
12
17
  end
@@ -72,6 +77,10 @@ module Sidekiq
72
77
  Sidekiq.redis { |conn| conn.client.location }
73
78
  end
74
79
 
80
+ def redis_connection
81
+ Sidekiq.redis { |conn| conn.client.id }
82
+ end
83
+
75
84
  def namespace
76
85
  @@ns ||= Sidekiq.redis {|conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
77
86
  end
@@ -101,6 +110,16 @@ module Sidekiq
101
110
  [score.to_f, jid]
102
111
  end
103
112
 
113
+ SAFE_QPARAMS = %w(page poll)
114
+
115
+ # Merge options with current params, filter safe params, and stringify to query string
116
+ def qparams(options)
117
+ options = options.stringify_keys
118
+ params.merge(options).map { |key, value|
119
+ SAFE_QPARAMS.include?(key) ? "#{key}=#{value}" : next
120
+ }.join("&")
121
+ end
122
+
104
123
  def truncate(text, truncate_after_chars = 2000)
105
124
  truncate_after_chars && text.size > truncate_after_chars ? "#{text[0..truncate_after_chars]}..." : text
106
125
  end
@@ -0,0 +1,2 @@
1
+ en:
2
+ translated_text: 'Changed text from add locals'
@@ -46,7 +46,30 @@ class TestClient < Sidekiq::Test
46
46
  assert_raises ArgumentError do
47
47
  Sidekiq::Client.push('queue' => 'foo', 'class' => MyWorker, 'args' => 1)
48
48
  end
49
+ end
50
+
51
+ describe 'as instance' do
52
+ it 'can push' do
53
+ @redis.expect :lpush, 1, ['queue:default', Array]
54
+ client = Sidekiq::Client.new
55
+ jid = client.push('class' => 'Blah', 'args' => [1,2,3])
56
+ assert_equal 24, jid.size
57
+ end
49
58
 
59
+ it 'allows local middleware modification' do
60
+ @redis.expect :lpush, 1, ['queue:default', Array]
61
+ $called = false
62
+ mware = Class.new { def call(worker_klass,msg,q); $called = true; msg;end }
63
+ client = Sidekiq::Client.new
64
+ client.middleware do |chain|
65
+ chain.add mware
66
+ end
67
+ client.push('class' => 'Blah', 'args' => [1,2,3])
68
+
69
+ assert $called
70
+ assert client.middleware.exists?(mware)
71
+ refute Sidekiq.client_middleware.exists?(mware)
72
+ end
50
73
  end
51
74
 
52
75
  it 'pushes messages to redis' do
@@ -136,12 +159,7 @@ class TestClient < Sidekiq::Test
136
159
 
137
160
  it 'retrieves queues' do
138
161
  @redis.expect :smembers, ['bob'], ['queues']
139
- assert_equal ['bob'], Sidekiq::Client.registered_queues
140
- end
141
-
142
- it 'retrieves workers' do
143
- @redis.expect :smembers, ['bob'], ['workers']
144
- assert_equal ['bob'], Sidekiq::Client.registered_workers
162
+ assert_equal ['bob'], Sidekiq::Queue.all.map(&:name)
145
163
  end
146
164
  end
147
165
 
@@ -201,11 +219,11 @@ class TestClient < Sidekiq::Test
201
219
 
202
220
  describe 'item normalization' do
203
221
  it 'defaults retry to true' do
204
- assert_equal true, Sidekiq::Client.send(:normalize_item, 'class' => QueuedWorker, 'args' => [])['retry']
222
+ assert_equal true, Sidekiq::Client.new.send(:normalize_item, 'class' => QueuedWorker, 'args' => [])['retry']
205
223
  end
206
224
 
207
225
  it "does not normalize numeric retry's" do
208
- assert_equal 2, Sidekiq::Client.send(:normalize_item, 'class' => CWorker, 'args' => [])['retry']
226
+ assert_equal 2, Sidekiq::Client.new.send(:normalize_item, 'class' => CWorker, 'args' => [])['retry']
209
227
  end
210
228
  end
211
229
  end
@@ -22,18 +22,18 @@ class TestExtensions < Sidekiq::Test
22
22
  end
23
23
 
24
24
  it 'allows delayed execution of ActiveRecord class methods' do
25
- assert_equal [], Sidekiq::Client.registered_queues
25
+ assert_equal [], Sidekiq::Queue.all.map(&:name)
26
26
  assert_equal 0, Sidekiq.redis {|c| c.llen('queue:default') }
27
27
  MyModel.delay.long_class_method
28
- assert_equal ['default'], Sidekiq::Client.registered_queues
28
+ assert_equal ['default'], Sidekiq::Queue.all.map(&:name)
29
29
  assert_equal 1, Sidekiq.redis {|c| c.llen('queue:default') }
30
30
  end
31
31
 
32
32
  it 'uses and stringifies specified options' do
33
- assert_equal [], Sidekiq::Client.registered_queues
33
+ assert_equal [], Sidekiq::Queue.all.map(&:name)
34
34
  assert_equal 0, Sidekiq.redis {|c| c.llen('queue:notdefault') }
35
35
  MyModel.delay(queue: :notdefault).long_class_method
36
- assert_equal ['notdefault'], Sidekiq::Client.registered_queues
36
+ assert_equal ['notdefault'], Sidekiq::Queue.all.map(&:name)
37
37
  assert_equal 1, Sidekiq.redis {|c| c.llen('queue:notdefault') }
38
38
  end
39
39
 
@@ -56,10 +56,10 @@ class TestExtensions < Sidekiq::Test
56
56
  end
57
57
 
58
58
  it 'allows delayed delivery of ActionMailer mails' do
59
- assert_equal [], Sidekiq::Client.registered_queues
59
+ assert_equal [], Sidekiq::Queue.all.map(&:name)
60
60
  assert_equal 0, Sidekiq.redis {|c| c.llen('queue:default') }
61
61
  UserMailer.delay.greetings(1, 2)
62
- assert_equal ['default'], Sidekiq::Client.registered_queues
62
+ assert_equal ['default'], Sidekiq::Queue.all.map(&:name)
63
63
  assert_equal 1, Sidekiq.redis {|c| c.llen('queue:default') }
64
64
  end
65
65
 
@@ -39,6 +39,20 @@ class TestRedisConnection < Sidekiq::Test
39
39
  end
40
40
  end
41
41
 
42
+ describe "socket path" do
43
+ it "uses a given :path" do
44
+ pool = Sidekiq::RedisConnection.create(:path => "/var/run/redis.sock")
45
+ assert_equal "unix", pool.checkout.client.scheme
46
+ assert_equal "redis:///var/run/redis.sock/0", pool.checkout.client.id
47
+ end
48
+
49
+ it "uses a given :path and :db" do
50
+ pool = Sidekiq::RedisConnection.create(:path => "/var/run/redis.sock", :db => 8)
51
+ assert_equal "unix", pool.checkout.client.scheme
52
+ assert_equal "redis:///var/run/redis.sock/8", pool.checkout.client.id
53
+ end
54
+ end
55
+
42
56
  describe "pool_timeout" do
43
57
  it "uses a given :timeout over the default of 1" do
44
58
  pool = Sidekiq::RedisConnection.create(:pool_timeout => 5)