atomic-sidekiq 1.1.0 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +7 -0
- data/README.md +9 -0
- data/atomic-sidekiq.gemspec +2 -1
- data/config.ru +10 -0
- data/docker-compose.yml +2 -0
- data/images/in_flight_web.png +0 -0
- data/images/recovered_web.png +0 -0
- data/lib/atomic_sidekiq.rb +3 -0
- data/lib/atomic_sidekiq/dead_job_collector.rb +7 -2
- data/lib/atomic_sidekiq/in_flight_keymaker.rb +4 -0
- data/lib/atomic_sidekiq/in_flight_queue.rb +35 -0
- data/lib/atomic_sidekiq/recovered_stats.rb +57 -0
- data/lib/atomic_sidekiq/web.rb +35 -0
- data/web/views/_paging.erb +23 -0
- data/web/views/in_flight.erb +44 -0
- data/web/views/recovered.erb +35 -0
- metadata +24 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af06b1517443b34e0a8e08127c1d730c81658c73
|
4
|
+
data.tar.gz: 01ad8568be11fa6c4e9c67da7422efe903d2b189
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e9aa27c6fd049d84c0a8010ae8a0a636e3bbfe15ee39a5ad194ef5f7193dcca5779ea5bfe80e257ff67f87f0717da967cf8bf0e802bcb1a4ea2e7a8f45dd561
|
7
|
+
data.tar.gz: 340e2d9f8997c905d0e6c0df0c11baff6aa7d1218e447fd3770daacc61d513fabad4662a03b2bd36ec437c96b476fd098d5ed644a1338af64296a8142873d39c
|
data/Gemfile.lock
CHANGED
@@ -14,8 +14,10 @@ GEM
|
|
14
14
|
url
|
15
15
|
concurrent-ruby (1.0.5)
|
16
16
|
connection_pool (2.2.1)
|
17
|
+
daemons (1.2.6)
|
17
18
|
diff-lcs (1.3)
|
18
19
|
docile (1.3.0)
|
20
|
+
eventmachine (1.2.5)
|
19
21
|
json (2.1.0)
|
20
22
|
parallel (1.12.1)
|
21
23
|
parser (2.5.0.5)
|
@@ -58,6 +60,10 @@ GEM
|
|
58
60
|
json (>= 1.8, < 3)
|
59
61
|
simplecov-html (~> 0.10.0)
|
60
62
|
simplecov-html (0.10.2)
|
63
|
+
thin (1.7.2)
|
64
|
+
daemons (~> 1.0, >= 1.0.9)
|
65
|
+
eventmachine (~> 1.0, >= 1.0.4)
|
66
|
+
rack (>= 1, < 3)
|
61
67
|
timecop (0.9.1)
|
62
68
|
unicode-display_width (1.3.0)
|
63
69
|
url (0.3.2)
|
@@ -72,6 +78,7 @@ DEPENDENCIES
|
|
72
78
|
rake (~> 11.3)
|
73
79
|
rspec
|
74
80
|
rubocop
|
81
|
+
thin
|
75
82
|
timecop
|
76
83
|
|
77
84
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -73,6 +73,15 @@ The performance test uses the default settings for both fetchers (default and At
|
|
73
73
|
|
74
74
|
The reliability improvements of AtomicSidekiq come at the cost of less throughput. AtomicSidekiq's algorithm is linear instead of constant like Sidekiq's default, meaning that the cost of performance increases linearly as more jobs are added to the queue.
|
75
75
|
|
76
|
+
## Web
|
77
|
+
AtomicSidekiq provides two different pages for checking stats on the job reliability. One shows which jobs are currently "in-flight" status (even if they might have exited uexpectedly) and how long before they expire. A second page shows how many jobs have been recovered by queue and by worker class.
|
78
|
+
|
79
|
+
![In-flight Web UI](https://raw.githubusercontent.com/Colex/atomic-sidekiq/master/images/in_flight_web.png)
|
80
|
+
|
81
|
+
_"Estimated Lost"_ shows how many jobs in-flight might have been lost (this is calculated by looking how many jobs are in "Busy" and how many are "In-flight").
|
82
|
+
|
83
|
+
![Recovered Stats Web UI](https://raw.githubusercontent.com/Colex/atomic-sidekiq/master/images/recovered_web.png)
|
84
|
+
|
76
85
|
## Tests
|
77
86
|
```sh
|
78
87
|
bundle exec rspec
|
data/atomic-sidekiq.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "atomic-sidekiq"
|
5
|
-
s.version = "1.1.
|
5
|
+
s.version = "1.1.2"
|
6
6
|
s.date = "2018-04-02"
|
7
7
|
s.summary = "Reliable fetcher for Sidekiq"
|
8
8
|
s.description = "Reliable fetcher for Sidekiq"
|
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_development_dependency "rubocop", "~> 0.54"
|
22
22
|
s.add_development_dependency "timecop", "~> 0.9"
|
23
23
|
s.add_development_dependency "codecov", ">= 0.1.10"
|
24
|
+
s.add_development_dependency "thin"
|
24
25
|
|
25
26
|
s.add_runtime_dependency "sidekiq", "~> 5.0"
|
26
27
|
end
|
data/config.ru
ADDED
data/docker-compose.yml
CHANGED
Binary file
|
Binary file
|
data/lib/atomic_sidekiq.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
require "sidekiq"
|
2
2
|
require_relative "atomic_sidekiq/sidekiq/sidekiq"
|
3
|
+
require_relative "atomic_sidekiq/in_flight_queue"
|
3
4
|
require_relative "atomic_sidekiq/in_flight_keymaker"
|
4
5
|
require_relative "atomic_sidekiq/unit_of_work"
|
5
6
|
require_relative "atomic_sidekiq/atomic_fetch"
|
6
7
|
require_relative "atomic_sidekiq/dead_job_collector"
|
7
8
|
require_relative "atomic_sidekiq/heartbeat"
|
9
|
+
require_relative "atomic_sidekiq/recovered_stats"
|
10
|
+
require_relative "atomic_sidekiq/web"
|
8
11
|
require_relative "atomic_sidekiq/atomic_operation/base"
|
9
12
|
require_relative "atomic_sidekiq/atomic_operation/acknowledge"
|
10
13
|
require_relative "atomic_sidekiq/atomic_operation/requeue"
|
@@ -9,6 +9,7 @@ module AtomicSidekiq
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def initialize(queue, in_flight_keymaker:)
|
12
|
+
@recovered_stats = RecoveredStats.new
|
12
13
|
@queue = queue
|
13
14
|
@in_flight_keymaker = in_flight_keymaker
|
14
15
|
@expire_op = AtomicOperation::Expire.new
|
@@ -20,10 +21,14 @@ module AtomicSidekiq
|
|
20
21
|
|
21
22
|
private
|
22
23
|
|
23
|
-
attr_reader :queue, :in_flight_keymaker, :expire_op
|
24
|
+
attr_reader :queue, :in_flight_keymaker, :expire_op, :recovered_stats
|
24
25
|
|
25
26
|
def expire!(job_key)
|
26
|
-
expire_op.perform(queue, job_key)
|
27
|
+
recovered = expire_op.perform(queue, job_key)
|
28
|
+
return if recovered.nil?
|
29
|
+
job = JSON.parse(recovered[1])
|
30
|
+
recovered_stats.increment!(job)
|
31
|
+
job
|
27
32
|
end
|
28
33
|
|
29
34
|
def each_keys
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module AtomicSidekiq
|
2
|
+
class InFlightQueue
|
3
|
+
def initialize
|
4
|
+
@keymaker = InFlightKeymaker.new(AtomicFetch::IN_FLIGHT_KEY_PREFIX)
|
5
|
+
end
|
6
|
+
|
7
|
+
def list
|
8
|
+
keys = list_keys
|
9
|
+
retrieve_jobs(keys)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :keymaker
|
15
|
+
|
16
|
+
def list_keys
|
17
|
+
matcher = keymaker.matcher
|
18
|
+
result = []
|
19
|
+
it = 0
|
20
|
+
loop do
|
21
|
+
it, keys = Sidekiq.redis { |conn| conn.scan(it, match: matcher) }
|
22
|
+
result.concat(keys)
|
23
|
+
it = it.to_i
|
24
|
+
break if it.zero?
|
25
|
+
end
|
26
|
+
result
|
27
|
+
end
|
28
|
+
|
29
|
+
def retrieve_jobs(keys)
|
30
|
+
Sidekiq.redis do |conn|
|
31
|
+
keys.map { |key| JSON.parse(conn.get(key)) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module AtomicSidekiq
|
2
|
+
class RecoveredStats
|
3
|
+
def increment!(job)
|
4
|
+
puts "INCREMENTING"
|
5
|
+
increment_by_job!(job["class"])
|
6
|
+
increment_by_queue!(job["queue"])
|
7
|
+
end
|
8
|
+
|
9
|
+
def stats_by_queue
|
10
|
+
iterate_stats(queue_prefix)
|
11
|
+
end
|
12
|
+
|
13
|
+
def stats_by_job
|
14
|
+
iterate_stats(job_prefix)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def iterate_stats(prefix)
|
20
|
+
iterate_keys(prefix).map do |key|
|
21
|
+
value = Sidekiq.redis { |conn| conn.get(key) }
|
22
|
+
[key.gsub(Regexp.new("#{prefix}:"), ""), value]
|
23
|
+
end.to_h
|
24
|
+
end
|
25
|
+
|
26
|
+
def iterate_keys(prefix)
|
27
|
+
it = 0
|
28
|
+
result = []
|
29
|
+
loop do
|
30
|
+
it, keys = Sidekiq.redis { |conn| conn.scan(it, match: "#{prefix}:*") }
|
31
|
+
result.concat(keys)
|
32
|
+
it = it.to_i
|
33
|
+
return result if it.zero?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def increment_by_job!(job_name)
|
38
|
+
Sidekiq.redis { |conn| conn.incr("#{job_prefix}:#{job_name}") }
|
39
|
+
end
|
40
|
+
|
41
|
+
def increment_by_queue!(queue)
|
42
|
+
Sidekiq.redis { |conn| conn.incr("#{queue_prefix}:#{queue}") }
|
43
|
+
end
|
44
|
+
|
45
|
+
def prefix
|
46
|
+
"atomic_sidekiq"
|
47
|
+
end
|
48
|
+
|
49
|
+
def queue_prefix
|
50
|
+
"#{prefix}:queue"
|
51
|
+
end
|
52
|
+
|
53
|
+
def job_prefix
|
54
|
+
"#{prefix}:job"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module AtomicSidekiq
|
2
|
+
module Web
|
3
|
+
VIEW_PATH = File.expand_path("../../web/views", __dir__)
|
4
|
+
|
5
|
+
def self.registered(app)
|
6
|
+
app.get "/in-flight" do
|
7
|
+
Web.render_in_flight
|
8
|
+
end
|
9
|
+
|
10
|
+
app.get "/recovered" do
|
11
|
+
Web.render_recovered
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.render_in_flight
|
16
|
+
@jobs = AtomicSidekiq::InFlightQueue.new.list
|
17
|
+
@total_size = @jobs.count
|
18
|
+
@count = 25
|
19
|
+
@current_page = (params[:page] || 1).to_i
|
20
|
+
@jobs = @jobs[@current_page..(@current_page + @count)]
|
21
|
+
erb File.read(File.join(VIEW_PATH, "in_flight.erb"))
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.render_recovered
|
25
|
+
@queues = AtomicSidekiq::RecoveredStats.new.stats_by_queue
|
26
|
+
@jobs = AtomicSidekiq::RecoveredStats.new.stats_by_job
|
27
|
+
erb File.read(File.join(VIEW_PATH, "recovered.erb"))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
require "sidekiq/web" unless defined?(Sidekiq::Web)
|
33
|
+
Sidekiq::Web.register(AtomicSidekiq::Web)
|
34
|
+
Sidekiq::Web.tabs["In-flight"] = "in-flight"
|
35
|
+
Sidekiq::Web.tabs["Recovered"] = "recovered"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<% if @total_size > @count %>
|
2
|
+
<ul class="pagination pull-right flip">
|
3
|
+
<li class="<%= "disabled" if @current_page == 1 %>">
|
4
|
+
<a href="<%= url %>?page=1">«</a>
|
5
|
+
</li>
|
6
|
+
<% if @current_page > 1 %>
|
7
|
+
<li>
|
8
|
+
<a href="<%= url %>?<%= qparams(page: @current_page - 1) %>"><%= @current_page - 1 %></a>
|
9
|
+
</li>
|
10
|
+
<% end %>
|
11
|
+
<li class="disabled">
|
12
|
+
<a href="<%= url %>?<%= qparams(page: @current_page) %>"><%= @current_page %></a>
|
13
|
+
</li>
|
14
|
+
<% if @total_size > @current_page * @count %>
|
15
|
+
<li>
|
16
|
+
<a href="<%= url %>?<%= qparams(page: @current_page + 1) %>"><%= @current_page + 1 %></a>
|
17
|
+
</li>
|
18
|
+
<% end %>
|
19
|
+
<li class="<%= "disabled" if @total_size <= @current_page * @count %>">
|
20
|
+
<a href="<%= url %>?<%= qparams(page: (@total_size.to_f / @count).ceil) %>">»</a>
|
21
|
+
</li>
|
22
|
+
</ul>
|
23
|
+
<% end %>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<header class="row">
|
2
|
+
<div class="col-sm-6">
|
3
|
+
<h3><%= t("In-flight") %></h3>
|
4
|
+
</div>
|
5
|
+
<div class="col-sm-4">
|
6
|
+
<%= erb :_paging, locals: { url: "#{root_path}in-flight" } %>
|
7
|
+
</div>
|
8
|
+
</header>
|
9
|
+
|
10
|
+
<div class="table_container">
|
11
|
+
<table class="inflight table table-hover table-bordered table-striped table-white">
|
12
|
+
<thead>
|
13
|
+
<th><%= t("Total In-flight") %></th>
|
14
|
+
<th><%= t("Estimated Lost") %></th>
|
15
|
+
</thead>
|
16
|
+
<tr>
|
17
|
+
<td><%= number_with_delimiter(@total_size) %> </td>
|
18
|
+
<td><%= number_with_delimiter(@total_size - workers.size) %> </td>
|
19
|
+
</tr>
|
20
|
+
</table>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div class="table_container">
|
24
|
+
<table class="inflight table table-hover table-bordered table-striped table-white">
|
25
|
+
<thead>
|
26
|
+
<th><%= t("Job") %></th>
|
27
|
+
<th><%= t("Args") %></th>
|
28
|
+
<th><%= t("Queue") %></th>
|
29
|
+
<th><%= t("Expires at") %></th>
|
30
|
+
</thead>
|
31
|
+
<% @jobs.each do |job| %>
|
32
|
+
<tr>
|
33
|
+
<td><%= h job["class"] %> </td>
|
34
|
+
<td><%= h job["args"] %> </td>
|
35
|
+
<td>
|
36
|
+
<a href="<%= root_path %>queues/<%= CGI.escape(job["queue"]) %>"><%= h job["queue"] %></a>
|
37
|
+
</td>
|
38
|
+
<td>
|
39
|
+
<%= relative_time(Time.at(job["expire_at"])) %>
|
40
|
+
</td>
|
41
|
+
</tr>
|
42
|
+
<% end %>
|
43
|
+
</table>
|
44
|
+
</div>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<header class="row">
|
2
|
+
<div class="col-sm-12">
|
3
|
+
<h3><%= t("Recovered Jobs") %></h3>
|
4
|
+
</div>
|
5
|
+
</header>
|
6
|
+
|
7
|
+
<div class="table_container">
|
8
|
+
<table class="recovered table table-hover table-bordered table-striped table-white">
|
9
|
+
<thead>
|
10
|
+
<th><%= t("Queue") %></th>
|
11
|
+
<th><%= t("Count") %></th>
|
12
|
+
</thead>
|
13
|
+
<% @queues.each do |key, count| %>
|
14
|
+
<tr>
|
15
|
+
<td><%= h key %> </td>
|
16
|
+
<td><%= h count %> </td>
|
17
|
+
</tr>
|
18
|
+
<% end %>
|
19
|
+
</table>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<div class="table_container">
|
23
|
+
<table class="recovered table table-hover table-bordered table-striped table-white">
|
24
|
+
<thead>
|
25
|
+
<th><%= t("Job Name") %></th>
|
26
|
+
<th><%= t("Count") %></th>
|
27
|
+
</thead>
|
28
|
+
<% @jobs.each do |key, count| %>
|
29
|
+
<tr>
|
30
|
+
<td><%= h key %> </td>
|
31
|
+
<td><%= h count %> </td>
|
32
|
+
</tr>
|
33
|
+
<% end %>
|
34
|
+
</table>
|
35
|
+
</div>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: atomic-sidekiq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Correia Santos
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 0.1.10
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: thin
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: sidekiq
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,8 +143,11 @@ files:
|
|
129
143
|
- bin/sidekiqfail
|
130
144
|
- bin/sidekiqload
|
131
145
|
- bin/test
|
146
|
+
- config.ru
|
132
147
|
- docker-compose.yml
|
133
148
|
- docker.env
|
149
|
+
- images/in_flight_web.png
|
150
|
+
- images/recovered_web.png
|
134
151
|
- lib/atomic-sidekiq.rb
|
135
152
|
- lib/atomic_sidekiq.rb
|
136
153
|
- lib/atomic_sidekiq/atomic_fetch.rb
|
@@ -146,8 +163,14 @@ files:
|
|
146
163
|
- lib/atomic_sidekiq/dead_job_collector.rb
|
147
164
|
- lib/atomic_sidekiq/heartbeat.rb
|
148
165
|
- lib/atomic_sidekiq/in_flight_keymaker.rb
|
166
|
+
- lib/atomic_sidekiq/in_flight_queue.rb
|
167
|
+
- lib/atomic_sidekiq/recovered_stats.rb
|
149
168
|
- lib/atomic_sidekiq/sidekiq/sidekiq.rb
|
150
169
|
- lib/atomic_sidekiq/unit_of_work.rb
|
170
|
+
- lib/atomic_sidekiq/web.rb
|
171
|
+
- web/views/_paging.erb
|
172
|
+
- web/views/in_flight.erb
|
173
|
+
- web/views/recovered.erb
|
151
174
|
homepage: https://github.com/Colex/atomic-sidekiq
|
152
175
|
licenses:
|
153
176
|
- MIT
|