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.
- checksums.yaml +7 -0
- data/Changes.md +14 -0
- data/Contributing.md +1 -3
- data/Pro-Changes.md +7 -0
- data/lib/sidekiq.rb +1 -1
- data/lib/sidekiq/api.rb +17 -8
- data/lib/sidekiq/capistrano.rb +4 -53
- data/lib/sidekiq/capistrano2.rb +54 -0
- data/lib/sidekiq/cli.rb +1 -0
- data/lib/sidekiq/client.rb +122 -91
- data/lib/sidekiq/core_ext.rb +17 -0
- data/lib/sidekiq/middleware/chain.rb +4 -0
- data/lib/sidekiq/middleware/server/retry_jobs.rb +4 -2
- data/lib/sidekiq/redis_connection.rb +27 -15
- data/lib/sidekiq/tasks/sidekiq.rake +92 -0
- data/lib/sidekiq/testing.rb +14 -16
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web.rb +1 -1
- data/lib/sidekiq/web_helpers.rb +21 -2
- data/test/fixtures/en.yml +2 -0
- data/test/test_client.rb +26 -8
- data/test/test_extensions.rb +6 -6
- data/test/test_redis_connection.rb +14 -0
- data/test/test_retry.rb +5 -5
- data/test/test_web.rb +16 -0
- data/web/assets/stylesheets/application.css +2 -11
- data/web/locales/en.yml +0 -1
- data/web/views/_nav.erb +1 -1
- data/web/views/_paging.erb +5 -5
- data/web/views/_poll.erb +5 -7
- data/web/views/_summary.erb +8 -8
- data/web/views/layout.erb +1 -1
- data/web/views/retries.erb +1 -1
- data/web/views/retry.erb +1 -1
- metadata +31 -51
data/lib/sidekiq/core_ext.rb
CHANGED
@@ -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
|
|
@@ -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,
|
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
|
-
|
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
|
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(
|
18
|
+
log_info(options)
|
15
19
|
|
16
20
|
ConnectionPool.new(:timeout => pool_timeout, :size => size) do
|
17
|
-
build_client(
|
21
|
+
build_client(options)
|
18
22
|
end
|
19
23
|
end
|
20
24
|
|
21
25
|
private
|
22
26
|
|
23
|
-
def build_client(
|
24
|
-
|
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(
|
34
|
-
|
35
|
-
|
36
|
-
|
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(
|
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}
|
57
|
+
Sidekiq.logger.info("Booting Sidekiq #{Sidekiq::VERSION} with redis options #{options}")
|
46
58
|
else
|
47
|
-
Sidekiq.logger.info("#{Sidekiq::NAME} client
|
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
|
data/lib/sidekiq/testing.rb
CHANGED
@@ -54,24 +54,22 @@ module Sidekiq
|
|
54
54
|
class EmptyQueueError < RuntimeError; end
|
55
55
|
|
56
56
|
class Client
|
57
|
-
|
58
|
-
alias_method :raw_push_real, :raw_push
|
57
|
+
alias_method :raw_push_real, :raw_push
|
59
58
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web.rb
CHANGED
@@ -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,
|
17
|
+
set :locales, ["#{root}/locales"]
|
18
18
|
|
19
19
|
helpers WebHelpers
|
20
20
|
|
data/lib/sidekiq/web_helpers.rb
CHANGED
@@ -5,8 +5,13 @@ module Sidekiq
|
|
5
5
|
module WebHelpers
|
6
6
|
def strings
|
7
7
|
@@strings ||= begin
|
8
|
-
|
9
|
-
|
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
|
data/test/test_client.rb
CHANGED
@@ -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::
|
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
|
data/test/test_extensions.rb
CHANGED
@@ -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::
|
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::
|
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::
|
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::
|
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::
|
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::
|
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)
|