sidekiq 3.5.4 → 4.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/4.0-Upgrade.md +50 -0
- data/Changes.md +15 -3
- data/Ent-Changes.md +27 -0
- data/Gemfile +2 -1
- data/Pro-3.0-Upgrade.md +46 -0
- data/Pro-Changes.md +21 -0
- data/README.md +4 -4
- data/bin/sidekiqctl +8 -2
- data/bin/sidekiqload +20 -6
- data/lib/sidekiq.rb +24 -11
- data/lib/sidekiq/api.rb +2 -2
- data/lib/sidekiq/cli.rb +19 -29
- data/lib/sidekiq/client.rb +0 -5
- data/lib/sidekiq/fetch.rb +35 -111
- data/lib/sidekiq/launcher.rb +105 -46
- data/lib/sidekiq/manager.rb +77 -180
- data/lib/sidekiq/middleware/server/retry_jobs.rb +1 -1
- data/lib/sidekiq/processor.rb +119 -96
- data/lib/sidekiq/redis_connection.rb +23 -5
- data/lib/sidekiq/scheduled.rb +47 -26
- data/lib/sidekiq/testing.rb +84 -14
- data/lib/sidekiq/util.rb +7 -0
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web_helpers.rb +8 -1
- data/sidekiq.gemspec +2 -2
- data/test/helper.rb +30 -5
- data/test/test_actors.rb +137 -0
- data/test/test_api.rb +395 -394
- data/test/test_fetch.rb +2 -57
- data/test/test_launcher.rb +80 -0
- data/test/test_manager.rb +13 -132
- data/test/test_middleware.rb +3 -5
- data/test/test_processor.rb +20 -57
- data/test/test_scheduled.rb +2 -2
- data/test/test_testing_fake.rb +64 -1
- data/web/assets/stylesheets/application.css +4 -0
- data/web/views/_footer.erb +2 -7
- metadata +30 -25
- data/lib/sidekiq/actor.rb +0 -39
data/lib/sidekiq/testing.rb
CHANGED
@@ -68,15 +68,15 @@ module Sidekiq
|
|
68
68
|
def raw_push(payloads)
|
69
69
|
if Sidekiq::Testing.fake?
|
70
70
|
payloads.each do |job|
|
71
|
-
job['
|
71
|
+
Queues.jobs[job['queue']] << Sidekiq.load_json(Sidekiq.dump_json(job))
|
72
72
|
end
|
73
73
|
true
|
74
74
|
elsif Sidekiq::Testing.inline?
|
75
75
|
payloads.each do |job|
|
76
|
-
job['jid'] ||= SecureRandom.hex(12)
|
77
76
|
klass = job['class'].constantize
|
78
|
-
|
79
|
-
|
77
|
+
job['id'] ||= SecureRandom.hex(12)
|
78
|
+
job_hash = Sidekiq.load_json(Sidekiq.dump_json(job))
|
79
|
+
klass.process_job(job_hash)
|
80
80
|
end
|
81
81
|
true
|
82
82
|
else
|
@@ -85,6 +85,64 @@ module Sidekiq
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
+
module Queues
|
89
|
+
##
|
90
|
+
# The Queues class is only for testing the fake queue implementation.
|
91
|
+
# The data is structured as a hash with queue name as hash key and array
|
92
|
+
# of job data as the value.
|
93
|
+
#
|
94
|
+
# {
|
95
|
+
# "default"=>[
|
96
|
+
# {
|
97
|
+
# "class"=>"TestTesting::QueueWorker",
|
98
|
+
# "args"=>[1, 2],
|
99
|
+
# "retry"=>true,
|
100
|
+
# "queue"=>"default",
|
101
|
+
# "jid"=>"abc5b065c5c4b27fc1102833",
|
102
|
+
# "created_at"=>1447445554.419934
|
103
|
+
# }
|
104
|
+
# ]
|
105
|
+
# }
|
106
|
+
#
|
107
|
+
# Example:
|
108
|
+
#
|
109
|
+
# require 'sidekiq/testing'
|
110
|
+
#
|
111
|
+
# assert_equal 0, Sidekiq::Queues["default"].size
|
112
|
+
# HardWorker.perform_async(:something)
|
113
|
+
# assert_equal 1, Sidekiq::Queues["default"].size
|
114
|
+
# assert_equal :something, Sidekiq::Queues["default"].first['args'][0]
|
115
|
+
#
|
116
|
+
# You can also clear all workers' jobs:
|
117
|
+
#
|
118
|
+
# assert_equal 0, Sidekiq::Queues["default"].size
|
119
|
+
# HardWorker.perform_async(:something)
|
120
|
+
# Sidekiq::Queues.clear_all
|
121
|
+
# assert_equal 0, Sidekiq::Queues["default"].size
|
122
|
+
#
|
123
|
+
# This can be useful to make sure jobs don't linger between tests:
|
124
|
+
#
|
125
|
+
# RSpec.configure do |config|
|
126
|
+
# config.before(:each) do
|
127
|
+
# Sidekiq::Queues.clear_all
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
class << self
|
132
|
+
def [](queue)
|
133
|
+
jobs[queue.to_s]
|
134
|
+
end
|
135
|
+
|
136
|
+
def jobs
|
137
|
+
@jobs ||= Hash.new { |hash, key| hash[key] = [] }
|
138
|
+
end
|
139
|
+
|
140
|
+
def clear_all
|
141
|
+
jobs.clear
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
88
146
|
module Worker
|
89
147
|
##
|
90
148
|
# The Sidekiq testing infrastructure overrides perform_async
|
@@ -143,28 +201,36 @@ module Sidekiq
|
|
143
201
|
#
|
144
202
|
module ClassMethods
|
145
203
|
|
204
|
+
# Queue for this worker
|
205
|
+
def queue
|
206
|
+
self.sidekiq_options["queue"].to_s
|
207
|
+
end
|
208
|
+
|
146
209
|
# Jobs queued for this worker
|
147
210
|
def jobs
|
148
|
-
|
211
|
+
Queues.jobs[queue].select { |job| job["class"] == self.to_s }
|
149
212
|
end
|
150
213
|
|
151
214
|
# Clear all jobs for this worker
|
152
215
|
def clear
|
153
|
-
jobs.clear
|
216
|
+
Queues.jobs[queue].clear
|
154
217
|
end
|
155
218
|
|
156
219
|
# Drain and run all jobs for this worker
|
157
220
|
def drain
|
158
|
-
while
|
159
|
-
|
221
|
+
while jobs.any?
|
222
|
+
next_job = jobs.first
|
223
|
+
Queues.jobs[queue].delete_if { |job| job["jid"] == next_job["jid"] }
|
224
|
+
process_job(next_job)
|
160
225
|
end
|
161
226
|
end
|
162
227
|
|
163
228
|
# Pop out a single job and perform it
|
164
229
|
def perform_one
|
165
230
|
raise(EmptyQueueError, "perform_one called with empty job queue") if jobs.empty?
|
166
|
-
|
167
|
-
|
231
|
+
next_job = jobs.first
|
232
|
+
Queues.jobs[queue].delete_if { |job| job["jid"] == next_job["jid"] }
|
233
|
+
process_job(next_job)
|
168
234
|
end
|
169
235
|
|
170
236
|
def process_job(job)
|
@@ -183,18 +249,22 @@ module Sidekiq
|
|
183
249
|
|
184
250
|
class << self
|
185
251
|
def jobs # :nodoc:
|
186
|
-
|
252
|
+
Queues.jobs.values.flatten
|
187
253
|
end
|
188
254
|
|
189
255
|
# Clear all queued jobs across all workers
|
190
256
|
def clear_all
|
191
|
-
|
257
|
+
Queues.clear_all
|
192
258
|
end
|
193
259
|
|
194
260
|
# Drain all queued jobs across all workers
|
195
261
|
def drain_all
|
196
|
-
|
197
|
-
jobs.
|
262
|
+
while jobs.any?
|
263
|
+
worker_classes = jobs.map { |job| job["class"] }.uniq
|
264
|
+
|
265
|
+
worker_classes.each do |worker_class|
|
266
|
+
worker_class.constantize.drain
|
267
|
+
end
|
198
268
|
end
|
199
269
|
end
|
200
270
|
end
|
data/lib/sidekiq/util.rb
CHANGED
@@ -19,6 +19,12 @@ module Sidekiq
|
|
19
19
|
raise ex
|
20
20
|
end
|
21
21
|
|
22
|
+
def safe_thread(name, &block)
|
23
|
+
Thread.new do
|
24
|
+
watchdog(name, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
22
28
|
def logger
|
23
29
|
Sidekiq.logger
|
24
30
|
end
|
@@ -49,6 +55,7 @@ module Sidekiq
|
|
49
55
|
handle_exception(ex, { event: event })
|
50
56
|
end
|
51
57
|
end
|
58
|
+
arr.clear
|
52
59
|
end
|
53
60
|
|
54
61
|
def want_a_hertz_donut?
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web_helpers.rb
CHANGED
@@ -160,7 +160,7 @@ module Sidekiq
|
|
160
160
|
options = options.stringify_keys
|
161
161
|
params.merge(options).map do |key, value|
|
162
162
|
SAFE_QPARAMS.include?(key) ? "#{key}=#{value}" : next
|
163
|
-
end.join("&")
|
163
|
+
end.compact.join("&")
|
164
164
|
end
|
165
165
|
|
166
166
|
def truncate(text, truncate_after_chars = 2000)
|
@@ -245,5 +245,12 @@ module Sidekiq
|
|
245
245
|
def product_version
|
246
246
|
"Sidekiq v#{Sidekiq::VERSION}"
|
247
247
|
end
|
248
|
+
|
249
|
+
def redis_connection_and_namespace
|
250
|
+
@redis_connection_and_namespace ||= begin
|
251
|
+
namespace_suffix = namespace == nil ? '' : "##{namespace}"
|
252
|
+
"#{redis_connection}#{namespace_suffix}"
|
253
|
+
end
|
254
|
+
end
|
248
255
|
end
|
249
256
|
end
|
data/sidekiq.gemspec
CHANGED
@@ -16,10 +16,10 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.require_paths = ["lib"]
|
17
17
|
gem.version = Sidekiq::VERSION
|
18
18
|
gem.add_dependency 'redis', '~> 3.2', '>= 3.2.1'
|
19
|
-
gem.add_dependency 'redis-namespace', '~> 1.5', '>= 1.5.2'
|
20
19
|
gem.add_dependency 'connection_pool', '~> 2.2', '>= 2.2.0'
|
21
|
-
gem.add_dependency 'celluloid', '~> 0.17.2'
|
22
20
|
gem.add_dependency 'json', '~> 1.0'
|
21
|
+
gem.add_dependency 'concurrent-ruby', '~> 1.0'
|
22
|
+
gem.add_development_dependency 'redis-namespace', '~> 1.5', '>= 1.5.2'
|
23
23
|
gem.add_development_dependency 'sinatra', '~> 1.4', '>= 1.4.6'
|
24
24
|
gem.add_development_dependency 'minitest', '~> 5.7', '>= 5.7.0'
|
25
25
|
gem.add_development_dependency 'rake', '~> 10.0'
|
data/test/helper.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
$CELLULOID_DEBUG = false
|
2
1
|
$TESTING = true
|
2
|
+
# disable minitest/parallel threads
|
3
|
+
ENV["N"] = "0"
|
4
|
+
|
3
5
|
if ENV["COVERAGE"]
|
4
6
|
require 'simplecov'
|
5
7
|
SimpleCov.start do
|
@@ -9,17 +11,30 @@ if ENV["COVERAGE"]
|
|
9
11
|
end
|
10
12
|
ENV['RACK_ENV'] = ENV['RAILS_ENV'] = 'test'
|
11
13
|
|
14
|
+
trap 'USR1' do
|
15
|
+
threads = Thread.list
|
16
|
+
|
17
|
+
puts
|
18
|
+
puts "=" * 80
|
19
|
+
puts "Received USR1 signal; printing all #{threads.count} thread backtraces."
|
20
|
+
|
21
|
+
threads.each do |thr|
|
22
|
+
description = thr == Thread.main ? "Main thread" : thr.inspect
|
23
|
+
puts
|
24
|
+
puts "#{description} backtrace: "
|
25
|
+
puts thr.backtrace.join("\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
puts "=" * 80
|
29
|
+
end
|
30
|
+
|
12
31
|
begin
|
13
32
|
require 'pry-byebug'
|
14
33
|
rescue LoadError
|
15
34
|
end
|
16
35
|
|
17
36
|
require 'minitest/autorun'
|
18
|
-
require 'minitest/pride'
|
19
37
|
|
20
|
-
require 'celluloid/current'
|
21
|
-
require 'celluloid/test'
|
22
|
-
Celluloid.boot
|
23
38
|
require 'sidekiq'
|
24
39
|
require 'sidekiq/util'
|
25
40
|
Sidekiq.logger.level = Logger::ERROR
|
@@ -47,3 +62,13 @@ def capture_logging(lvl=Logger::INFO)
|
|
47
62
|
Sidekiq.logger = old
|
48
63
|
end
|
49
64
|
end
|
65
|
+
|
66
|
+
def with_logging(lvl=Logger::DEBUG)
|
67
|
+
old = Sidekiq.logger.level
|
68
|
+
begin
|
69
|
+
Sidekiq.logger.level = lvl
|
70
|
+
yield
|
71
|
+
ensure
|
72
|
+
Sidekiq.logger.level = old
|
73
|
+
end
|
74
|
+
end
|
data/test/test_actors.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
require 'sidekiq/cli'
|
3
|
+
require 'sidekiq/fetch'
|
4
|
+
require 'sidekiq/scheduled'
|
5
|
+
require 'sidekiq/processor'
|
6
|
+
|
7
|
+
class TestActors < Sidekiq::Test
|
8
|
+
class JoeWorker
|
9
|
+
include Sidekiq::Worker
|
10
|
+
def perform(slp)
|
11
|
+
raise "boom" if slp == "boom"
|
12
|
+
sleep(slp) if slp > 0
|
13
|
+
$count += 1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'threads' do
|
18
|
+
before do
|
19
|
+
Sidekiq.redis {|c| c.flushdb}
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'scheduler' do
|
23
|
+
it 'can start and stop' do
|
24
|
+
f = Sidekiq::Scheduled::Poller.new
|
25
|
+
f.start
|
26
|
+
f.terminate
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'can schedule' do
|
30
|
+
ss = Sidekiq::ScheduledSet.new
|
31
|
+
q = Sidekiq::Queue.new
|
32
|
+
|
33
|
+
JoeWorker.perform_in(0.01, 0)
|
34
|
+
|
35
|
+
assert_equal 0, q.size
|
36
|
+
assert_equal 1, ss.size
|
37
|
+
|
38
|
+
sleep 0.015
|
39
|
+
s = Sidekiq::Scheduled::Poller.new
|
40
|
+
s.enqueue
|
41
|
+
assert_equal 1, q.size
|
42
|
+
assert_equal 0, ss.size
|
43
|
+
s.terminate
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'processor' do
|
48
|
+
before do
|
49
|
+
$count = 0
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'can start and stop' do
|
53
|
+
f = Sidekiq::Processor.new(Mgr.new)
|
54
|
+
f.terminate
|
55
|
+
end
|
56
|
+
|
57
|
+
class Mgr
|
58
|
+
attr_reader :latest_error
|
59
|
+
attr_reader :mutex
|
60
|
+
attr_reader :cond
|
61
|
+
def initialize
|
62
|
+
@mutex = ::Mutex.new
|
63
|
+
@cond = ::ConditionVariable.new
|
64
|
+
end
|
65
|
+
def processor_died(inst, err)
|
66
|
+
@latest_error = err
|
67
|
+
@mutex.synchronize do
|
68
|
+
@cond.signal
|
69
|
+
end
|
70
|
+
end
|
71
|
+
def processor_stopped(inst)
|
72
|
+
@mutex.synchronize do
|
73
|
+
@cond.signal
|
74
|
+
end
|
75
|
+
end
|
76
|
+
def options
|
77
|
+
{ :concurrency => 3, :queues => ['default'] }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'can process' do
|
82
|
+
mgr = Mgr.new
|
83
|
+
|
84
|
+
p = Sidekiq::Processor.new(mgr)
|
85
|
+
JoeWorker.perform_async(0)
|
86
|
+
|
87
|
+
a = $count
|
88
|
+
p.process_one
|
89
|
+
b = $count
|
90
|
+
assert_equal a + 1, b
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'deals with errors' do
|
94
|
+
mgr = Mgr.new
|
95
|
+
|
96
|
+
p = Sidekiq::Processor.new(mgr)
|
97
|
+
JoeWorker.perform_async("boom")
|
98
|
+
q = Sidekiq::Queue.new
|
99
|
+
assert_equal 1, q.size
|
100
|
+
|
101
|
+
a = $count
|
102
|
+
mgr.mutex.synchronize do
|
103
|
+
p.start
|
104
|
+
mgr.cond.wait(mgr.mutex)
|
105
|
+
end
|
106
|
+
b = $count
|
107
|
+
assert_equal a, b
|
108
|
+
|
109
|
+
sleep 0.001
|
110
|
+
assert_equal false, p.thread.status
|
111
|
+
p.terminate(true)
|
112
|
+
refute_nil mgr.latest_error
|
113
|
+
assert_equal RuntimeError, mgr.latest_error.class
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'gracefully kills' do
|
117
|
+
mgr = Mgr.new
|
118
|
+
|
119
|
+
p = Sidekiq::Processor.new(mgr)
|
120
|
+
JoeWorker.perform_async(1)
|
121
|
+
q = Sidekiq::Queue.new
|
122
|
+
assert_equal 1, q.size
|
123
|
+
|
124
|
+
a = $count
|
125
|
+
p.start
|
126
|
+
sleep(0.02)
|
127
|
+
p.terminate
|
128
|
+
p.kill(true)
|
129
|
+
|
130
|
+
b = $count
|
131
|
+
assert_equal a, b
|
132
|
+
assert_equal false, p.thread.status
|
133
|
+
refute mgr.latest_error, mgr.latest_error.to_s
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
data/test/test_api.rb
CHANGED
@@ -1,492 +1,493 @@
|
|
1
1
|
require_relative 'helper'
|
2
|
+
require 'sidekiq/api'
|
2
3
|
|
3
4
|
class TestApi < Sidekiq::Test
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
it "is initially zero" do
|
5
|
+
describe 'api' do
|
6
|
+
before do
|
8
7
|
Sidekiq.redis {|c| c.flushdb }
|
9
|
-
s = Sidekiq::Stats.new
|
10
|
-
assert_equal 0, s.processed
|
11
|
-
assert_equal 0, s.failed
|
12
|
-
assert_equal 0, s.enqueued
|
13
8
|
end
|
14
9
|
|
15
|
-
describe "
|
16
|
-
it "
|
17
|
-
Sidekiq.redis { |conn| conn.set("stat:processed", 5) }
|
10
|
+
describe "stats" do
|
11
|
+
it "is initially zero" do
|
18
12
|
s = Sidekiq::Stats.new
|
19
|
-
assert_equal
|
13
|
+
assert_equal 0, s.processed
|
14
|
+
assert_equal 0, s.failed
|
15
|
+
assert_equal 0, s.enqueued
|
20
16
|
end
|
21
|
-
end
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
describe "reset" do
|
32
|
-
before do
|
33
|
-
Sidekiq.redis do |conn|
|
34
|
-
conn.set('stat:processed', 5)
|
35
|
-
conn.set('stat:failed', 10)
|
18
|
+
describe "processed" do
|
19
|
+
it "returns number of processed jobs" do
|
20
|
+
Sidekiq.redis { |conn| conn.set("stat:processed", 5) }
|
21
|
+
s = Sidekiq::Stats.new
|
22
|
+
assert_equal 5, s.processed
|
36
23
|
end
|
37
24
|
end
|
38
25
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
26
|
+
describe "failed" do
|
27
|
+
it "returns number of failed jobs" do
|
28
|
+
Sidekiq.redis { |conn| conn.set("stat:failed", 5) }
|
29
|
+
s = Sidekiq::Stats.new
|
30
|
+
assert_equal 5, s.failed
|
31
|
+
end
|
44
32
|
end
|
45
33
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
34
|
+
describe "reset" do
|
35
|
+
before do
|
36
|
+
Sidekiq.redis do |conn|
|
37
|
+
conn.set('stat:processed', 5)
|
38
|
+
conn.set('stat:failed', 10)
|
39
|
+
end
|
40
|
+
end
|
52
41
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
42
|
+
it 'will reset all stats by default' do
|
43
|
+
Sidekiq::Stats.new.reset
|
44
|
+
s = Sidekiq::Stats.new
|
45
|
+
assert_equal 0, s.failed
|
46
|
+
assert_equal 0, s.processed
|
47
|
+
end
|
59
48
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
49
|
+
it 'can reset individual stats' do
|
50
|
+
Sidekiq::Stats.new.reset('failed')
|
51
|
+
s = Sidekiq::Stats.new
|
52
|
+
assert_equal 0, s.failed
|
53
|
+
assert_equal 5, s.processed
|
54
|
+
end
|
67
55
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
56
|
+
it 'can accept anything that responds to #to_s' do
|
57
|
+
Sidekiq::Stats.new.reset(:failed)
|
58
|
+
s = Sidekiq::Stats.new
|
59
|
+
assert_equal 0, s.failed
|
60
|
+
assert_equal 5, s.processed
|
61
|
+
end
|
72
62
|
|
73
|
-
|
74
|
-
|
75
|
-
|
63
|
+
it 'ignores anything other than "failed" or "processed"' do
|
64
|
+
Sidekiq::Stats.new.reset((1..10).to_a, ['failed'])
|
65
|
+
s = Sidekiq::Stats.new
|
66
|
+
assert_equal 0, s.failed
|
67
|
+
assert_equal 5, s.processed
|
68
|
+
end
|
76
69
|
end
|
77
70
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
3.times { conn.rpush 'queue:bar', '{}' }
|
84
|
-
conn.sadd 'queues', 'bar'
|
71
|
+
describe "queues" do
|
72
|
+
it "is initially empty" do
|
73
|
+
s = Sidekiq::Stats::Queues.new
|
74
|
+
assert_equal 0, s.lengths.size
|
85
75
|
end
|
86
76
|
|
87
|
-
|
88
|
-
|
89
|
-
|
77
|
+
it "returns a hash of queue and size in order" do
|
78
|
+
Sidekiq.redis do |conn|
|
79
|
+
conn.rpush 'queue:foo', '{}'
|
80
|
+
conn.sadd 'queues', 'foo'
|
90
81
|
|
91
|
-
|
92
|
-
|
93
|
-
|
82
|
+
3.times { conn.rpush 'queue:bar', '{}' }
|
83
|
+
conn.sadd 'queues', 'bar'
|
84
|
+
end
|
94
85
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
conn.flushdb
|
99
|
-
conn.rpush 'queue:foo', '{}'
|
100
|
-
conn.sadd 'queues', 'foo'
|
86
|
+
s = Sidekiq::Stats::Queues.new
|
87
|
+
assert_equal ({ "foo" => 1, "bar" => 3 }), s.lengths
|
88
|
+
assert_equal "bar", s.lengths.first.first
|
101
89
|
|
102
|
-
|
103
|
-
conn.sadd 'queues', 'bar'
|
90
|
+
assert_equal Sidekiq::Stats.new.queues, Sidekiq::Stats::Queues.new.lengths
|
104
91
|
end
|
105
|
-
|
106
|
-
s = Sidekiq::Stats.new
|
107
|
-
assert_equal 4, s.enqueued
|
108
92
|
end
|
109
|
-
end
|
110
93
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
after do
|
118
|
-
DateTime::DATE_FORMATS[:default] = @before
|
119
|
-
end
|
94
|
+
describe "enqueued" do
|
95
|
+
it "returns total enqueued jobs" do
|
96
|
+
Sidekiq.redis do |conn|
|
97
|
+
conn.rpush 'queue:foo', '{}'
|
98
|
+
conn.sadd 'queues', 'foo'
|
120
99
|
|
121
|
-
|
122
|
-
|
123
|
-
Sidekiq.redis do |c|
|
124
|
-
c.incrby("stat:processed:2012-12-24", 4)
|
125
|
-
c.incrby("stat:processed:2012-12-25", 1)
|
126
|
-
c.incrby("stat:processed:2012-12-26", 6)
|
127
|
-
c.incrby("stat:processed:2012-12-27", 2)
|
100
|
+
3.times { conn.rpush 'queue:bar', '{}' }
|
101
|
+
conn.sadd 'queues', 'bar'
|
128
102
|
end
|
129
|
-
Time.stub(:now, Time.parse("2012-12-26 1:00:00 -0500")) do
|
130
|
-
s = Sidekiq::Stats::History.new(2)
|
131
|
-
assert_equal({ "2012-12-26" => 6, "2012-12-25" => 1 }, s.processed)
|
132
103
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
s = Sidekiq::Stats::History.new(2, Date.parse("2012-12-25"))
|
137
|
-
assert_equal({ "2012-12-25" => 1, "2012-12-24" => 4 }, s.processed)
|
138
|
-
end
|
104
|
+
s = Sidekiq::Stats.new
|
105
|
+
assert_equal 4, s.enqueued
|
139
106
|
end
|
140
107
|
end
|
141
108
|
|
142
|
-
describe "
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
c.incrby("stat:failed:2012-12-27", 2)
|
149
|
-
end
|
150
|
-
Time.stub(:now, Time.parse("2012-12-26 1:00:00 -0500")) do
|
151
|
-
s = Sidekiq::Stats::History.new(2)
|
152
|
-
assert_equal ({ "2012-12-26" => 6, "2012-12-25" => 1 }), s.failed
|
109
|
+
describe "over time" do
|
110
|
+
before do
|
111
|
+
require 'active_support/core_ext/time/conversions'
|
112
|
+
@before = Time::DATE_FORMATS[:default]
|
113
|
+
Time::DATE_FORMATS[:default] = "%d/%m/%Y %H:%M:%S"
|
114
|
+
end
|
153
115
|
|
154
|
-
|
155
|
-
|
116
|
+
after do
|
117
|
+
Time::DATE_FORMATS[:default] = @before
|
118
|
+
end
|
156
119
|
|
157
|
-
|
158
|
-
|
120
|
+
describe "processed" do
|
121
|
+
it 'retrieves hash of dates' do
|
122
|
+
Sidekiq.redis do |c|
|
123
|
+
c.incrby("stat:processed:2012-12-24", 4)
|
124
|
+
c.incrby("stat:processed:2012-12-25", 1)
|
125
|
+
c.incrby("stat:processed:2012-12-26", 6)
|
126
|
+
c.incrby("stat:processed:2012-12-27", 2)
|
127
|
+
end
|
128
|
+
Time.stub(:now, Time.parse("2012-12-26 1:00:00 -0500")) do
|
129
|
+
s = Sidekiq::Stats::History.new(2)
|
130
|
+
assert_equal({ "2012-12-26" => 6, "2012-12-25" => 1 }, s.processed)
|
131
|
+
|
132
|
+
s = Sidekiq::Stats::History.new(3)
|
133
|
+
assert_equal({ "2012-12-26" => 6, "2012-12-25" => 1, "2012-12-24" => 4 }, s.processed)
|
134
|
+
|
135
|
+
s = Sidekiq::Stats::History.new(2, Date.parse("2012-12-25"))
|
136
|
+
assert_equal({ "2012-12-25" => 1, "2012-12-24" => 4 }, s.processed)
|
137
|
+
end
|
159
138
|
end
|
160
139
|
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
describe 'with an empty database' do
|
166
|
-
before do
|
167
|
-
Sidekiq.redis {|c| c.flushdb }
|
168
|
-
end
|
169
140
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
141
|
+
describe "failed" do
|
142
|
+
it 'retrieves hash of dates' do
|
143
|
+
Sidekiq.redis do |c|
|
144
|
+
c.incrby("stat:failed:2012-12-24", 4)
|
145
|
+
c.incrby("stat:failed:2012-12-25", 1)
|
146
|
+
c.incrby("stat:failed:2012-12-26", 6)
|
147
|
+
c.incrby("stat:failed:2012-12-27", 2)
|
148
|
+
end
|
149
|
+
Time.stub(:now, Time.parse("2012-12-26 1:00:00 -0500")) do
|
150
|
+
s = Sidekiq::Stats::History.new(2)
|
151
|
+
assert_equal ({ "2012-12-26" => 6, "2012-12-25" => 1 }), s.failed
|
152
|
+
|
153
|
+
s = Sidekiq::Stats::History.new(3)
|
154
|
+
assert_equal ({ "2012-12-26" => 6, "2012-12-25" => 1, "2012-12-24" => 4 }), s.failed
|
155
|
+
|
156
|
+
s = Sidekiq::Stats::History.new(2, Date.parse("2012-12-25"))
|
157
|
+
assert_equal ({ "2012-12-25" => 1, "2012-12-24" => 4 }), s.failed
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
178
162
|
end
|
179
163
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
assert_equal
|
185
|
-
|
186
|
-
job = q.first
|
187
|
-
assert_equal 24, job.jid.size
|
188
|
-
assert_equal [1, 'mike'], job.args
|
189
|
-
assert_equal Time.new(2012, 12, 26), job.enqueued_at
|
164
|
+
describe 'with an empty database' do
|
165
|
+
it 'shows queue as empty' do
|
166
|
+
q = Sidekiq::Queue.new
|
167
|
+
assert_equal 0, q.size
|
168
|
+
assert_equal 0, q.latency
|
190
169
|
end
|
191
170
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
assert_equal 0, q.size
|
196
|
-
end
|
171
|
+
class ApiWorker
|
172
|
+
include Sidekiq::Worker
|
173
|
+
end
|
197
174
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
175
|
+
it 'can enumerate jobs' do
|
176
|
+
q = Sidekiq::Queue.new
|
177
|
+
Time.stub(:now, Time.new(2012, 12, 26)) do
|
178
|
+
ApiWorker.perform_async(1, 'mike')
|
179
|
+
assert_equal ['TestApi::ApiWorker'], q.map(&:klass)
|
203
180
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
end
|
181
|
+
job = q.first
|
182
|
+
assert_equal 24, job.jid.size
|
183
|
+
assert_equal [1, 'mike'], job.args
|
184
|
+
assert_equal Time.new(2012, 12, 26), job.enqueued_at
|
185
|
+
end
|
186
|
+
assert q.latency > 10_000_000
|
211
187
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
assert_equal 1, q.size
|
188
|
+
q = Sidekiq::Queue.new('other')
|
189
|
+
assert_equal 0, q.size
|
190
|
+
end
|
216
191
|
|
217
|
-
|
218
|
-
|
219
|
-
|
192
|
+
it 'has no enqueued_at time for jobs enqueued in the future' do
|
193
|
+
job_id = ApiWorker.perform_in(100, 1, 'foo')
|
194
|
+
job = Sidekiq::ScheduledSet.new.find_job(job_id)
|
195
|
+
assert_nil job.enqueued_at
|
196
|
+
end
|
220
197
|
|
221
|
-
|
222
|
-
|
223
|
-
|
198
|
+
it 'unwraps delayed jobs' do
|
199
|
+
Sidekiq::Queue.delay.foo(1,2,3)
|
200
|
+
q = Sidekiq::Queue.new
|
201
|
+
x = q.first
|
202
|
+
assert_equal "Sidekiq::Queue.foo", x.display_class
|
203
|
+
assert_equal [1,2,3], x.display_args
|
204
|
+
end
|
224
205
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
job.add_to_queue
|
231
|
-
queued_job = q.find_job(job_id)
|
232
|
-
refute_nil queued_job
|
233
|
-
assert_equal queued_job.jid, job_id
|
234
|
-
assert_nil Sidekiq::ScheduledSet.new.find_job(job_id)
|
235
|
-
refute_nil Sidekiq::ScheduledSet.new.find_job(remain_id)
|
236
|
-
end
|
206
|
+
it 'has no enqueued_at time for jobs enqueued in the future' do
|
207
|
+
job_id = ApiWorker.perform_in(100, 1, 'foo')
|
208
|
+
job = Sidekiq::ScheduledSet.new.find_job(job_id)
|
209
|
+
assert_nil job.enqueued_at
|
210
|
+
end
|
237
211
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
assert_equal 2, jids.size
|
243
|
-
(remain_id, job_id) = jids
|
244
|
-
job = Sidekiq::ScheduledSet.new.find_job(job_id)
|
245
|
-
q = Sidekiq::Queue.new
|
246
|
-
job.add_to_queue
|
247
|
-
queued_job = q.find_job(job_id)
|
248
|
-
refute_nil queued_job
|
249
|
-
assert_equal queued_job.jid, job_id
|
250
|
-
assert_nil Sidekiq::ScheduledSet.new.find_job(job_id)
|
251
|
-
refute_nil Sidekiq::ScheduledSet.new.find_job(remain_id)
|
252
|
-
end
|
212
|
+
it 'can delete jobs' do
|
213
|
+
q = Sidekiq::Queue.new
|
214
|
+
ApiWorker.perform_async(1, 'mike')
|
215
|
+
assert_equal 1, q.size
|
253
216
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
refute_nil job
|
258
|
-
assert_equal job_id, job.jid
|
259
|
-
assert_in_delta job.latency, 0.0, 0.1
|
260
|
-
end
|
217
|
+
x = q.first
|
218
|
+
assert_equal "TestApi::ApiWorker", x.display_class
|
219
|
+
assert_equal [1,'mike'], x.display_args
|
261
220
|
|
262
|
-
|
263
|
-
|
264
|
-
51.times do
|
265
|
-
ApiWorker.perform_in(100, 'aaron')
|
221
|
+
assert_equal [true], q.map(&:delete)
|
222
|
+
assert_equal 0, q.size
|
266
223
|
end
|
267
|
-
set = Sidekiq::ScheduledSet.new
|
268
|
-
set.map(&:delete)
|
269
|
-
assert_equal set.size, 0
|
270
|
-
end
|
271
224
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
225
|
+
it "can move scheduled job to queue" do
|
226
|
+
remain_id = ApiWorker.perform_in(100, 1, 'jason')
|
227
|
+
job_id = ApiWorker.perform_in(100, 1, 'jason')
|
228
|
+
job = Sidekiq::ScheduledSet.new.find_job(job_id)
|
229
|
+
q = Sidekiq::Queue.new
|
230
|
+
job.add_to_queue
|
231
|
+
queued_job = q.find_job(job_id)
|
232
|
+
refute_nil queued_job
|
233
|
+
assert_equal queued_job.jid, job_id
|
234
|
+
assert_nil Sidekiq::ScheduledSet.new.find_job(job_id)
|
235
|
+
refute_nil Sidekiq::ScheduledSet.new.find_job(remain_id)
|
276
236
|
end
|
277
|
-
q = Sidekiq::Queue.new
|
278
|
-
q.map(&:delete)
|
279
|
-
assert_equal q.size, 0
|
280
|
-
end
|
281
237
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
238
|
+
it "handles multiple scheduled jobs when moving to queue" do
|
239
|
+
jids = Sidekiq::Client.push_bulk('class' => ApiWorker,
|
240
|
+
'args' => [[1, 'jason'], [2, 'jason']],
|
241
|
+
'at' => Time.now.to_f)
|
242
|
+
assert_equal 2, jids.size
|
243
|
+
(remain_id, job_id) = jids
|
244
|
+
job = Sidekiq::ScheduledSet.new.find_job(job_id)
|
245
|
+
q = Sidekiq::Queue.new
|
246
|
+
job.add_to_queue
|
247
|
+
queued_job = q.find_job(job_id)
|
248
|
+
refute_nil queued_job
|
249
|
+
assert_equal queued_job.jid, job_id
|
250
|
+
assert_nil Sidekiq::ScheduledSet.new.find_job(job_id)
|
251
|
+
refute_nil Sidekiq::ScheduledSet.new.find_job(remain_id)
|
252
|
+
end
|
289
253
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
254
|
+
it 'can find job by id in sorted sets' do
|
255
|
+
job_id = ApiWorker.perform_in(100, 1, 'jason')
|
256
|
+
job = Sidekiq::ScheduledSet.new.find_job(job_id)
|
257
|
+
refute_nil job
|
258
|
+
assert_equal job_id, job.jid
|
259
|
+
assert_in_delta job.latency, 0.0, 0.1
|
260
|
+
end
|
294
261
|
|
295
|
-
|
296
|
-
|
297
|
-
|
262
|
+
it 'can remove jobs when iterating over a sorted set' do
|
263
|
+
# scheduled jobs must be greater than SortedSet#each underlying page size
|
264
|
+
51.times do
|
265
|
+
ApiWorker.perform_in(100, 'aaron')
|
266
|
+
end
|
267
|
+
set = Sidekiq::ScheduledSet.new
|
268
|
+
set.map(&:delete)
|
269
|
+
assert_equal set.size, 0
|
298
270
|
end
|
299
|
-
end
|
300
271
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
272
|
+
it 'can remove jobs when iterating over a queue' do
|
273
|
+
# initial queue size must be greater than Queue#each underlying page size
|
274
|
+
51.times do
|
275
|
+
ApiWorker.perform_async(1, 'aaron')
|
276
|
+
end
|
277
|
+
q = Sidekiq::Queue.new
|
278
|
+
q.map(&:delete)
|
279
|
+
assert_equal q.size, 0
|
280
|
+
end
|
308
281
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
282
|
+
it 'can find job by id in queues' do
|
283
|
+
q = Sidekiq::Queue.new
|
284
|
+
job_id = ApiWorker.perform_async(1, 'jason')
|
285
|
+
job = q.find_job(job_id)
|
286
|
+
refute_nil job
|
287
|
+
assert_equal job_id, job.jid
|
288
|
+
end
|
316
289
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
290
|
+
it 'can clear a queue' do
|
291
|
+
q = Sidekiq::Queue.new
|
292
|
+
2.times { ApiWorker.perform_async(1, 'mike') }
|
293
|
+
q.clear
|
321
294
|
|
322
|
-
|
323
|
-
|
295
|
+
Sidekiq.redis do |conn|
|
296
|
+
refute conn.smembers('queues').include?('foo')
|
297
|
+
refute conn.exists('queue:foo')
|
298
|
+
end
|
299
|
+
end
|
324
300
|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
301
|
+
it 'can fetch by score' do
|
302
|
+
same_time = Time.now.to_f
|
303
|
+
add_retry('bob1', same_time)
|
304
|
+
add_retry('bob2', same_time)
|
305
|
+
r = Sidekiq::RetrySet.new
|
306
|
+
assert_equal 2, r.fetch(same_time).size
|
307
|
+
end
|
329
308
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
309
|
+
it 'can fetch by score and jid' do
|
310
|
+
same_time = Time.now.to_f
|
311
|
+
add_retry('bob1', same_time)
|
312
|
+
add_retry('bob2', same_time)
|
313
|
+
r = Sidekiq::RetrySet.new
|
314
|
+
assert_equal 1, r.fetch(same_time, 'bob1').size
|
315
|
+
end
|
336
316
|
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
assert_raises(ArgumentError) do
|
341
|
-
Sidekiq::RetrySet.new.delete(start_time)
|
317
|
+
it 'shows empty retries' do
|
318
|
+
r = Sidekiq::RetrySet.new
|
319
|
+
assert_equal 0, r.size
|
342
320
|
end
|
343
|
-
end
|
344
321
|
|
345
|
-
|
346
|
-
|
347
|
-
add_retry('bob1', same_time)
|
348
|
-
add_retry('bob2', same_time)
|
349
|
-
r = Sidekiq::RetrySet.new
|
350
|
-
assert_equal 2, r.size
|
351
|
-
Sidekiq::RetrySet.new.delete(same_time, 'bob1')
|
352
|
-
assert_equal 1, r.size
|
353
|
-
end
|
322
|
+
it 'can enumerate retries' do
|
323
|
+
add_retry
|
354
324
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
r.first.retry
|
360
|
-
assert_equal 0, r.size
|
361
|
-
assert_equal 1, Sidekiq::Queue.new('default').size
|
362
|
-
job = Sidekiq::Queue.new('default').first
|
363
|
-
assert_equal 'bob', job.jid
|
364
|
-
assert_equal 1, job['retry_count']
|
365
|
-
end
|
325
|
+
r = Sidekiq::RetrySet.new
|
326
|
+
assert_equal 1, r.size
|
327
|
+
array = r.to_a
|
328
|
+
assert_equal 1, array.size
|
366
329
|
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
assert_equal 0, r.size
|
374
|
-
end
|
330
|
+
retri = array.first
|
331
|
+
assert_equal 'ApiWorker', retri.klass
|
332
|
+
assert_equal 'default', retri.queue
|
333
|
+
assert_equal 'bob', retri.jid
|
334
|
+
assert_in_delta Time.now.to_f, retri.at.to_f, 0.02
|
335
|
+
end
|
375
336
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
'key' => identity_string,
|
382
|
-
'identity' => identity_string,
|
383
|
-
'started_at' => Time.now.to_f - 15,
|
384
|
-
}
|
385
|
-
|
386
|
-
time = Time.now.to_f
|
387
|
-
Sidekiq.redis do |conn|
|
388
|
-
conn.multi do
|
389
|
-
conn.sadd('processes', odata['key'])
|
390
|
-
conn.hmset(odata['key'], 'info', Sidekiq.dump_json(odata), 'busy', 10, 'beat', time)
|
391
|
-
conn.sadd('processes', 'fake:pid')
|
337
|
+
it 'requires a jid to delete an entry' do
|
338
|
+
start_time = Time.now.to_f
|
339
|
+
add_retry('bob2', Time.now.to_f)
|
340
|
+
assert_raises(ArgumentError) do
|
341
|
+
Sidekiq::RetrySet.new.delete(start_time)
|
392
342
|
end
|
393
343
|
end
|
394
344
|
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
signals_string = "#{odata['key']}-signals"
|
404
|
-
assert_equal "TERM", Sidekiq.redis{|c| c.lpop(signals_string) }
|
405
|
-
assert_equal "USR1", Sidekiq.redis{|c| c.lpop(signals_string) }
|
406
|
-
end
|
407
|
-
|
408
|
-
it 'can enumerate workers' do
|
409
|
-
w = Sidekiq::Workers.new
|
410
|
-
assert_equal 0, w.size
|
411
|
-
w.each do
|
412
|
-
assert false
|
345
|
+
it 'can delete a single retry from score and jid' do
|
346
|
+
same_time = Time.now.to_f
|
347
|
+
add_retry('bob1', same_time)
|
348
|
+
add_retry('bob2', same_time)
|
349
|
+
r = Sidekiq::RetrySet.new
|
350
|
+
assert_equal 2, r.size
|
351
|
+
Sidekiq::RetrySet.new.delete(same_time, 'bob1')
|
352
|
+
assert_equal 1, r.size
|
413
353
|
end
|
414
354
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
355
|
+
it 'can retry a retry' do
|
356
|
+
add_retry
|
357
|
+
r = Sidekiq::RetrySet.new
|
358
|
+
assert_equal 1, r.size
|
359
|
+
r.first.retry
|
360
|
+
assert_equal 0, r.size
|
361
|
+
assert_equal 1, Sidekiq::Queue.new('default').size
|
362
|
+
job = Sidekiq::Queue.new('default').first
|
363
|
+
assert_equal 'bob', job.jid
|
364
|
+
assert_equal 1, job['retry_count']
|
421
365
|
end
|
422
366
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
367
|
+
it 'can clear retries' do
|
368
|
+
add_retry
|
369
|
+
add_retry('test')
|
370
|
+
r = Sidekiq::RetrySet.new
|
371
|
+
assert_equal 2, r.size
|
372
|
+
r.clear
|
373
|
+
assert_equal 0, r.size
|
427
374
|
end
|
428
375
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
376
|
+
it 'can enumerate processes' do
|
377
|
+
identity_string = "identity_string"
|
378
|
+
odata = {
|
379
|
+
'pid' => 123,
|
380
|
+
'hostname' => Socket.gethostname,
|
381
|
+
'key' => identity_string,
|
382
|
+
'identity' => identity_string,
|
383
|
+
'started_at' => Time.now.to_f - 15,
|
384
|
+
}
|
385
|
+
|
386
|
+
time = Time.now.to_f
|
387
|
+
Sidekiq.redis do |conn|
|
388
|
+
conn.multi do
|
389
|
+
conn.sadd('processes', odata['key'])
|
390
|
+
conn.hmset(odata['key'], 'info', Sidekiq.dump_json(odata), 'busy', 10, 'beat', time)
|
391
|
+
conn.sadd('processes', 'fake:pid')
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
ps = Sidekiq::ProcessSet.new.to_a
|
396
|
+
assert_equal 1, ps.size
|
397
|
+
data = ps.first
|
398
|
+
assert_equal 10, data['busy']
|
399
|
+
assert_equal time, data['beat']
|
400
|
+
assert_equal 123, data['pid']
|
401
|
+
data.quiet!
|
402
|
+
data.stop!
|
403
|
+
signals_string = "#{odata['key']}-signals"
|
404
|
+
assert_equal "TERM", Sidekiq.redis{|c| c.lpop(signals_string) }
|
405
|
+
assert_equal "USR1", Sidekiq.redis{|c| c.lpop(signals_string) }
|
434
406
|
end
|
435
407
|
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
c.hmset("b#{s}", '5678', data)
|
408
|
+
it 'can enumerate workers' do
|
409
|
+
w = Sidekiq::Workers.new
|
410
|
+
assert_equal 0, w.size
|
411
|
+
w.each do
|
412
|
+
assert false
|
442
413
|
end
|
443
|
-
end
|
444
414
|
|
445
|
-
|
446
|
-
|
415
|
+
hn = Socket.gethostname
|
416
|
+
key = "#{hn}:#{$$}"
|
417
|
+
pdata = { 'pid' => $$, 'hostname' => hn, 'started_at' => Time.now.to_i }
|
418
|
+
Sidekiq.redis do |conn|
|
419
|
+
conn.sadd('processes', key)
|
420
|
+
conn.hmset(key, 'info', Sidekiq.dump_json(pdata), 'busy', 0, 'beat', Time.now.to_f)
|
421
|
+
end
|
447
422
|
|
448
|
-
|
449
|
-
|
450
|
-
|
423
|
+
s = "#{key}:workers"
|
424
|
+
data = Sidekiq.dump_json({ 'payload' => {}, 'queue' => 'default', 'run_at' => Time.now.to_i })
|
425
|
+
Sidekiq.redis do |c|
|
426
|
+
c.hmset(s, '1234', data)
|
427
|
+
end
|
451
428
|
|
452
|
-
|
453
|
-
|
454
|
-
|
429
|
+
w.each do |p, x, y|
|
430
|
+
assert_equal key, p
|
431
|
+
assert_equal "1234", x
|
432
|
+
assert_equal 'default', y['queue']
|
433
|
+
assert_equal Time.now.year, Time.at(y['run_at']).year
|
434
|
+
end
|
455
435
|
|
456
|
-
|
457
|
-
|
436
|
+
s = "#{key}:workers"
|
437
|
+
data = Sidekiq.dump_json({ 'payload' => {}, 'queue' => 'default', 'run_at' => (Time.now.to_i - 2*60*60) })
|
438
|
+
Sidekiq.redis do |c|
|
439
|
+
c.multi do
|
440
|
+
c.hmset(s, '5678', data)
|
441
|
+
c.hmset("b#{s}", '5678', data)
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
assert_equal ['1234', '5678'], w.map { |_, tid, _| tid }
|
458
446
|
end
|
459
447
|
|
460
|
-
|
461
|
-
|
462
|
-
|
448
|
+
it 'can reschedule jobs' do
|
449
|
+
add_retry('foo1')
|
450
|
+
add_retry('foo2')
|
463
451
|
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
Sidekiq.redis do |conn|
|
468
|
-
conn.sadd('processes', key)
|
469
|
-
conn.hmset(key, 'info', Sidekiq.dump_json(data), 'busy', 0, 'beat', Time.now.to_f)
|
470
|
-
end
|
452
|
+
retries = Sidekiq::RetrySet.new
|
453
|
+
assert_equal 2, retries.size
|
454
|
+
refute(retries.map { |r| r.score > (Time.now.to_f + 9) }.any?)
|
471
455
|
|
472
|
-
|
473
|
-
|
474
|
-
|
456
|
+
retries.each do |retri|
|
457
|
+
retri.reschedule(Time.now.to_f + 10) if retri.jid == 'foo2'
|
458
|
+
end
|
475
459
|
|
476
|
-
|
477
|
-
|
478
|
-
conn.sadd('processes', "bar:986")
|
460
|
+
assert_equal 2, retries.size
|
461
|
+
assert(retries.map { |r| r.score > (Time.now.to_f + 9) }.any?)
|
479
462
|
end
|
480
463
|
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
464
|
+
it 'prunes processes which have died' do
|
465
|
+
data = { 'pid' => rand(10_000), 'hostname' => "app#{rand(1_000)}", 'started_at' => Time.now.to_f }
|
466
|
+
key = "#{data['hostname']}:#{data['pid']}"
|
467
|
+
Sidekiq.redis do |conn|
|
468
|
+
conn.sadd('processes', key)
|
469
|
+
conn.hmset(key, 'info', Sidekiq.dump_json(data), 'busy', 0, 'beat', Time.now.to_f)
|
470
|
+
end
|
485
471
|
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
472
|
+
ps = Sidekiq::ProcessSet.new
|
473
|
+
assert_equal 1, ps.size
|
474
|
+
assert_equal 1, ps.to_a.size
|
475
|
+
|
476
|
+
Sidekiq.redis do |conn|
|
477
|
+
conn.sadd('processes', "bar:987")
|
478
|
+
conn.sadd('processes', "bar:986")
|
479
|
+
end
|
480
|
+
|
481
|
+
ps = Sidekiq::ProcessSet.new
|
482
|
+
assert_equal 1, ps.size
|
483
|
+
assert_equal 1, ps.to_a.size
|
484
|
+
end
|
485
|
+
|
486
|
+
def add_retry(jid = 'bob', at = Time.now.to_f)
|
487
|
+
payload = Sidekiq.dump_json('class' => 'ApiWorker', 'args' => [1, 'mike'], 'queue' => 'default', 'jid' => jid, 'retry_count' => 2, 'failed_at' => Time.now.to_f)
|
488
|
+
Sidekiq.redis do |conn|
|
489
|
+
conn.zadd('retry', at.to_s, payload)
|
490
|
+
end
|
490
491
|
end
|
491
492
|
end
|
492
493
|
end
|