sidekiq_queue_metrics 2.1.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +1 -0
- data/Gemfile +6 -2
- data/lib/sidekiq_queue_metrics/configuration.rb +14 -2
- data/lib/sidekiq_queue_metrics/helpers.rb +43 -0
- data/lib/sidekiq_queue_metrics/job_death_middleware.rb +17 -0
- data/lib/sidekiq_queue_metrics/monitor/job_death_monitor.rb +1 -1
- data/lib/sidekiq_queue_metrics/monitor/job_success_monitor.rb +1 -1
- data/lib/sidekiq_queue_metrics/monitor/monitor.rb +4 -18
- data/lib/sidekiq_queue_metrics/queue_metrics.rb +16 -17
- data/lib/sidekiq_queue_metrics/storage.rb +16 -20
- data/lib/sidekiq_queue_metrics/upgrade_manager.rb +76 -0
- data/lib/sidekiq_queue_metrics/version.rb +1 -1
- data/lib/sidekiq_queue_metrics/views/queue_summary.erb +2 -2
- data/lib/sidekiq_queue_metrics/views/queues_stats.erb +2 -2
- data/lib/sidekiq_queue_metrics/web_extension.rb +4 -10
- data/sidekiq_queue_metrics.gemspec +5 -2
- data/spec/lib/sidekiq_queue_metrics/helpers_spec.rb +107 -0
- data/spec/lib/sidekiq_queue_metrics/job_death_middleware_spec.rb +39 -0
- data/spec/lib/sidekiq_queue_metrics/monitor/job_death_monitor_spec.rb +41 -24
- data/spec/lib/sidekiq_queue_metrics/monitor/job_success_monitor_spec.rb +36 -22
- data/spec/lib/sidekiq_queue_metrics/queue_metrics_spec.rb +59 -12
- data/spec/lib/sidekiq_queue_metrics/storage_spec.rb +21 -51
- data/spec/lib/sidekiq_queue_metrics/upgrade_manager_spec.rb +87 -0
- data/spec/spec_helper.rb +2 -0
- metadata +42 -7
- data/Gemfile.lock +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9513e994c0d230463b79ed7a2d43e47b6159ea9d5ac36b3288d3da3b2f07f091
|
4
|
+
data.tar.gz: c6c488224f22b3b74038312c6ba273af65489aa04ccc1e68057389088aa682dc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df033456251c68174d066d57bdbdf6acd122554d03e601735b79c98524d573083e5b92880c18abe3516ede22f7d56eec682995e41f2b97c76f9bbe3b95b5b414
|
7
|
+
data.tar.gz: 855585c740c62a970edef6222d7d13cad47a02528e5856441b66f452de9137461c88c6d01c2c271ccb189338f65d63dbae985ea8a7ded80f3ea87084e2a8f78b
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -1,10 +1,22 @@
|
|
1
1
|
module Sidekiq::QueueMetrics
|
2
|
+
def self.support_death_handlers?
|
3
|
+
Sidekiq::VERSION >= '5.1'
|
4
|
+
end
|
5
|
+
|
2
6
|
def self.init(config)
|
3
7
|
config.server_middleware do |chain|
|
4
8
|
chain.add Sidekiq::QueueMetrics::JobSuccessMonitor
|
5
9
|
end
|
6
10
|
|
7
|
-
config.
|
11
|
+
config.on(:startup) { UpgradeManager.upgrade_if_needed }
|
12
|
+
|
13
|
+
if support_death_handlers?
|
14
|
+
config.death_handlers << Sidekiq::QueueMetrics::JobDeathMonitor.proc
|
15
|
+
else
|
16
|
+
config.server_middleware do |chain|
|
17
|
+
chain.add Sidekiq::QueueMetrics::JobDeathMiddleware
|
18
|
+
end
|
19
|
+
end
|
8
20
|
end
|
9
21
|
|
10
22
|
def self.storage_location=(key)
|
@@ -22,4 +34,4 @@ module Sidekiq::QueueMetrics
|
|
22
34
|
def self.storage_location
|
23
35
|
@storage_location
|
24
36
|
end
|
25
|
-
end
|
37
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Sidekiq::QueueMetrics
|
2
|
+
module Helpers
|
3
|
+
FAILED_JOBS_KEY = 'failed_jobs'.freeze
|
4
|
+
|
5
|
+
def self.build_queue_stats_key(queue)
|
6
|
+
"#{stats_key}:#{queue}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.build_failed_jobs_key(queue)
|
10
|
+
"#{FAILED_JOBS_KEY}:#{queue}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.stats_key
|
14
|
+
Sidekiq::QueueMetrics.storage_location || 'queue_stats'
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.convert_hash_values(original_hash, &block)
|
18
|
+
original_hash.reduce({}) do |result, (k,v)|
|
19
|
+
result[k] = case v
|
20
|
+
when Array then v
|
21
|
+
when Hash then convert_hash_values(v, &block)
|
22
|
+
else block.(v)
|
23
|
+
end
|
24
|
+
|
25
|
+
result
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.build_metrics_for_view(last_metrics, current_metrics)
|
30
|
+
current_metrics.each_with_object({}) do |(queue, metric), new_queue_metrics|
|
31
|
+
new_queue_metrics[queue] = metric.each_with_object({}) do |(name, count), updated_metrics|
|
32
|
+
previous_metric_value = last_metrics[queue] ? last_metrics[queue][name] : nil
|
33
|
+
animate = !previous_metric_value.nil? && previous_metric_value != count
|
34
|
+
|
35
|
+
updated_metrics[name] = {
|
36
|
+
'count' => count,
|
37
|
+
'animate' => animate
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Sidekiq::QueueMetrics
|
2
|
+
class JobDeathMiddleware
|
3
|
+
def call(worker, msg, queue)
|
4
|
+
call_dead_monitor(msg) if is_dead_job?(msg)
|
5
|
+
|
6
|
+
yield if block_given?
|
7
|
+
end
|
8
|
+
|
9
|
+
def is_dead_job?(msg)
|
10
|
+
msg.key?('retry_count') && msg['retry_count'] == 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def call_dead_monitor(msg)
|
14
|
+
Sidekiq::QueueMetrics::JobDeathMonitor.proc.call(msg, msg['error_class'])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -4,25 +4,11 @@ require 'sidekiq_queue_metrics/storage'
|
|
4
4
|
module Sidekiq::QueueMetrics
|
5
5
|
class Monitor
|
6
6
|
def monitor(queue)
|
7
|
-
|
8
|
-
stats[queue] ||= {}
|
9
|
-
|
10
|
-
if stats[queue][status_counter].nil?
|
11
|
-
stats[queue][status_counter] = 1
|
12
|
-
else
|
13
|
-
stats[queue][status_counter] += 1
|
14
|
-
end
|
15
|
-
|
16
|
-
Storage.set_stats(stats.to_json)
|
17
|
-
end
|
18
|
-
|
19
|
-
protected
|
20
|
-
def status_counter
|
7
|
+
Storage.increment_stat(queue, status_counter)
|
21
8
|
end
|
22
9
|
|
23
|
-
|
24
|
-
|
25
|
-
JSON.load(Storage.get_stats || '{}')
|
10
|
+
protected def status_counter
|
11
|
+
fail 'This method should be implemented by child monitors'
|
26
12
|
end
|
27
13
|
end
|
28
|
-
end
|
14
|
+
end
|
@@ -6,34 +6,34 @@ module Sidekiq::QueueMetrics
|
|
6
6
|
class << self
|
7
7
|
def fetch
|
8
8
|
queues = []
|
9
|
-
|
9
|
+
enqueued_jobs = scheduled_jobs = retry_stats = {}
|
10
|
+
|
10
11
|
together do
|
11
12
|
async do
|
12
13
|
queues = Sidekiq::Queue.all.map(&:name).map(&:to_s)
|
13
14
|
queues.each {|queue| enqueued_jobs[queue] = fetch_enqueued_jobs(queue)}
|
14
15
|
end
|
15
16
|
|
16
|
-
async {success_and_failed_stats = fetch_success_and_failed_stats}
|
17
17
|
async {retry_stats = fetch_retry_stats}
|
18
18
|
async {scheduled_jobs = fetch_scheduled_stats}
|
19
19
|
end
|
20
20
|
|
21
|
-
queues.
|
22
|
-
stats = {
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
queues.reduce({}) do |stats, queue|
|
22
|
+
stats[queue] = {
|
23
|
+
'enqueued' => val_or_default(enqueued_jobs[queue]),
|
24
|
+
'in_retry' => val_or_default(retry_stats[queue]),
|
25
|
+
'scheduled' => val_or_default(scheduled_jobs[queue])
|
26
|
+
}.merge(fetch_success_and_failed_stats(queue))
|
27
27
|
|
28
|
-
stats
|
29
|
-
|
30
|
-
stats['scheduled'] = val_or_default(scheduled_jobs[queue])
|
31
|
-
{queue => stats}
|
32
|
-
end.reduce({}, :merge)
|
28
|
+
stats
|
29
|
+
end
|
33
30
|
end
|
34
31
|
|
35
|
-
def fetch_success_and_failed_stats
|
36
|
-
|
32
|
+
def fetch_success_and_failed_stats(queue)
|
33
|
+
default_metric_values = { 'processed' => 0, 'failed' => 0 }
|
34
|
+
default_metric_values.merge(
|
35
|
+
Sidekiq::QueueMetrics::Storage.get_stats(queue)
|
36
|
+
)
|
37
37
|
end
|
38
38
|
|
39
39
|
def fetch_enqueued_jobs(queue)
|
@@ -52,8 +52,7 @@ module Sidekiq::QueueMetrics
|
|
52
52
|
Storage.failed_jobs(queue).reverse
|
53
53
|
end
|
54
54
|
|
55
|
-
private
|
56
|
-
def val_or_default(val, default = 0)
|
55
|
+
private def val_or_default(val, default = 0)
|
57
56
|
val || default
|
58
57
|
end
|
59
58
|
end
|
@@ -1,42 +1,38 @@
|
|
1
1
|
module Sidekiq::QueueMetrics
|
2
2
|
class Storage
|
3
|
-
FAILED_JOBS_KEY = 'failed_jobs'.freeze
|
4
|
-
|
5
3
|
class << self
|
6
|
-
def
|
4
|
+
def increment_stat(queue, stat, value = 1)
|
7
5
|
Sidekiq.redis_pool.with do |conn|
|
8
|
-
conn.
|
6
|
+
conn.hincrby(Helpers.build_queue_stats_key(queue), stat, value)
|
9
7
|
end
|
10
8
|
end
|
11
9
|
|
12
|
-
def get_stats(
|
13
|
-
Sidekiq.redis_pool.with do |conn|
|
14
|
-
conn.
|
10
|
+
def get_stats(queue)
|
11
|
+
stats = Sidekiq.redis_pool.with do |conn|
|
12
|
+
conn.hgetall(Helpers.build_queue_stats_key(queue))
|
15
13
|
end
|
14
|
+
|
15
|
+
Helpers.convert_hash_values(stats) { |value| value.to_i }
|
16
16
|
end
|
17
17
|
|
18
18
|
def add_failed_job(job, max_count = Sidekiq::QueueMetrics.max_recently_failed_jobs)
|
19
|
-
|
20
|
-
queue = job['queue']
|
21
|
-
failed_jobs = JSON.parse(conn.get("#{FAILED_JOBS_KEY}:#{queue}") || '[]')
|
19
|
+
queue = job['queue']
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
21
|
+
Sidekiq.redis_pool.with do |conn|
|
22
|
+
failed_job_key_for_queue = Helpers.build_failed_jobs_key(queue)
|
26
23
|
|
27
|
-
conn.
|
24
|
+
conn.lpush(failed_job_key_for_queue, Sidekiq.dump_json(job))
|
25
|
+
conn.rpop(failed_job_key_for_queue) if conn.llen(failed_job_key_for_queue) >= max_count
|
28
26
|
end
|
29
27
|
end
|
30
28
|
|
31
29
|
def failed_jobs(queue)
|
32
|
-
Sidekiq.redis_pool.with do |conn|
|
33
|
-
|
30
|
+
result = Sidekiq.redis_pool.with do |conn|
|
31
|
+
conn.lrange(Helpers.build_failed_jobs_key(queue), 0, -1)
|
34
32
|
end
|
35
|
-
end
|
36
33
|
|
37
|
-
|
38
|
-
Sidekiq::QueueMetrics.storage_location || 'queue_stats'
|
34
|
+
result.map(&Sidekiq.method(:load_json))
|
39
35
|
end
|
40
36
|
end
|
41
37
|
end
|
42
|
-
end
|
38
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'redlock'
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module QueueMetrics
|
5
|
+
class UpgradeManager
|
6
|
+
def self.logger
|
7
|
+
@@logger ||= Logger.new(STDOUT)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Check if an upgrade is needed and it's not already in progress.
|
11
|
+
# If it's in progress, it will block during that time waiting for the upgrade to complete.
|
12
|
+
#
|
13
|
+
# In case the lock is not released because the upgrade is taking too long
|
14
|
+
# it will raise an exception
|
15
|
+
#
|
16
|
+
# @raises [Redlock::LockError]
|
17
|
+
#
|
18
|
+
def self.upgrade_if_needed
|
19
|
+
acquire_lock do
|
20
|
+
return unless upgrade_needed?
|
21
|
+
|
22
|
+
v2_to_v3_upgrade
|
23
|
+
end
|
24
|
+
rescue Redlock::LockError
|
25
|
+
fail 'A long running upgrade is in progress. Try restarting the application once finished'
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.v2_to_v3_upgrade
|
29
|
+
logger.info('Starting sidekiq_queue_metrics v3 upgrade')
|
30
|
+
|
31
|
+
Sidekiq.redis_pool.with do |conn|
|
32
|
+
old_collected_metrics = JSON.load(conn.get(Helpers.stats_key))
|
33
|
+
old_collected_metrics.each do |(queue, stats)|
|
34
|
+
logger.info("Upgrading #{queue} statistics")
|
35
|
+
|
36
|
+
stats.each { |(stat, value)| Sidekiq::QueueMetrics::Storage.increment_stat(queue, stat, value) }
|
37
|
+
|
38
|
+
failed_jobs_key = Helpers.build_failed_jobs_key(queue)
|
39
|
+
|
40
|
+
if conn.exists(failed_jobs_key) && conn.type(failed_jobs_key) == 'string'
|
41
|
+
temporal_failed_key = "_#{failed_jobs_key}"
|
42
|
+
|
43
|
+
failed_jobs = JSON.parse(conn.get(Helpers.build_failed_jobs_key(queue)) || '[]')
|
44
|
+
|
45
|
+
conn.rename(failed_jobs_key, temporal_failed_key)
|
46
|
+
|
47
|
+
failed_jobs.each { |job| Sidekiq::QueueMetrics::Storage::add_failed_job(job) }
|
48
|
+
|
49
|
+
conn.del(temporal_failed_key)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
conn.del(Helpers.stats_key)
|
54
|
+
end
|
55
|
+
|
56
|
+
logger.info("Sucessfully upgraded")
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.upgrade_needed?
|
60
|
+
Sidekiq.redis_pool.with { |conn| conn.exists(Helpers.stats_key) }
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.acquire_lock(&block)
|
64
|
+
Sidekiq.redis_pool.with do |conn|
|
65
|
+
lock_manager = Redlock::Client.new([conn], {
|
66
|
+
retry_count: 5,
|
67
|
+
retry_delay: 500,
|
68
|
+
retry_jitter: 150, # milliseconds
|
69
|
+
redis_timeout: 0.1 # seconds
|
70
|
+
})
|
71
|
+
lock_manager.lock!('sidekiq_queue_metrics:upgrade_lock', 10000, &block)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -60,11 +60,11 @@
|
|
60
60
|
<td><%= job['class'] %></td>
|
61
61
|
<td><%= job['args'].join(', ') %></td>
|
62
62
|
<td>
|
63
|
-
<a href="<%= "
|
63
|
+
<a href="<%= "#{root_path}queue_metrics/queues/#{@queue}/jobs/#{job['jid']}" %>">
|
64
64
|
<%= relative_time(Time.at(job['enqueued_at'])) %>
|
65
65
|
</a>
|
66
66
|
</td>
|
67
67
|
<td><%= job['error_message'] %></td>
|
68
68
|
</tr>
|
69
69
|
<% end %>
|
70
|
-
</table>
|
70
|
+
</table>
|
@@ -59,7 +59,7 @@
|
|
59
59
|
<thead>
|
60
60
|
<tr>
|
61
61
|
<th>
|
62
|
-
<a href="<%= "
|
62
|
+
<a href="<%= "#{root_path}queue_metrics/queues/#{queue}/summary" %>">
|
63
63
|
<span class="heading"><%= queue %></span>
|
64
64
|
</a>
|
65
65
|
</th>
|
@@ -73,4 +73,4 @@
|
|
73
73
|
</tr>
|
74
74
|
<% end %>
|
75
75
|
</table>
|
76
|
-
<% end %>
|
76
|
+
<% end %>
|
@@ -5,17 +5,11 @@ module Sidekiq::QueueMetrics
|
|
5
5
|
|
6
6
|
app.get "/queue_metrics" do
|
7
7
|
queue_metrics = Sidekiq::QueueMetrics.fetch
|
8
|
-
@@last_metrics ||= queue_metrics
|
9
|
-
|
10
|
-
@queue_metrics = queue_metrics.each_with_object({}) do |queue_metric, new_queue_metrics|
|
11
|
-
queue, metric = queue_metric
|
12
|
-
new_queue_metrics[queue] = metric.each_with_object({}) do |current_metric, updated_metrics|
|
13
|
-
name, count = current_metric
|
14
|
-
updated_metrics[name] = {'count' => count, 'animate' => @@last_metrics[queue][name] != count}
|
15
|
-
end
|
16
|
-
end
|
17
8
|
|
9
|
+
@@last_metrics ||= queue_metrics
|
10
|
+
@queue_metrics = Helpers.build_metrics_for_view(@@last_metrics, queue_metrics)
|
18
11
|
@@last_metrics = queue_metrics
|
12
|
+
|
19
13
|
render(:erb, File.read(File.join(view_path, "queues_stats.erb")))
|
20
14
|
end
|
21
15
|
|
@@ -35,4 +29,4 @@ module Sidekiq::QueueMetrics
|
|
35
29
|
end
|
36
30
|
end
|
37
31
|
end
|
38
|
-
end
|
32
|
+
end
|
@@ -17,8 +17,11 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
|
-
s.add_dependency 'sidekiq'
|
20
|
+
s.add_dependency 'sidekiq'
|
21
21
|
s.add_dependency 'eldritch'
|
22
|
-
s.
|
22
|
+
s.add_dependency 'redlock'
|
23
|
+
|
24
|
+
s.add_development_dependency 'bundler', '~> 1.5'
|
23
25
|
s.add_development_dependency 'rspec'
|
26
|
+
s.add_development_dependency 'fakeredis'
|
24
27
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
describe Sidekiq::QueueMetrics::Helpers do
|
2
|
+
describe '.build_queue_stats_key' do
|
3
|
+
context 'with default storage location' do
|
4
|
+
it 'should return the name of the key where metrics are stored for a given queue' do
|
5
|
+
expect(subject.build_queue_stats_key('test')).to eql('queue_stats:test')
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'with a different storage location' do
|
10
|
+
before { Sidekiq::QueueMetrics.storage_location = 'different_storage_location' }
|
11
|
+
after { Sidekiq::QueueMetrics.storage_location = nil}
|
12
|
+
|
13
|
+
it 'should return the name of the key where metrics are stored for a given queue' do
|
14
|
+
expect(subject.build_queue_stats_key('test')).to eql('different_storage_location:test')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.build_failed_jobs_key' do
|
20
|
+
it 'should return the name of the key where failed jobs are stored for a given queue' do
|
21
|
+
expect(subject.build_failed_jobs_key('test')).to eql('failed_jobs:test')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '.build_metrics_for_view' do
|
26
|
+
context 'when there is no changes' do
|
27
|
+
let(:last_metrics) {{
|
28
|
+
'mailer_queue' => {
|
29
|
+
'processed' => 3
|
30
|
+
}
|
31
|
+
}}
|
32
|
+
let(:current_metrics) {{
|
33
|
+
'mailer_queue' => {
|
34
|
+
'processed' => 3
|
35
|
+
}
|
36
|
+
}}
|
37
|
+
|
38
|
+
it 'should add an animate key and a count with the same metric value' do
|
39
|
+
expected_result = {
|
40
|
+
'mailer_queue' => {
|
41
|
+
'processed' => { 'count' => 3, 'animate' => false }
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
expect(
|
46
|
+
subject.build_metrics_for_view(last_metrics, current_metrics)
|
47
|
+
).to eql(expected_result)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when only values are changed' do
|
52
|
+
let(:last_metrics) {{
|
53
|
+
'mailer_queue' => {
|
54
|
+
'processed' => 3
|
55
|
+
}
|
56
|
+
}}
|
57
|
+
let(:current_metrics) {{
|
58
|
+
'mailer_queue' => {
|
59
|
+
'processed' => 4
|
60
|
+
}
|
61
|
+
}}
|
62
|
+
|
63
|
+
it 'should add an animate key and a count with the last metric value' do
|
64
|
+
expected_result = {
|
65
|
+
'mailer_queue' => {
|
66
|
+
'processed' => { 'count' => 4, 'animate' => true }
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
expect(
|
71
|
+
subject.build_metrics_for_view(last_metrics, current_metrics)
|
72
|
+
).to eql(expected_result)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context' when a new queue is added' do
|
77
|
+
let(:last_metrics) {{
|
78
|
+
'mailer_queue' => {
|
79
|
+
'processed' => 3
|
80
|
+
}
|
81
|
+
}}
|
82
|
+
let(:current_metrics) {{
|
83
|
+
'mailer_queue' => {
|
84
|
+
'processed' => 4
|
85
|
+
},
|
86
|
+
'new_queue' => {
|
87
|
+
'failed' => 1
|
88
|
+
}
|
89
|
+
}}
|
90
|
+
|
91
|
+
it 'should add the new queue' do
|
92
|
+
expected_result = {
|
93
|
+
'mailer_queue' => {
|
94
|
+
'processed' => { 'count' => 4, 'animate' => true }
|
95
|
+
},
|
96
|
+
'new_queue' => {
|
97
|
+
'failed' => { 'count' => 1, 'animate' => false }
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
expect(
|
102
|
+
subject.build_metrics_for_view(last_metrics, current_metrics)
|
103
|
+
).to eql(expected_result)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
describe Sidekiq::QueueMetrics::JobDeathMiddleware do
|
2
|
+
let(:redis_connection) { Redis.new }
|
3
|
+
|
4
|
+
before(:all) do
|
5
|
+
Sidekiq.redis = ConnectionPool.new { redis_connection }
|
6
|
+
end
|
7
|
+
|
8
|
+
before { redis_connection.flushall }
|
9
|
+
|
10
|
+
context 'when retry_count key is not present' do
|
11
|
+
it 'should call the job dead monitor' do
|
12
|
+
expect_any_instance_of(Sidekiq::QueueMetrics::JobDeathMonitor).not_to receive(:monitor)
|
13
|
+
|
14
|
+
subject.call(Class.new, {}, 'test_queue')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when retry_count key is greater than 0' do
|
19
|
+
it 'should call the job dead monitor' do
|
20
|
+
expect_any_instance_of(Sidekiq::QueueMetrics::JobDeathMonitor).not_to receive(:monitor)
|
21
|
+
|
22
|
+
subject.call(Class.new, { 'retry_count' => 1 }, 'test_queue')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when retry_count key is 0' do
|
27
|
+
it 'should call the job dead monitor' do
|
28
|
+
expect_any_instance_of(Sidekiq::QueueMetrics::JobDeathMonitor).to receive(:monitor).with({
|
29
|
+
'retry_count' => 0,
|
30
|
+
'error_class' => 'StandardError'
|
31
|
+
})
|
32
|
+
|
33
|
+
subject.call(Class.new, {
|
34
|
+
'retry_count' => 0,
|
35
|
+
'error_class' => 'StandardError'
|
36
|
+
}, 'test_queue')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,53 +1,70 @@
|
|
1
1
|
describe Sidekiq::QueueMetrics::JobDeathMonitor do
|
2
|
+
let(:redis_connection) { Redis.new }
|
3
|
+
|
4
|
+
before(:all) do
|
5
|
+
Sidekiq.redis = ConnectionPool.new { redis_connection }
|
6
|
+
end
|
7
|
+
|
8
|
+
before { redis_connection.flushall }
|
9
|
+
|
2
10
|
describe '#call' do
|
3
11
|
let(:job) {{'queue' => 'mailer_queue'}}
|
4
|
-
let(:
|
5
|
-
let(:monitor) {Sidekiq::QueueMetrics::JobDeathMonitor.proc}
|
12
|
+
let(:monitor) { Sidekiq::QueueMetrics::JobDeathMonitor.proc }
|
6
13
|
|
7
14
|
context 'when stats does not exist' do
|
8
15
|
it 'should create stats key and add stats of queue' do
|
9
|
-
|
10
|
-
|
11
|
-
expect(
|
16
|
+
monitor.call(job)
|
17
|
+
|
18
|
+
expect(
|
19
|
+
Sidekiq::QueueMetrics::Storage.get_stats('mailer_queue')
|
20
|
+
).to eq({ 'failed' => 1 })
|
21
|
+
end
|
12
22
|
|
23
|
+
it 'should add the job to the failed jobs list' do
|
13
24
|
monitor.call(job)
|
25
|
+
|
26
|
+
expect(
|
27
|
+
Sidekiq::QueueMetrics::Storage.failed_jobs('mailer_queue')
|
28
|
+
).to eql([job])
|
14
29
|
end
|
15
30
|
end
|
16
31
|
|
17
32
|
context 'when stats exists' do
|
18
33
|
it 'should create a new queue when it does not exist' do
|
19
|
-
|
20
|
-
existing_stats = {mailer_queue: {failed: 1}}.to_json
|
21
|
-
expected_stats = {mailer_queue: {failed: 1}, job_queue: {failed: 1}}.to_json
|
34
|
+
Sidekiq::QueueMetrics::Storage.increment_stat('mailer_queue', 'failed')
|
22
35
|
|
23
|
-
|
24
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:set_stats).with(expected_stats)
|
25
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:add_failed_job).with(job_queue)
|
36
|
+
job_queue = {'queue' => 'job_queue'}
|
26
37
|
|
27
38
|
monitor.call(job_queue)
|
39
|
+
|
40
|
+
expect(
|
41
|
+
Sidekiq::QueueMetrics::Storage.get_stats('mailer_queue')
|
42
|
+
).to eq({ 'failed' => 1 })
|
43
|
+
|
44
|
+
expect(
|
45
|
+
Sidekiq::QueueMetrics::Storage.get_stats('job_queue')
|
46
|
+
).to eq({ 'failed' => 1 })
|
28
47
|
end
|
29
48
|
|
30
49
|
it 'should update existing queue' do
|
31
|
-
|
32
|
-
expected_stats = {mailer_queue: {failed: 2}}.to_json
|
33
|
-
|
34
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:get_stats).and_return(existing_stats)
|
35
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:set_stats).with(expected_stats)
|
36
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:add_failed_job).with(job)
|
50
|
+
Sidekiq::QueueMetrics::Storage.increment_stat('mailer_queue', 'failed')
|
37
51
|
|
38
52
|
monitor.call(job)
|
53
|
+
|
54
|
+
expect(
|
55
|
+
Sidekiq::QueueMetrics::Storage.get_stats('mailer_queue')
|
56
|
+
).to eq({ 'failed' => 2 })
|
39
57
|
end
|
40
58
|
|
41
59
|
it 'should create failed counter when other counters exists' do
|
42
|
-
|
43
|
-
expected_stats = {mailer_queue: {processed: 1, failed: 1}}.to_json
|
44
|
-
|
45
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:get_stats).and_return(existing_stats)
|
46
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:set_stats).with(expected_stats)
|
47
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:add_failed_job).with(job)
|
60
|
+
Sidekiq::QueueMetrics::Storage.increment_stat('mailer_queue', 'processed')
|
48
61
|
|
49
62
|
monitor.call(job)
|
63
|
+
|
64
|
+
expect(
|
65
|
+
Sidekiq::QueueMetrics::Storage.get_stats('mailer_queue')
|
66
|
+
).to eq({ 'processed' => 1, 'failed' => 1 })
|
50
67
|
end
|
51
68
|
end
|
52
69
|
end
|
53
|
-
end
|
70
|
+
end
|
@@ -1,48 +1,62 @@
|
|
1
1
|
describe Sidekiq::QueueMetrics::JobSuccessMonitor do
|
2
|
+
let(:redis_connection) { Redis.new }
|
3
|
+
|
4
|
+
let(:worker) { double(:worker) }
|
5
|
+
let(:job) { double(:job) }
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
Sidekiq.redis = ConnectionPool.new { redis_connection }
|
9
|
+
end
|
10
|
+
|
11
|
+
before { redis_connection.flushall }
|
12
|
+
|
2
13
|
describe '#call' do
|
3
|
-
let(:
|
4
|
-
let(:worker) {double('worker')}
|
5
|
-
let(:monitor) {Sidekiq::QueueMetrics::JobSuccessMonitor.new}
|
14
|
+
let(:monitor) { Sidekiq::QueueMetrics::JobSuccessMonitor.new }
|
6
15
|
|
7
16
|
context 'when stats does not exist' do
|
8
17
|
it 'should create stats key and add stats of queue' do
|
9
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:get_stats).and_return(nil)
|
10
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:set_stats).with({mailer_queue: {processed: 1}}.to_json)
|
11
|
-
|
12
18
|
monitor.call(worker, job, 'mailer_queue')
|
19
|
+
|
20
|
+
expect(
|
21
|
+
Sidekiq::QueueMetrics::Storage.get_stats('mailer_queue')
|
22
|
+
).to eq({ 'processed' => 1 })
|
13
23
|
end
|
14
24
|
end
|
15
25
|
|
16
26
|
context 'when stats exists' do
|
17
27
|
it 'should create a new queue when it does not exist' do
|
18
|
-
|
19
|
-
expected_stats = {mailer_queue: {processed: 1}, job_queue: {processed: 1}}.to_json
|
20
|
-
|
21
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:get_stats).and_return(existing_stats)
|
22
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:set_stats).with(expected_stats)
|
28
|
+
Sidekiq::QueueMetrics::Storage.increment_stat('mailer_queue', 'processed')
|
23
29
|
|
24
30
|
monitor.call(worker, job, 'job_queue')
|
31
|
+
|
32
|
+
expect(
|
33
|
+
Sidekiq::QueueMetrics::Storage.get_stats('mailer_queue')
|
34
|
+
).to eq({ 'processed' => 1 })
|
35
|
+
|
36
|
+
expect(
|
37
|
+
Sidekiq::QueueMetrics::Storage.get_stats('job_queue')
|
38
|
+
).to eq({ 'processed' => 1 })
|
25
39
|
end
|
26
40
|
|
27
41
|
it 'should update existing queue' do
|
28
|
-
|
29
|
-
expected_stats = {mailer_queue: {processed: 2}}.to_json
|
30
|
-
|
31
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:get_stats).and_return(existing_stats)
|
32
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:set_stats).with(expected_stats)
|
42
|
+
Sidekiq::QueueMetrics::Storage.increment_stat('mailer_queue', 'processed')
|
33
43
|
|
34
44
|
monitor.call(worker, job, 'mailer_queue')
|
45
|
+
|
46
|
+
expect(
|
47
|
+
Sidekiq::QueueMetrics::Storage.get_stats('mailer_queue')
|
48
|
+
).to eq({ 'processed' => 2 })
|
35
49
|
end
|
36
50
|
|
37
51
|
it 'should create failed counter when other counters exists' do
|
38
|
-
|
39
|
-
expected_stats = {mailer_queue: {failed: 1, processed: 1}}.to_json
|
40
|
-
|
41
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:get_stats).and_return(existing_stats)
|
42
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:set_stats).with(expected_stats)
|
52
|
+
Sidekiq::QueueMetrics::Storage.increment_stat('mailer_queue', 'failed')
|
43
53
|
|
44
54
|
monitor.call(worker, job, 'mailer_queue')
|
55
|
+
|
56
|
+
expect(
|
57
|
+
Sidekiq::QueueMetrics::Storage.get_stats('mailer_queue')
|
58
|
+
).to eq({ 'processed' => 1, 'failed' => 1 })
|
45
59
|
end
|
46
60
|
end
|
47
61
|
end
|
48
|
-
end
|
62
|
+
end
|
@@ -1,4 +1,38 @@
|
|
1
1
|
describe Sidekiq::QueueMetrics do
|
2
|
+
let(:redis_connection) { Redis.new }
|
3
|
+
|
4
|
+
before(:all) do
|
5
|
+
Sidekiq.redis = ConnectionPool.new { redis_connection }
|
6
|
+
end
|
7
|
+
|
8
|
+
before { redis_connection.flushall }
|
9
|
+
|
10
|
+
describe '#init' do
|
11
|
+
if Sidekiq::QueueMetrics.support_death_handlers?
|
12
|
+
it 'attach the expected listeners for failed job' do
|
13
|
+
Sidekiq::QueueMetrics.init(Sidekiq)
|
14
|
+
|
15
|
+
expect(Sidekiq.death_handlers).to_not be_empty
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'doesn\'t attach the JobDeathMiddleware to the server middleware chain' do
|
19
|
+
Sidekiq::QueueMetrics.init(Sidekiq)
|
20
|
+
|
21
|
+
expect(
|
22
|
+
Sidekiq.server_middleware.entries.select { |x| x.klass == Sidekiq::QueueMetrics::JobDeathMiddleware }
|
23
|
+
).to be_empty
|
24
|
+
end
|
25
|
+
else
|
26
|
+
it 'attach the JobDeathMiddleware to the server middleware chain' do
|
27
|
+
Sidekiq::QueueMetrics.init(Sidekiq)
|
28
|
+
|
29
|
+
expect(
|
30
|
+
Sidekiq.server_middleware.entries.select { |x| x.klass == Sidekiq::QueueMetrics::JobDeathMiddleware }
|
31
|
+
).not_to be_empty
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
2
36
|
describe '#fetch' do
|
3
37
|
before(:each) do
|
4
38
|
queues = [OpenStruct.new(name: :mailer_queue), OpenStruct.new(name: :heavy_jobs_queue)]
|
@@ -6,13 +40,22 @@ describe Sidekiq::QueueMetrics do
|
|
6
40
|
end
|
7
41
|
|
8
42
|
it 'should fetch current queue stats' do
|
9
|
-
|
10
|
-
|
11
|
-
|
43
|
+
Sidekiq::QueueMetrics::Storage.increment_stat('mailer_queue', 'processed', 2)
|
44
|
+
Sidekiq::QueueMetrics::Storage.increment_stat('mailer_queue', 'failed')
|
45
|
+
Sidekiq::QueueMetrics::Storage.increment_stat('heavy_jobs_queue', 'processed')
|
46
|
+
|
47
|
+
jobs_in_retry_queue = [
|
48
|
+
OpenStruct.new(queue: 'mailer_queue'),
|
49
|
+
OpenStruct.new(queue: 'heavy_jobs_queue')
|
50
|
+
]
|
51
|
+
scheduled_jobs = [
|
52
|
+
OpenStruct.new(queue: 'mailer_queue'),
|
53
|
+
OpenStruct.new(queue: 'heavy_jobs_queue')
|
54
|
+
]
|
12
55
|
|
13
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:get_stats).and_return(stats.to_json)
|
14
56
|
expect(Sidekiq::RetrySet).to receive(:new).and_return(jobs_in_retry_queue)
|
15
57
|
expect(Sidekiq::ScheduledSet).to receive(:new).and_return(scheduled_jobs)
|
58
|
+
|
16
59
|
expect(Sidekiq::Queue).to receive(:new).with('mailer_queue').and_return(OpenStruct.new(size: 1))
|
17
60
|
expect(Sidekiq::Queue).to receive(:new).with('heavy_jobs_queue').and_return(OpenStruct.new(size: 1))
|
18
61
|
|
@@ -32,12 +75,14 @@ describe Sidekiq::QueueMetrics do
|
|
32
75
|
end
|
33
76
|
|
34
77
|
it 'should have default value as zero' do
|
35
|
-
|
78
|
+
Sidekiq::QueueMetrics::Storage.increment_stat('mailer_queue', 'processed', 2)
|
79
|
+
Sidekiq::QueueMetrics::Storage.increment_stat('heavy_jobs_queue', 'failed')
|
80
|
+
|
36
81
|
scheduled_jobs = jobs_in_retry_queue = []
|
37
82
|
|
38
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:get_stats).and_return(stats.to_json)
|
39
83
|
expect(Sidekiq::RetrySet).to receive(:new).and_return(jobs_in_retry_queue)
|
40
84
|
expect(Sidekiq::ScheduledSet).to receive(:new).and_return(scheduled_jobs)
|
85
|
+
|
41
86
|
expect(Sidekiq::Queue).to receive(:new).with('mailer_queue').and_return(OpenStruct.new(size: 0))
|
42
87
|
expect(Sidekiq::Queue).to receive(:new).with('heavy_jobs_queue').and_return(OpenStruct.new(size: 0))
|
43
88
|
|
@@ -53,7 +98,6 @@ describe Sidekiq::QueueMetrics do
|
|
53
98
|
it 'should return Sidekiq::QueueMetrics for all sidekiq queues' do
|
54
99
|
jobs_in_retry_queue = scheduled_jobs = []
|
55
100
|
|
56
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:get_stats).and_return(nil)
|
57
101
|
expect(Sidekiq::RetrySet).to receive(:new).and_return(jobs_in_retry_queue)
|
58
102
|
expect(Sidekiq::ScheduledSet).to receive(:new).and_return(scheduled_jobs)
|
59
103
|
expect(Sidekiq::Queue).to receive(:new).with('mailer_queue').and_return(OpenStruct.new(size: 0))
|
@@ -78,11 +122,14 @@ describe Sidekiq::QueueMetrics do
|
|
78
122
|
describe '#failed_jobs' do
|
79
123
|
it 'should return failed jobs for a queue' do
|
80
124
|
queue = 'default_queue'
|
81
|
-
job_1 = double(:job)
|
82
|
-
job_2 = double(:job)
|
83
|
-
expect(Sidekiq::QueueMetrics::Storage).to receive(:failed_jobs).and_return([job_1, job_2])
|
84
125
|
|
85
|
-
|
126
|
+
job_1 = {'queue' => queue, 'args' => [1]}
|
127
|
+
job_2 = {'queue' => queue, 'args' => [2]}
|
128
|
+
|
129
|
+
Sidekiq::QueueMetrics::Storage.add_failed_job(job_1)
|
130
|
+
Sidekiq::QueueMetrics::Storage.add_failed_job(job_2)
|
131
|
+
|
132
|
+
expect(Sidekiq::QueueMetrics.failed_jobs(queue)).to eq([job_1, job_2])
|
86
133
|
end
|
87
134
|
end
|
88
|
-
end
|
135
|
+
end
|
@@ -1,82 +1,52 @@
|
|
1
1
|
describe Sidekiq::QueueMetrics::Storage do
|
2
|
-
|
3
|
-
|
2
|
+
let(:redis_connection) { Redis.new }
|
3
|
+
let(:queue) { 'mailer_queue' }
|
4
|
+
let(:job) { {'queue' => queue, 'args' => [1]} }
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
|
-
def with
|
10
|
-
yield conn
|
11
|
-
end
|
6
|
+
before(:all) do
|
7
|
+
Sidekiq.redis = ConnectionPool.new { redis_connection }
|
12
8
|
end
|
13
9
|
|
14
|
-
|
15
|
-
let(:mock_redis_pool) {MockRedisPool.new(mock_redis_conn)}
|
10
|
+
before { redis_connection.flushall }
|
16
11
|
|
17
12
|
describe '#add_failed_job' do
|
18
13
|
it 'should add first failed job' do
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
expect(mock_redis_conn).to receive(:set).with("failed_jobs:mailer_queue", [job].to_json)
|
24
|
-
|
25
|
-
Sidekiq::QueueMetrics::Storage.add_failed_job(job)
|
14
|
+
expect do
|
15
|
+
Sidekiq::QueueMetrics::Storage.add_failed_job(job)
|
16
|
+
end.to change{ Sidekiq::QueueMetrics::Storage.failed_jobs(queue).length }.from(0).to(1)
|
26
17
|
end
|
27
18
|
|
28
19
|
it 'should add failed job to existing jobs' do
|
29
|
-
|
30
|
-
new_job = {'queue' => 'mailer_queue', 'args' => [1]}
|
31
|
-
existing_jobs = [{'queue' => 'mailer_queue', 'args' => [2]}]
|
32
|
-
|
33
|
-
expect(Sidekiq).to receive(:redis_pool).and_return(mock_redis_pool)
|
34
|
-
expect(mock_redis_conn).to receive(:get).with(key).and_return(existing_jobs.to_json)
|
35
|
-
|
36
|
-
expect(mock_redis_conn).to receive(:set).with(key, [existing_jobs.first, new_job].to_json)
|
20
|
+
Sidekiq::QueueMetrics::Storage.add_failed_job(job)
|
37
21
|
|
38
|
-
|
22
|
+
expect do
|
23
|
+
Sidekiq::QueueMetrics::Storage.add_failed_job({'queue' => queue, 'args' => [2]})
|
24
|
+
end.to change{ Sidekiq::QueueMetrics::Storage.failed_jobs(queue).length }.from(1).to(2)
|
39
25
|
end
|
40
26
|
|
41
27
|
it 'should delete old job when failed jobs limit has reached' do
|
42
|
-
|
43
|
-
|
44
|
-
oldest_job = {'queue' => 'mailer_queue', 'args' => [2]}
|
45
|
-
older_job = {'queue' => 'mailer_queue', 'args' => [3]}
|
46
|
-
|
47
|
-
existing_jobs = [oldest_job, older_job]
|
48
|
-
|
49
|
-
expect(Sidekiq).to receive(:redis_pool).and_return(mock_redis_pool)
|
50
|
-
expect(mock_redis_conn).to receive(:get).with(key).and_return(existing_jobs.to_json)
|
51
|
-
|
52
|
-
expect(mock_redis_conn).to receive(:set).with(key, [older_job, new_job].to_json)
|
28
|
+
Sidekiq::QueueMetrics::Storage.add_failed_job(job)
|
29
|
+
Sidekiq::QueueMetrics::Storage.add_failed_job({'queue' => queue, 'args' => [2]})
|
53
30
|
|
54
|
-
|
31
|
+
expect do
|
32
|
+
Sidekiq::QueueMetrics::Storage.add_failed_job({'queue' => queue, 'args' => [3]}, 2)
|
33
|
+
end.to_not change { Sidekiq::QueueMetrics::Storage.failed_jobs(queue).length }
|
55
34
|
end
|
56
35
|
end
|
57
36
|
|
58
37
|
describe '#failed_jobs' do
|
59
38
|
context 'when failed jobs are not present' do
|
60
39
|
it 'should return failed jobs for a given queue' do
|
61
|
-
queue = 'mailer_queue'
|
62
|
-
expect(Sidekiq).to receive(:redis_pool).and_return(mock_redis_pool)
|
63
|
-
|
64
|
-
expect(mock_redis_conn).to receive(:get).with("failed_jobs:#{queue}").and_return(nil)
|
65
|
-
|
66
40
|
expect(Sidekiq::QueueMetrics::Storage.failed_jobs(queue)).to be_empty
|
67
41
|
end
|
68
42
|
end
|
69
43
|
|
70
44
|
context 'when failed jobs are present' do
|
71
45
|
it 'should return failed jobs for a given queue' do
|
72
|
-
|
73
|
-
jobs = [{'queue' => 'mailer_queue', 'args' => [1]}]
|
74
|
-
expect(Sidekiq).to receive(:redis_pool).and_return(mock_redis_pool)
|
75
|
-
|
76
|
-
expect(mock_redis_conn).to receive(:get).with("failed_jobs:#{queue}").and_return(jobs.to_json)
|
46
|
+
Sidekiq::QueueMetrics::Storage.add_failed_job(job)
|
77
47
|
|
78
|
-
expect(Sidekiq::QueueMetrics::Storage.failed_jobs(queue)).to eq(
|
48
|
+
expect(Sidekiq::QueueMetrics::Storage.failed_jobs(queue)).to eq([job])
|
79
49
|
end
|
80
50
|
end
|
81
51
|
end
|
82
|
-
end
|
52
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
describe Sidekiq::QueueMetrics::UpgradeManager do
|
2
|
+
let(:redis_connection) { Redis.new }
|
3
|
+
|
4
|
+
before(:all) do
|
5
|
+
Sidekiq.redis = ConnectionPool.new { redis_connection }
|
6
|
+
end
|
7
|
+
|
8
|
+
before { redis_connection.flushall }
|
9
|
+
|
10
|
+
describe 'upgrading to v3' do
|
11
|
+
let(:old_queue_stats) {{
|
12
|
+
'mailer_queue' => {
|
13
|
+
'failed' => 1,
|
14
|
+
'processed' => 3
|
15
|
+
},
|
16
|
+
'other_queue' => {
|
17
|
+
'failed' => 1,
|
18
|
+
'processed' => 143
|
19
|
+
}
|
20
|
+
}}
|
21
|
+
|
22
|
+
let(:failed_jobs_mailer_queue) {
|
23
|
+
[{ 'queue' => 'mailer_queue', 'args' => [1]}]
|
24
|
+
}
|
25
|
+
|
26
|
+
let(:failed_jobs_other_queue) {
|
27
|
+
[{ 'queue' => 'other_queue', 'args' => [2]}]
|
28
|
+
}
|
29
|
+
|
30
|
+
describe '.v2_to_v3_upgrade' do
|
31
|
+
before do
|
32
|
+
redis_connection.set(Sidekiq::QueueMetrics::Helpers.stats_key, JSON.generate(old_queue_stats))
|
33
|
+
redis_connection.set(Sidekiq::QueueMetrics::Helpers.build_failed_jobs_key('mailer_queue'), JSON.generate(failed_jobs_mailer_queue))
|
34
|
+
redis_connection.set(Sidekiq::QueueMetrics::Helpers.build_failed_jobs_key('other_queue'), JSON.generate(failed_jobs_other_queue))
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should delete the old stats key' do
|
38
|
+
Sidekiq::QueueMetrics::UpgradeManager.v2_to_v3_upgrade
|
39
|
+
|
40
|
+
expect(redis_connection.exists(Sidekiq::QueueMetrics::Helpers.stats_key)).to be_falsey
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should set the previous values into the new stats format' do
|
44
|
+
Sidekiq::QueueMetrics::UpgradeManager.v2_to_v3_upgrade
|
45
|
+
|
46
|
+
mailer_queue_stats = Sidekiq::QueueMetrics::Storage.get_stats('mailer_queue')
|
47
|
+
other_queue_stats = Sidekiq::QueueMetrics::Storage.get_stats('other_queue')
|
48
|
+
|
49
|
+
expect(mailer_queue_stats['processed']).to be(3)
|
50
|
+
expect(mailer_queue_stats['failed']).to be(1)
|
51
|
+
|
52
|
+
expect(other_queue_stats['processed']).to be(143)
|
53
|
+
expect(other_queue_stats['failed']).to be(1)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should add the failed jobs into the same key with new format' do
|
57
|
+
Sidekiq::QueueMetrics::UpgradeManager.v2_to_v3_upgrade
|
58
|
+
|
59
|
+
expect(Sidekiq::QueueMetrics::Storage.failed_jobs('mailer_queue')).to eql(failed_jobs_mailer_queue)
|
60
|
+
expect(Sidekiq::QueueMetrics::Storage.failed_jobs('other_queue')).to eql(failed_jobs_other_queue)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should delete temporal failed jobs keys' do
|
64
|
+
mailer_temporal_key = "_#{Sidekiq::QueueMetrics::Helpers.build_failed_jobs_key('mailer_queue')}"
|
65
|
+
other_temporal_key = "_#{Sidekiq::QueueMetrics::Helpers.build_failed_jobs_key('other_queue')}"
|
66
|
+
|
67
|
+
Sidekiq::QueueMetrics::UpgradeManager.v2_to_v3_upgrade
|
68
|
+
|
69
|
+
expect(redis_connection.exists(mailer_temporal_key)).to be_falsey
|
70
|
+
expect(redis_connection.exists(other_temporal_key)).to be_falsey
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '.upgrade_needed?' do
|
75
|
+
it 'should be true if the old queue stats key exists' do
|
76
|
+
redis_connection.set(Sidekiq::QueueMetrics::Helpers.stats_key, JSON.generate(old_queue_stats))
|
77
|
+
|
78
|
+
expect(Sidekiq::QueueMetrics::UpgradeManager.upgrade_needed?).to be_truthy
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should be false if the old queue stats key is not set' do
|
82
|
+
expect(Sidekiq::QueueMetrics::UpgradeManager.upgrade_needed?).to be_falsey
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq_queue_metrics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ajit Singh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sidekiq
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: eldritch
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: redlock
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +80,20 @@ dependencies:
|
|
66
80
|
- - ">="
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: fakeredis
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
69
97
|
description:
|
70
98
|
email: jeetsingh.ajit@gamil.com
|
71
99
|
executables: []
|
@@ -76,26 +104,31 @@ files:
|
|
76
104
|
- ".rspec"
|
77
105
|
- ".travis.yml"
|
78
106
|
- Gemfile
|
79
|
-
- Gemfile.lock
|
80
107
|
- LICENSE
|
81
108
|
- README.md
|
82
109
|
- lib/sidekiq_queue_metrics.rb
|
83
110
|
- lib/sidekiq_queue_metrics/configuration.rb
|
111
|
+
- lib/sidekiq_queue_metrics/helpers.rb
|
112
|
+
- lib/sidekiq_queue_metrics/job_death_middleware.rb
|
84
113
|
- lib/sidekiq_queue_metrics/monitor/job_death_monitor.rb
|
85
114
|
- lib/sidekiq_queue_metrics/monitor/job_success_monitor.rb
|
86
115
|
- lib/sidekiq_queue_metrics/monitor/monitor.rb
|
87
116
|
- lib/sidekiq_queue_metrics/queue_metrics.rb
|
88
117
|
- lib/sidekiq_queue_metrics/storage.rb
|
118
|
+
- lib/sidekiq_queue_metrics/upgrade_manager.rb
|
89
119
|
- lib/sidekiq_queue_metrics/version.rb
|
90
120
|
- lib/sidekiq_queue_metrics/views/failed_job.erb
|
91
121
|
- lib/sidekiq_queue_metrics/views/queue_summary.erb
|
92
122
|
- lib/sidekiq_queue_metrics/views/queues_stats.erb
|
93
123
|
- lib/sidekiq_queue_metrics/web_extension.rb
|
94
124
|
- sidekiq_queue_metrics.gemspec
|
125
|
+
- spec/lib/sidekiq_queue_metrics/helpers_spec.rb
|
126
|
+
- spec/lib/sidekiq_queue_metrics/job_death_middleware_spec.rb
|
95
127
|
- spec/lib/sidekiq_queue_metrics/monitor/job_death_monitor_spec.rb
|
96
128
|
- spec/lib/sidekiq_queue_metrics/monitor/job_success_monitor_spec.rb
|
97
129
|
- spec/lib/sidekiq_queue_metrics/queue_metrics_spec.rb
|
98
130
|
- spec/lib/sidekiq_queue_metrics/storage_spec.rb
|
131
|
+
- spec/lib/sidekiq_queue_metrics/upgrade_manager_spec.rb
|
99
132
|
- spec/spec_helper.rb
|
100
133
|
homepage: https://github.com/ajitsing/sidekiq_queue_metrics
|
101
134
|
licenses:
|
@@ -116,14 +149,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
149
|
- !ruby/object:Gem::Version
|
117
150
|
version: '0'
|
118
151
|
requirements: []
|
119
|
-
|
120
|
-
rubygems_version: 2.7.6
|
152
|
+
rubygems_version: 3.0.4
|
121
153
|
signing_key:
|
122
154
|
specification_version: 4
|
123
155
|
summary: Records stats of each sidekiq queue and exposes APIs to retrieve them
|
124
156
|
test_files:
|
157
|
+
- spec/lib/sidekiq_queue_metrics/helpers_spec.rb
|
158
|
+
- spec/lib/sidekiq_queue_metrics/job_death_middleware_spec.rb
|
125
159
|
- spec/lib/sidekiq_queue_metrics/monitor/job_death_monitor_spec.rb
|
126
160
|
- spec/lib/sidekiq_queue_metrics/monitor/job_success_monitor_spec.rb
|
127
161
|
- spec/lib/sidekiq_queue_metrics/queue_metrics_spec.rb
|
128
162
|
- spec/lib/sidekiq_queue_metrics/storage_spec.rb
|
163
|
+
- spec/lib/sidekiq_queue_metrics/upgrade_manager_spec.rb
|
129
164
|
- spec/spec_helper.rb
|
data/Gemfile.lock
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
GEM
|
2
|
-
remote: http://rubygems.org/
|
3
|
-
specs:
|
4
|
-
concurrent-ruby (1.0.5)
|
5
|
-
connection_pool (2.2.2)
|
6
|
-
diff-lcs (1.3)
|
7
|
-
eldritch (1.1.3)
|
8
|
-
reentrant_mutex (~> 1.1.0)
|
9
|
-
rack (2.0.6)
|
10
|
-
rack-protection (2.0.3)
|
11
|
-
rack
|
12
|
-
redis (4.0.1)
|
13
|
-
reentrant_mutex (1.1.1)
|
14
|
-
rspec (3.7.0)
|
15
|
-
rspec-core (~> 3.7.0)
|
16
|
-
rspec-expectations (~> 3.7.0)
|
17
|
-
rspec-mocks (~> 3.7.0)
|
18
|
-
rspec-core (3.7.1)
|
19
|
-
rspec-support (~> 3.7.0)
|
20
|
-
rspec-expectations (3.7.0)
|
21
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
-
rspec-support (~> 3.7.0)
|
23
|
-
rspec-mocks (3.7.0)
|
24
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
25
|
-
rspec-support (~> 3.7.0)
|
26
|
-
rspec-support (3.7.1)
|
27
|
-
sidekiq (5.1.3)
|
28
|
-
concurrent-ruby (~> 1.0)
|
29
|
-
connection_pool (~> 2.2, >= 2.2.0)
|
30
|
-
rack-protection (>= 1.5.0)
|
31
|
-
redis (>= 3.3.5, < 5)
|
32
|
-
|
33
|
-
PLATFORMS
|
34
|
-
ruby
|
35
|
-
|
36
|
-
DEPENDENCIES
|
37
|
-
eldritch
|
38
|
-
rspec
|
39
|
-
sidekiq
|
40
|
-
|
41
|
-
BUNDLED WITH
|
42
|
-
1.16.1
|