sidekiq 2.17.8 → 3.0.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 +4 -4
- data/3.0-Upgrade.md +63 -0
- data/Changes.md +66 -3
- data/Contributing.md +1 -3
- data/Pro-Changes.md +18 -0
- data/README.md +2 -2
- data/bin/sidekiqctl +19 -6
- data/lib/sidekiq.rb +53 -11
- data/lib/sidekiq/actor.rb +1 -0
- data/lib/sidekiq/api.rb +145 -58
- data/lib/sidekiq/cli.rb +22 -18
- data/lib/sidekiq/client.rb +44 -14
- data/lib/sidekiq/core_ext.rb +5 -8
- data/lib/sidekiq/exception_handler.rb +19 -28
- data/lib/sidekiq/fetch.rb +3 -3
- data/lib/sidekiq/launcher.rb +30 -3
- data/lib/sidekiq/logging.rb +2 -2
- data/lib/sidekiq/manager.rb +19 -16
- data/lib/sidekiq/middleware/chain.rb +1 -1
- data/lib/sidekiq/middleware/i18n.rb +1 -1
- data/lib/sidekiq/middleware/server/retry_jobs.rb +23 -7
- data/lib/sidekiq/processor.rb +36 -54
- data/lib/sidekiq/redis_connection.rb +1 -3
- data/lib/sidekiq/util.rb +4 -4
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web.rb +57 -8
- data/lib/sidekiq/web_helpers.rb +6 -15
- data/lib/sidekiq/worker.rb +3 -1
- data/sidekiq.gemspec +5 -5
- data/test/test_api.rb +59 -19
- data/test/test_cli.rb +1 -1
- data/test/test_client.rb +44 -5
- data/test/test_exception_handler.rb +4 -87
- data/test/test_middleware.rb +3 -2
- data/test/test_redis_connection.rb +0 -6
- data/test/test_retry.rb +13 -68
- data/test/test_scheduled.rb +1 -1
- data/test/test_scheduling.rb +5 -0
- data/test/test_sidekiq.rb +18 -0
- data/test/test_web.rb +98 -58
- data/web/assets/stylesheets/application.css +5 -0
- data/web/locales/cs.yml +68 -0
- data/web/locales/da.yml +9 -1
- data/web/locales/de.yml +15 -7
- data/web/locales/el.yml +68 -0
- data/web/locales/en.yml +8 -3
- data/web/locales/es.yml +9 -1
- data/web/locales/fr.yml +34 -26
- data/web/locales/it.yml +26 -18
- data/web/locales/ja.yml +8 -2
- data/web/locales/ko.yml +0 -2
- data/web/locales/nl.yml +8 -3
- data/web/locales/no.yml +9 -3
- data/web/locales/pl.yml +0 -1
- data/web/locales/pt-br.yml +11 -4
- data/web/locales/pt.yml +8 -1
- data/web/locales/ru.yml +29 -22
- data/web/locales/sv.yml +68 -0
- data/web/locales/zh-tw.yml +68 -0
- data/web/views/_job_info.erb +8 -2
- data/web/views/_summary.erb +13 -7
- data/web/views/busy.erb +55 -0
- data/web/views/dead.erb +30 -0
- data/web/views/layout.erb +1 -0
- data/web/views/morgue.erb +66 -0
- metadata +29 -30
- data/config.ru +0 -18
- data/lib/sidekiq/capistrano.rb +0 -5
- data/lib/sidekiq/capistrano2.rb +0 -54
- data/lib/sidekiq/tasks/sidekiq.rake +0 -119
- data/lib/sidekiq/yaml_patch.rb +0 -21
- data/test/test_util.rb +0 -18
- data/web/views/_workers.erb +0 -22
- data/web/views/workers.erb +0 -16
data/test/test_cli.rb
CHANGED
@@ -109,7 +109,7 @@ class TestCli < Sidekiq::Test
|
|
109
109
|
after do
|
110
110
|
Sidekiq.logger = @old_logger
|
111
111
|
Sidekiq.options.delete(:logfile)
|
112
|
-
File.unlink @tmp_log_path if File.
|
112
|
+
File.unlink @tmp_log_path if File.exist?(@tmp_log_path)
|
113
113
|
end
|
114
114
|
|
115
115
|
it 'sets the logfile path' do
|
data/test/test_client.rb
CHANGED
@@ -20,10 +20,12 @@ class TestClient < Sidekiq::Test
|
|
20
20
|
def @redis.with; yield self; end
|
21
21
|
def @redis.exec; true; end
|
22
22
|
Sidekiq.instance_variable_set(:@redis, @redis)
|
23
|
+
Sidekiq::Client.instance_variable_set(:@default, nil)
|
23
24
|
end
|
24
25
|
|
25
26
|
after do
|
26
27
|
Sidekiq.instance_variable_set(:@redis, REDIS)
|
28
|
+
Sidekiq::Client.instance_variable_set(:@default, nil)
|
27
29
|
end
|
28
30
|
|
29
31
|
it 'raises ArgumentError with invalid params' do
|
@@ -59,7 +61,7 @@ class TestClient < Sidekiq::Test
|
|
59
61
|
it 'allows local middleware modification' do
|
60
62
|
@redis.expect :lpush, 1, ['queue:default', Array]
|
61
63
|
$called = false
|
62
|
-
mware = Class.new { def call(worker_klass,msg,q); $called = true; msg;end }
|
64
|
+
mware = Class.new { def call(worker_klass,msg,q,r); $called = true; msg;end }
|
63
65
|
client = Sidekiq::Client.new
|
64
66
|
client.middleware do |chain|
|
65
67
|
chain.add mware
|
@@ -177,7 +179,7 @@ class TestClient < Sidekiq::Test
|
|
177
179
|
end
|
178
180
|
it 'returns the jids for the jobs' do
|
179
181
|
Sidekiq::Client.push_bulk('class' => 'QueuedWorker', 'args' => (1..2).to_a.map { |x| Array(x) }).each do |jid|
|
180
|
-
assert_match
|
182
|
+
assert_match(/[0-9a-f]{12}/, jid)
|
181
183
|
end
|
182
184
|
end
|
183
185
|
end
|
@@ -198,7 +200,8 @@ class TestClient < Sidekiq::Test
|
|
198
200
|
describe 'client middleware' do
|
199
201
|
|
200
202
|
class Stopper
|
201
|
-
def call(worker_class, message, queue)
|
203
|
+
def call(worker_class, message, queue, r)
|
204
|
+
raise ArgumentError unless r
|
202
205
|
yield if message['args'].first.odd?
|
203
206
|
end
|
204
207
|
end
|
@@ -207,9 +210,9 @@ class TestClient < Sidekiq::Test
|
|
207
210
|
Sidekiq.client_middleware.add Stopper
|
208
211
|
begin
|
209
212
|
assert_equal nil, Sidekiq::Client.push('class' => MyWorker, 'args' => [0])
|
210
|
-
assert_match
|
213
|
+
assert_match(/[0-9a-f]{12}/, Sidekiq::Client.push('class' => MyWorker, 'args' => [1]))
|
211
214
|
Sidekiq::Client.push_bulk('class' => MyWorker, 'args' => [[0], [1]]).each do |jid|
|
212
|
-
assert_match
|
215
|
+
assert_match(/[0-9a-f]{12}/, jid)
|
213
216
|
end
|
214
217
|
ensure
|
215
218
|
Sidekiq.client_middleware.remove Stopper
|
@@ -233,4 +236,40 @@ class TestClient < Sidekiq::Test
|
|
233
236
|
assert_equal 2, Sidekiq::Client.new.__send__(:normalize_item, 'class' => CWorker, 'args' => [])['retry']
|
234
237
|
end
|
235
238
|
end
|
239
|
+
|
240
|
+
describe 'sharding' do
|
241
|
+
class DWorker < BaseWorker
|
242
|
+
end
|
243
|
+
it 'allows sidekiq_options to point to different Redi' do
|
244
|
+
conn = MiniTest::Mock.new
|
245
|
+
conn.expect(:multi, [0, 1])
|
246
|
+
DWorker.sidekiq_options('pool' => ConnectionPool.new(size: 1) { conn })
|
247
|
+
DWorker.perform_async(1,2,3)
|
248
|
+
conn.verify
|
249
|
+
end
|
250
|
+
it 'allows #via to point to different Redi' do
|
251
|
+
conn = MiniTest::Mock.new
|
252
|
+
conn.expect(:multi, [0, 1])
|
253
|
+
default = Sidekiq::Client.new.redis_pool
|
254
|
+
sharded_pool = ConnectionPool.new(size: 1) { conn }
|
255
|
+
Sidekiq::Client.via(sharded_pool) do
|
256
|
+
CWorker.perform_async(1,2,3)
|
257
|
+
assert_equal sharded_pool, Sidekiq::Client.new.redis_pool
|
258
|
+
assert_raises RuntimeError do
|
259
|
+
Sidekiq::Client.via(default) do
|
260
|
+
# nothing
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
assert_equal default, Sidekiq::Client.new.redis_pool
|
265
|
+
conn.verify
|
266
|
+
end
|
267
|
+
it 'allows Resque helpers to point to different Redi' do
|
268
|
+
conn = MiniTest::Mock.new
|
269
|
+
conn.expect(:zadd, 1, [String, Array])
|
270
|
+
DWorker.sidekiq_options('pool' => ConnectionPool.new(size: 1) { conn })
|
271
|
+
Sidekiq::Client.enqueue_in(10, DWorker, 3)
|
272
|
+
conn.verify
|
273
|
+
end
|
274
|
+
end
|
236
275
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'helper'
|
2
|
-
require 'sidekiq'
|
3
2
|
require 'sidekiq/exception_handler'
|
4
3
|
require 'stringio'
|
5
4
|
require 'logger'
|
@@ -33,9 +32,9 @@ class TestExceptionHandler < Sidekiq::Test
|
|
33
32
|
Component.new.invoke_exception(:a => 1)
|
34
33
|
@str_logger.rewind
|
35
34
|
log = @str_logger.readlines
|
36
|
-
assert_match
|
37
|
-
assert_match
|
38
|
-
assert_match
|
35
|
+
assert_match(/a=>1/, log[0], "didn't include the context")
|
36
|
+
assert_match(/Something didn't work!/, log[1], "didn't include the exception message")
|
37
|
+
assert_match(/test\/test_exception_handler.rb/, log[2], "didn't include the backtrace")
|
39
38
|
end
|
40
39
|
|
41
40
|
describe "when the exception does not have a backtrace" do
|
@@ -46,93 +45,11 @@ class TestExceptionHandler < Sidekiq::Test
|
|
46
45
|
begin
|
47
46
|
Component.new.handle_exception exception
|
48
47
|
pass
|
49
|
-
rescue
|
48
|
+
rescue StandardError
|
50
49
|
flunk "failed handling a nil backtrace"
|
51
50
|
end
|
52
51
|
end
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
56
|
-
describe "with fake Airbrake" do
|
57
|
-
before do
|
58
|
-
::Airbrake = Minitest::Mock.new
|
59
|
-
end
|
60
|
-
|
61
|
-
after do
|
62
|
-
Object.__send__(:remove_const, "Airbrake") # HACK should probably inject Airbrake etc into this class in the future
|
63
|
-
end
|
64
|
-
|
65
|
-
it "notifies Airbrake" do
|
66
|
-
::Airbrake.expect(:notify_or_ignore,nil,[TEST_EXCEPTION,:parameters => { :a => 1 }])
|
67
|
-
Component.new.invoke_exception(:a => 1)
|
68
|
-
::Airbrake.verify
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
describe "with fake Honeybadger" do
|
73
|
-
before do
|
74
|
-
::Honeybadger = Minitest::Mock.new
|
75
|
-
end
|
76
|
-
|
77
|
-
after do
|
78
|
-
Object.__send__(:remove_const, "Honeybadger") # HACK should probably inject Honeybadger etc into this class in the future
|
79
|
-
end
|
80
|
-
|
81
|
-
it "notifies Honeybadger" do
|
82
|
-
::Honeybadger.expect(:notify_or_ignore,nil,[TEST_EXCEPTION,:parameters => { :a => 1 }])
|
83
|
-
Component.new.invoke_exception(:a => 1)
|
84
|
-
::Honeybadger.verify
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
describe "with fake ExceptionNotifier" do
|
89
|
-
before do
|
90
|
-
::ExceptionNotifier = MiniTest::Mock.new
|
91
|
-
end
|
92
|
-
|
93
|
-
after do
|
94
|
-
Object.__send__(:remove_const, "ExceptionNotifier")
|
95
|
-
end
|
96
|
-
|
97
|
-
it "notifies ExceptionNotifier" do
|
98
|
-
::ExceptionNotifier.expect(:notify_exception,true,[TEST_EXCEPTION, :data => { :message => { :b => 2 } }])
|
99
|
-
Component.new.invoke_exception(:b => 2)
|
100
|
-
::ExceptionNotifier.verify
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
describe "with fake Exceptional" do
|
105
|
-
before do
|
106
|
-
::Exceptional = Class.new do
|
107
|
-
|
108
|
-
def self.context(msg)
|
109
|
-
@msg = msg
|
110
|
-
end
|
111
|
-
|
112
|
-
def self.check_context
|
113
|
-
@msg
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
::Exceptional::Config = Minitest::Mock.new
|
118
|
-
::Exceptional::Remote = Minitest::Mock.new
|
119
|
-
::Exceptional::ExceptionData = Minitest::Mock.new
|
120
|
-
end
|
121
|
-
|
122
|
-
after do
|
123
|
-
Object.__send__(:remove_const, "Exceptional")
|
124
|
-
end
|
125
|
-
|
126
|
-
it "notifies Exceptional" do
|
127
|
-
::Exceptional::Config.expect(:should_send_to_api?,true)
|
128
|
-
exception_data = Object.new
|
129
|
-
::Exceptional::Remote.expect(:error,nil,[exception_data])
|
130
|
-
::Exceptional::ExceptionData.expect(:new,exception_data,[TEST_EXCEPTION])
|
131
|
-
Component.new.invoke_exception(:c => 3)
|
132
|
-
assert_equal({:c => 3},::Exceptional.check_context,"did not record arguments properly")
|
133
|
-
::Exceptional::Config.verify
|
134
|
-
::Exceptional::Remote.verify
|
135
|
-
::Exceptional::ExceptionData.verify
|
136
|
-
end
|
137
|
-
end
|
138
55
|
end
|
data/test/test_middleware.rb
CHANGED
@@ -112,6 +112,7 @@ class TestMiddleware < Sidekiq::Test
|
|
112
112
|
describe 'i18n' do
|
113
113
|
before do
|
114
114
|
require 'i18n'
|
115
|
+
I18n.enforce_available_locales = false
|
115
116
|
require 'sidekiq/middleware/i18n'
|
116
117
|
end
|
117
118
|
|
@@ -119,7 +120,7 @@ class TestMiddleware < Sidekiq::Test
|
|
119
120
|
I18n.locale = 'fr'
|
120
121
|
msg = {}
|
121
122
|
mw = Sidekiq::Middleware::I18n::Client.new
|
122
|
-
mw.call(nil, msg, nil) { }
|
123
|
+
mw.call(nil, msg, nil, nil) { }
|
123
124
|
assert_equal :fr, msg['locale']
|
124
125
|
|
125
126
|
msg['locale'] = 'jp'
|
@@ -142,7 +143,7 @@ class TestMiddleware < Sidekiq::Test
|
|
142
143
|
assert_equal :jp, I18n.locale
|
143
144
|
end
|
144
145
|
|
145
|
-
I18n.enforce_available_locales =
|
146
|
+
I18n.enforce_available_locales = false
|
146
147
|
I18n.available_locales = nil
|
147
148
|
end
|
148
149
|
end
|
@@ -81,12 +81,6 @@ class TestRedisConnection < Sidekiq::Test
|
|
81
81
|
ENV[var] = nil
|
82
82
|
end
|
83
83
|
|
84
|
-
describe "with REDISTOGO_URL set" do
|
85
|
-
it "sets connection URI to RedisToGo" do
|
86
|
-
with_env_var 'REDISTOGO_URL', 'redis://redis-to-go:6379/0'
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
84
|
describe "with REDISTOGO_URL and a parallel REDIS_PROVIDER set" do
|
91
85
|
it "sets connection URI to the provider" do
|
92
86
|
uri = 'redis://sidekiq-redis-provider:6379/0'
|
data/test/test_retry.rb
CHANGED
@@ -10,6 +10,7 @@ class TestRetry < Sidekiq::Test
|
|
10
10
|
Sidekiq.instance_variable_set(:@redis, @redis)
|
11
11
|
|
12
12
|
def @redis.with; yield self; end
|
13
|
+
def @redis.multi; yield self; end
|
13
14
|
end
|
14
15
|
|
15
16
|
let(:worker) do
|
@@ -50,6 +51,9 @@ class TestRetry < Sidekiq::Test
|
|
50
51
|
1.upto(max_retries) do
|
51
52
|
@redis.expect :zadd, 1, ['retry', String, String]
|
52
53
|
end
|
54
|
+
@redis.expect :zadd, 1, ['dead', Float, String]
|
55
|
+
@redis.expect :zremrangebyscore, 0, ['dead', String, Float]
|
56
|
+
@redis.expect :zremrangebyrank, 0, ['dead', Numeric, Numeric]
|
53
57
|
msg = { 'class' => 'Bob', 'args' => [1,2,'foo'], 'retry' => true }
|
54
58
|
handler = Sidekiq::Middleware::Server::RetryJobs.new({:max_retries => max_retries})
|
55
59
|
1.upto(max_retries + 1) do
|
@@ -166,90 +170,31 @@ class TestRetry < Sidekiq::Test
|
|
166
170
|
it 'throws away old messages after too many retries (using the default)' do
|
167
171
|
now = Time.now.to_f
|
168
172
|
msg = {"class"=>"Bob", "args"=>[1, 2, "foo"], "queue"=>"default", "error_message"=>"kerblammo!", "error_class"=>"RuntimeError", "failed_at"=>now, "retry"=>true, "retry_count"=>25}
|
169
|
-
@redis.expect :zadd, 1, [
|
173
|
+
@redis.expect :zadd, 1, ['dead', Float, String]
|
174
|
+
@redis.expect :zremrangebyscore, 0, ['dead', String, Float]
|
175
|
+
@redis.expect :zremrangebyrank, 0, ['dead', Numeric, Numeric]
|
170
176
|
handler = Sidekiq::Middleware::Server::RetryJobs.new
|
171
177
|
assert_raises RuntimeError do
|
172
178
|
handler.call(worker, msg, 'default') do
|
173
179
|
raise "kerblammo!"
|
174
180
|
end
|
175
181
|
end
|
176
|
-
|
177
|
-
assert_raises(MockExpectationError) { @redis.verify }
|
182
|
+
@redis.verify
|
178
183
|
end
|
179
184
|
|
180
185
|
it 'throws away old messages after too many retries (using user-specified max)' do
|
181
186
|
now = Time.now.to_f
|
182
187
|
msg = {"class"=>"Bob", "args"=>[1, 2, "foo"], "queue"=>"default", "error_message"=>"kerblammo!", "error_class"=>"RuntimeError", "failed_at"=>now, "retry"=>3, "retry_count"=>3}
|
183
|
-
@redis.expect :zadd, 1, [
|
188
|
+
@redis.expect :zadd, 1, ['dead', Float, String]
|
189
|
+
@redis.expect :zremrangebyscore, 0, ['dead', String, Float]
|
190
|
+
@redis.expect :zremrangebyrank, 0, ['dead', Numeric, Numeric]
|
184
191
|
handler = Sidekiq::Middleware::Server::RetryJobs.new
|
185
192
|
assert_raises RuntimeError do
|
186
193
|
handler.call(worker, msg, 'default') do
|
187
194
|
raise "kerblammo!"
|
188
195
|
end
|
189
196
|
end
|
190
|
-
|
191
|
-
assert_raises(MockExpectationError) { @redis.verify }
|
192
|
-
end
|
193
|
-
|
194
|
-
describe "retry exhaustion" do
|
195
|
-
let(:handler){ Sidekiq::Middleware::Server::RetryJobs.new }
|
196
|
-
let(:worker) { Minitest::Mock.new }
|
197
|
-
let(:msg){ {"class"=>"Bob", "args"=>[1, 2, "foo"], "queue"=>"default", "error_message"=>"kerblammo!", "error_class"=>"RuntimeError", "failed_at"=>Time.now.to_f, "retry"=>3, "retry_count"=>3} }
|
198
|
-
|
199
|
-
describe "worker method" do
|
200
|
-
let(:worker) do
|
201
|
-
klass = Class.new do
|
202
|
-
include Sidekiq::Worker
|
203
|
-
|
204
|
-
def self.name; "Worker"; end
|
205
|
-
|
206
|
-
def retries_exhausted(*args)
|
207
|
-
args << "retried_method"
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
it 'calls worker.retries_exhausted after too many retries' do
|
213
|
-
assert_equal [1,2, "foo", "retried_method"], handler.__send__(:retries_exhausted, worker.new, msg)
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
describe "worker block" do
|
218
|
-
let(:worker) do
|
219
|
-
Class.new do
|
220
|
-
include Sidekiq::Worker
|
221
|
-
|
222
|
-
sidekiq_retries_exhausted do |msg|
|
223
|
-
msg.tap {|m| m['called_by_callback'] = true }
|
224
|
-
end
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
it 'calls worker sidekiq_retries_exhausted_block after too many retries' do
|
229
|
-
new_msg = handler.__send__(:retries_exhausted, worker.new, msg)
|
230
|
-
expected_msg = msg.merge('called_by_callback' => true)
|
231
|
-
|
232
|
-
assert_equal expected_msg, new_msg, "sidekiq_retries_exhausted block not called"
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
it 'handles and logs retries_exhausted failures gracefully (drops them)' do
|
237
|
-
def worker.retries_exhausted(*args)
|
238
|
-
raise 'bam!'
|
239
|
-
end
|
240
|
-
|
241
|
-
e = task_misbehaving_worker
|
242
|
-
assert_equal e.message, "kerblammo!"
|
243
|
-
worker.verify
|
244
|
-
end
|
245
|
-
|
246
|
-
def task_misbehaving_worker
|
247
|
-
assert_raises RuntimeError do
|
248
|
-
handler.call(worker, msg, 'default') do
|
249
|
-
raise 'kerblammo!'
|
250
|
-
end
|
251
|
-
end
|
252
|
-
end
|
197
|
+
@redis.verify
|
253
198
|
end
|
254
199
|
|
255
200
|
describe "custom retry delay" do
|
@@ -262,7 +207,7 @@ class TestRetry < Sidekiq::Test
|
|
262
207
|
after do
|
263
208
|
Sidekiq.logger = @old_logger
|
264
209
|
Sidekiq.options.delete(:logfile)
|
265
|
-
File.unlink @tmp_log_path if File.
|
210
|
+
File.unlink @tmp_log_path if File.exist?(@tmp_log_path)
|
266
211
|
end
|
267
212
|
|
268
213
|
let(:custom_worker) do
|
data/test/test_scheduled.rb
CHANGED
data/test/test_scheduling.rb
CHANGED
@@ -4,6 +4,7 @@ require 'sidekiq/scheduled'
|
|
4
4
|
class TestScheduling < Sidekiq::Test
|
5
5
|
describe 'middleware' do
|
6
6
|
before do
|
7
|
+
Sidekiq::Client.instance_variable_set(:@default, nil)
|
7
8
|
@redis = Minitest::Mock.new
|
8
9
|
# Ugh, this is terrible.
|
9
10
|
Sidekiq.instance_variable_set(:@redis, @redis)
|
@@ -11,6 +12,10 @@ class TestScheduling < Sidekiq::Test
|
|
11
12
|
def @redis.with; yield self; end
|
12
13
|
end
|
13
14
|
|
15
|
+
after do
|
16
|
+
Sidekiq::Client.instance_variable_set(:@default, nil)
|
17
|
+
end
|
18
|
+
|
14
19
|
class ScheduledWorker
|
15
20
|
include Sidekiq::Worker
|
16
21
|
sidekiq_options :queue => :custom_queue
|
data/test/test_sidekiq.rb
CHANGED
@@ -34,4 +34,22 @@ class TestSidekiq < Sidekiq::Test
|
|
34
34
|
assert_equal "Calm down, bro\n", $stdout.string
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
describe 'lifecycle events' do
|
39
|
+
it 'handles invalid input' do
|
40
|
+
e = assert_raises ArgumentError do
|
41
|
+
Sidekiq.on(:startp)
|
42
|
+
end
|
43
|
+
assert_match(/Invalid event name/, e.message)
|
44
|
+
e = assert_raises ArgumentError do
|
45
|
+
Sidekiq.on('startup')
|
46
|
+
end
|
47
|
+
assert_match(/Symbols only/, e.message)
|
48
|
+
Sidekiq.on(:startup) do
|
49
|
+
1 + 1
|
50
|
+
end
|
51
|
+
|
52
|
+
assert_equal 2, Sidekiq.options[:lifecycle_events][:startup].first.call
|
53
|
+
end
|
54
|
+
end
|
37
55
|
end
|
data/test/test_web.rb
CHANGED
@@ -4,6 +4,7 @@ require 'sidekiq/web'
|
|
4
4
|
require 'rack/test'
|
5
5
|
|
6
6
|
class TestWeb < Sidekiq::Test
|
7
|
+
|
7
8
|
describe 'sidekiq web' do
|
8
9
|
include Rack::Test::Methods
|
9
10
|
|
@@ -30,18 +31,20 @@ class TestWeb < Sidekiq::Test
|
|
30
31
|
|
31
32
|
it 'can display workers' do
|
32
33
|
Sidekiq.redis do |conn|
|
33
|
-
|
34
|
-
conn.sadd('
|
35
|
-
conn.
|
34
|
+
conn.incr('busy')
|
35
|
+
conn.sadd('processes', 'foo:1234')
|
36
|
+
conn.hmset('foo:1234', 'info', Sidekiq.dump_json('hostname' => 'foo', 'started_at' => Time.now.to_f), 'at', Time.now.to_f, 'busy', 4)
|
37
|
+
identity = 'foo:1234:workers'
|
36
38
|
hash = {:queue => 'critical', :payload => { 'class' => WebWorker.name, 'args' => [1,'abc'] }, :run_at => Time.now.to_i }
|
37
|
-
conn.
|
39
|
+
conn.hmset(identity, 1001, Sidekiq.dump_json(hash))
|
38
40
|
end
|
41
|
+
assert_equal ['1001'], Sidekiq::Workers.new.map { |pid, tid, data| tid }
|
39
42
|
|
40
|
-
get '/
|
43
|
+
get '/busy'
|
41
44
|
assert_equal 200, last_response.status
|
42
|
-
assert_match
|
43
|
-
assert_match
|
44
|
-
assert_match
|
45
|
+
assert_match(/status-active/, last_response.body)
|
46
|
+
assert_match(/critical/, last_response.body)
|
47
|
+
assert_match(/WebWorker/, last_response.body)
|
45
48
|
end
|
46
49
|
|
47
50
|
it 'can display queues' do
|
@@ -49,8 +52,8 @@ class TestWeb < Sidekiq::Test
|
|
49
52
|
|
50
53
|
get '/queues'
|
51
54
|
assert_equal 200, last_response.status
|
52
|
-
assert_match
|
53
|
-
refute_match
|
55
|
+
assert_match(/foo/, last_response.body)
|
56
|
+
refute_match(/HardWorker/, last_response.body)
|
54
57
|
end
|
55
58
|
|
56
59
|
it 'handles queue view' do
|
@@ -76,26 +79,6 @@ class TestWeb < Sidekiq::Test
|
|
76
79
|
end
|
77
80
|
end
|
78
81
|
|
79
|
-
it 'can clear an empty worker list' do
|
80
|
-
post '/reset'
|
81
|
-
assert_equal 302, last_response.status
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'can clear a non-empty worker list' do
|
85
|
-
Sidekiq.redis do |conn|
|
86
|
-
identity = 'foo'
|
87
|
-
conn.sadd('workers', identity)
|
88
|
-
end
|
89
|
-
|
90
|
-
post '/reset'
|
91
|
-
|
92
|
-
assert_equal 302, last_response.status
|
93
|
-
|
94
|
-
Sidekiq.redis do |conn|
|
95
|
-
refute conn.smembers('workers').any?
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
82
|
it 'can delete a job' do
|
100
83
|
Sidekiq.redis do |conn|
|
101
84
|
conn.rpush('queue:foo', "{}")
|
@@ -117,15 +100,15 @@ class TestWeb < Sidekiq::Test
|
|
117
100
|
it 'can display retries' do
|
118
101
|
get '/retries'
|
119
102
|
assert_equal 200, last_response.status
|
120
|
-
assert_match
|
121
|
-
refute_match
|
103
|
+
assert_match(/found/, last_response.body)
|
104
|
+
refute_match(/HardWorker/, last_response.body)
|
122
105
|
|
123
106
|
add_retry
|
124
107
|
|
125
108
|
get '/retries'
|
126
109
|
assert_equal 200, last_response.status
|
127
|
-
refute_match
|
128
|
-
assert_match
|
110
|
+
refute_match(/found/, last_response.body)
|
111
|
+
assert_match(/HardWorker/, last_response.body)
|
129
112
|
end
|
130
113
|
|
131
114
|
it 'can display a single retry' do
|
@@ -134,7 +117,7 @@ class TestWeb < Sidekiq::Test
|
|
134
117
|
assert_equal 302, last_response.status
|
135
118
|
get "/retries/#{job_params(*params)}"
|
136
119
|
assert_equal 200, last_response.status
|
137
|
-
assert_match
|
120
|
+
assert_match(/HardWorker/, last_response.body)
|
138
121
|
end
|
139
122
|
|
140
123
|
it 'handles missing retry' do
|
@@ -150,7 +133,7 @@ class TestWeb < Sidekiq::Test
|
|
150
133
|
|
151
134
|
get "/retries"
|
152
135
|
assert_equal 200, last_response.status
|
153
|
-
refute_match
|
136
|
+
refute_match(/#{params.first['args'][2]}/, last_response.body)
|
154
137
|
end
|
155
138
|
|
156
139
|
it 'can delete all retries' do
|
@@ -170,21 +153,21 @@ class TestWeb < Sidekiq::Test
|
|
170
153
|
|
171
154
|
get '/queues/default'
|
172
155
|
assert_equal 200, last_response.status
|
173
|
-
assert_match
|
156
|
+
assert_match(/#{params.first['args'][2]}/, last_response.body)
|
174
157
|
end
|
175
158
|
|
176
159
|
it 'can display scheduled' do
|
177
160
|
get '/scheduled'
|
178
161
|
assert_equal 200, last_response.status
|
179
|
-
assert_match
|
180
|
-
refute_match
|
162
|
+
assert_match(/found/, last_response.body)
|
163
|
+
refute_match(/HardWorker/, last_response.body)
|
181
164
|
|
182
165
|
add_scheduled
|
183
166
|
|
184
167
|
get '/scheduled'
|
185
168
|
assert_equal 200, last_response.status
|
186
|
-
refute_match
|
187
|
-
assert_match
|
169
|
+
refute_match(/found/, last_response.body)
|
170
|
+
assert_match(/HardWorker/, last_response.body)
|
188
171
|
end
|
189
172
|
|
190
173
|
it 'can display a single scheduled job' do
|
@@ -193,7 +176,7 @@ class TestWeb < Sidekiq::Test
|
|
193
176
|
assert_equal 302, last_response.status
|
194
177
|
get "/scheduled/#{job_params(*params)}"
|
195
178
|
assert_equal 200, last_response.status
|
196
|
-
assert_match
|
179
|
+
assert_match(/HardWorker/, last_response.body)
|
197
180
|
end
|
198
181
|
|
199
182
|
it 'handles missing scheduled job' do
|
@@ -209,7 +192,7 @@ class TestWeb < Sidekiq::Test
|
|
209
192
|
|
210
193
|
get '/queues/default'
|
211
194
|
assert_equal 200, last_response.status
|
212
|
-
assert_match
|
195
|
+
assert_match(/#{params.first['args'][2]}/, last_response.body)
|
213
196
|
end
|
214
197
|
|
215
198
|
it 'can delete a single scheduled job' do
|
@@ -220,7 +203,7 @@ class TestWeb < Sidekiq::Test
|
|
220
203
|
|
221
204
|
get "/scheduled"
|
222
205
|
assert_equal 200, last_response.status
|
223
|
-
refute_match
|
206
|
+
refute_match(/#{params.first['args'][2]}/, last_response.body)
|
224
207
|
end
|
225
208
|
|
226
209
|
it 'can delete scheduled' do
|
@@ -247,12 +230,12 @@ class TestWeb < Sidekiq::Test
|
|
247
230
|
assert_equal 1, q.size
|
248
231
|
get '/queues/default'
|
249
232
|
assert_equal 200, last_response.status
|
250
|
-
assert_match
|
233
|
+
assert_match(/#{params[0]['args'][2]}/, last_response.body)
|
251
234
|
end
|
252
235
|
end
|
253
236
|
|
254
237
|
it 'can retry all retries' do
|
255
|
-
msg
|
238
|
+
msg = add_retry.first
|
256
239
|
add_retry
|
257
240
|
|
258
241
|
post "/retries/all/retry", 'retry' => 'Retry'
|
@@ -270,7 +253,7 @@ class TestWeb < Sidekiq::Test
|
|
270
253
|
params = add_xss_retry
|
271
254
|
get '/retries'
|
272
255
|
assert_equal 200, last_response.status
|
273
|
-
assert_match
|
256
|
+
assert_match(/FailWorker/, last_response.body)
|
274
257
|
|
275
258
|
assert last_response.body.include?( "fail message: <a>hello</a>" )
|
276
259
|
assert !last_response.body.include?( "fail message: <a>hello</a>" )
|
@@ -278,19 +261,20 @@ class TestWeb < Sidekiq::Test
|
|
278
261
|
assert last_response.body.include?( "args\">"<a>hello</a>"<" )
|
279
262
|
assert !last_response.body.include?( "args\"><a>hello</a><" )
|
280
263
|
|
281
|
-
|
282
264
|
# on /workers page
|
283
265
|
Sidekiq.redis do |conn|
|
284
|
-
|
285
|
-
conn.sadd('
|
286
|
-
conn.
|
266
|
+
pro = 'foo:1234'
|
267
|
+
conn.sadd('processes', pro)
|
268
|
+
conn.hmset(pro, 'info', Sidekiq.dump_json('started_at' => Time.now.to_f), 'busy', 1, 'beat', Time.now.to_f)
|
269
|
+
identity = "#{pro}:workers"
|
287
270
|
hash = {:queue => 'critical', :payload => { 'class' => "FailWorker", 'args' => ["<a>hello</a>"] }, :run_at => Time.now.to_i }
|
288
|
-
conn.
|
271
|
+
conn.hmset(identity, 100001, Sidekiq.dump_json(hash))
|
272
|
+
conn.incr('busy')
|
289
273
|
end
|
290
274
|
|
291
|
-
get '/
|
275
|
+
get '/busy'
|
292
276
|
assert_equal 200, last_response.status
|
293
|
-
assert_match
|
277
|
+
assert_match(/FailWorker/, last_response.body)
|
294
278
|
assert last_response.body.include?( "<a>hello</a>" )
|
295
279
|
assert !last_response.body.include?( "<a>hello</a>" )
|
296
280
|
|
@@ -332,7 +316,7 @@ class TestWeb < Sidekiq::Test
|
|
332
316
|
end
|
333
317
|
|
334
318
|
get '/custom'
|
335
|
-
assert_match
|
319
|
+
assert_match(/Changed text/, last_response.body)
|
336
320
|
|
337
321
|
ensure
|
338
322
|
Sidekiq::Web.tabs.delete 'Custom Tab'
|
@@ -340,6 +324,8 @@ class TestWeb < Sidekiq::Test
|
|
340
324
|
end
|
341
325
|
|
342
326
|
describe 'stats' do
|
327
|
+
include Sidekiq::Util
|
328
|
+
|
343
329
|
before do
|
344
330
|
Sidekiq.redis do |conn|
|
345
331
|
conn.set("stat:processed", 5)
|
@@ -394,6 +380,41 @@ class TestWeb < Sidekiq::Test
|
|
394
380
|
end
|
395
381
|
end
|
396
382
|
|
383
|
+
describe 'dead jobs' do
|
384
|
+
it 'shows empty index' do
|
385
|
+
get 'morgue'
|
386
|
+
assert_equal 200, last_response.status
|
387
|
+
end
|
388
|
+
|
389
|
+
it 'shows index with jobs' do
|
390
|
+
(_, score) = add_dead
|
391
|
+
get 'morgue'
|
392
|
+
assert_equal 200, last_response.status
|
393
|
+
assert_match(/#{score}/, last_response.body)
|
394
|
+
end
|
395
|
+
|
396
|
+
it 'can delete all dead' do
|
397
|
+
3.times { add_dead }
|
398
|
+
|
399
|
+
assert_equal 3, Sidekiq::DeadSet.new.size
|
400
|
+
post "/morgue/all/delete", 'delete' => 'Delete'
|
401
|
+
assert_equal 0, Sidekiq::DeadSet.new.size
|
402
|
+
assert_equal 302, last_response.status
|
403
|
+
assert_equal 'http://example.org/morgue', last_response.header['Location']
|
404
|
+
end
|
405
|
+
|
406
|
+
it 'can retry a dead job' do
|
407
|
+
params = add_dead
|
408
|
+
post "/morgue/#{job_params(*params)}", 'retry' => 'Retry'
|
409
|
+
assert_equal 302, last_response.status
|
410
|
+
assert_equal 'http://example.org/morgue', last_response.header['Location']
|
411
|
+
|
412
|
+
get '/queues/foo'
|
413
|
+
assert_equal 200, last_response.status
|
414
|
+
assert_match(/#{params.first['args'][2]}/, last_response.body)
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
397
418
|
def add_scheduled
|
398
419
|
score = Time.now.to_f
|
399
420
|
msg = { 'class' => 'HardWorker',
|
@@ -421,6 +442,22 @@ class TestWeb < Sidekiq::Test
|
|
421
442
|
[msg, score]
|
422
443
|
end
|
423
444
|
|
445
|
+
def add_dead
|
446
|
+
msg = { 'class' => 'HardWorker',
|
447
|
+
'args' => ['bob', 1, Time.now.to_f],
|
448
|
+
'queue' => 'foo',
|
449
|
+
'error_message' => 'Some fake message',
|
450
|
+
'error_class' => 'RuntimeError',
|
451
|
+
'retry_count' => 0,
|
452
|
+
'failed_at' => Time.now.utc,
|
453
|
+
'jid' => SecureRandom.hex(12) }
|
454
|
+
score = Time.now.to_f
|
455
|
+
Sidekiq.redis do |conn|
|
456
|
+
conn.zadd('dead', score, Sidekiq.dump_json(msg))
|
457
|
+
end
|
458
|
+
[msg, score]
|
459
|
+
end
|
460
|
+
|
424
461
|
def add_xss_retry
|
425
462
|
msg = { 'class' => 'FailWorker',
|
426
463
|
'args' => ['<a>hello</a>'],
|
@@ -438,11 +475,14 @@ class TestWeb < Sidekiq::Test
|
|
438
475
|
end
|
439
476
|
|
440
477
|
def add_worker
|
441
|
-
|
478
|
+
key = "#{hostname}:#{$$}"
|
442
479
|
msg = "{\"queue\":\"default\",\"payload\":{\"retry\":true,\"queue\":\"default\",\"timeout\":20,\"backtrace\":5,\"class\":\"HardWorker\",\"args\":[\"bob\",10,5],\"jid\":\"2b5ad2b016f5e063a1c62872\"},\"run_at\":1361208995}"
|
443
480
|
Sidekiq.redis do |conn|
|
444
|
-
conn.
|
445
|
-
|
481
|
+
conn.multi do
|
482
|
+
conn.sadd("processes", key)
|
483
|
+
conn.hmset(key, 'busy', 4)
|
484
|
+
conn.hmset("#{key}:workers", Time.now.to_f, msg)
|
485
|
+
end
|
446
486
|
end
|
447
487
|
end
|
448
488
|
end
|