sidekiq 3.5.4 → 4.0.0.pre1
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 +40 -0
- data/Changes.md +5 -8
- data/Ent-Changes.md +6 -0
- data/Gemfile +2 -1
- data/Pro-3.0-Upgrade.md +46 -0
- data/bin/sidekiqctl +3 -3
- data/bin/sidekiqload +17 -6
- data/lib/sidekiq.rb +24 -11
- data/lib/sidekiq/cli.rb +19 -28
- 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 +71 -181
- data/lib/sidekiq/middleware/server/retry_jobs.rb +1 -1
- data/lib/sidekiq/processor.rb +119 -96
- data/lib/sidekiq/redis_connection.rb +22 -4
- data/lib/sidekiq/scheduled.rb +47 -26
- data/lib/sidekiq/util.rb +7 -0
- data/lib/sidekiq/version.rb +1 -1
- data/sidekiq.gemspec +1 -1
- 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
- metadata +19 -14
- data/lib/sidekiq/actor.rb +0 -39
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/sidekiq.gemspec
CHANGED
@@ -18,8 +18,8 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.add_dependency 'redis', '~> 3.2', '>= 3.2.1'
|
19
19
|
gem.add_dependency 'redis-namespace', '~> 1.5', '>= 1.5.2'
|
20
20
|
gem.add_dependency 'connection_pool', '~> 2.2', '>= 2.2.0'
|
21
|
-
gem.add_dependency 'celluloid', '~> 0.17.2'
|
22
21
|
gem.add_dependency 'json', '~> 1.0'
|
22
|
+
gem.add_dependency 'concurrent-ruby', '1.0.0.pre4'
|
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
|