sidekiq 7.1.6 → 7.2.1
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/Changes.md +48 -0
- data/bin/multi_queue_bench +268 -0
- data/lib/sidekiq/api.rb +60 -8
- data/lib/sidekiq/config.rb +4 -4
- data/lib/sidekiq/deploy.rb +1 -1
- data/lib/sidekiq/job_retry.rb +3 -3
- data/lib/sidekiq/metrics/query.rb +4 -1
- data/lib/sidekiq/metrics/tracking.rb +7 -3
- data/lib/sidekiq/paginator.rb +2 -2
- data/lib/sidekiq/rails.rb +3 -3
- data/lib/sidekiq/redis_client_adapter.rb +16 -0
- data/lib/sidekiq/redis_connection.rb +2 -3
- data/lib/sidekiq/scheduled.rb +1 -1
- data/lib/sidekiq/testing.rb +8 -2
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/application.rb +17 -1
- data/lib/sidekiq/web/csrf_protection.rb +6 -3
- data/lib/sidekiq/web/helpers.rb +8 -14
- data/sidekiq.gemspec +1 -1
- data/web/assets/javascripts/application.js +15 -0
- data/web/assets/javascripts/dashboard-charts.js +14 -0
- data/web/assets/javascripts/dashboard.js +7 -9
- data/web/assets/javascripts/metrics.js +34 -0
- data/web/assets/stylesheets/application.css +9 -0
- data/web/views/_metrics_period_select.erb +1 -1
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +7 -7
- data/web/views/dashboard.erb +23 -33
- data/web/views/filtering.erb +4 -4
- data/web/views/metrics.erb +36 -27
- data/web/views/metrics_for_job.erb +26 -35
- data/web/views/queues.erb +6 -2
- metadata +5 -4
@@ -15,7 +15,7 @@ module Sidekiq
|
|
15
15
|
"manifest-src 'self'",
|
16
16
|
"media-src 'self'",
|
17
17
|
"object-src 'none'",
|
18
|
-
"script-src 'self' https: http:
|
18
|
+
"script-src 'self' https: http:",
|
19
19
|
"style-src 'self' https: http: 'unsafe-inline'",
|
20
20
|
"worker-src 'self'",
|
21
21
|
"base-uri 'self'"
|
@@ -330,6 +330,22 @@ module Sidekiq
|
|
330
330
|
|
331
331
|
########
|
332
332
|
# Filtering
|
333
|
+
|
334
|
+
get "/filter/metrics" do
|
335
|
+
redirect "#{root_path}metrics"
|
336
|
+
end
|
337
|
+
|
338
|
+
post "/filter/metrics" do
|
339
|
+
x = params[:substr]
|
340
|
+
q = Sidekiq::Metrics::Query.new
|
341
|
+
@period = h((params[:period] || "")[0..1])
|
342
|
+
@periods = METRICS_PERIODS
|
343
|
+
minutes = @periods.fetch(@period, @periods.values.first)
|
344
|
+
@query_result = q.top_jobs(minutes: minutes, class_filter: Regexp.new(Regexp.escape(x), Regexp::IGNORECASE))
|
345
|
+
|
346
|
+
erb :metrics
|
347
|
+
end
|
348
|
+
|
333
349
|
get "/filter/retries" do
|
334
350
|
x = params[:substr]
|
335
351
|
return redirect "#{root_path}retries" unless x && x != ""
|
@@ -27,7 +27,6 @@
|
|
27
27
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
28
28
|
|
29
29
|
require "securerandom"
|
30
|
-
require "base64"
|
31
30
|
require "rack/request"
|
32
31
|
|
33
32
|
module Sidekiq
|
@@ -143,7 +142,7 @@ module Sidekiq
|
|
143
142
|
one_time_pad = SecureRandom.random_bytes(token.length)
|
144
143
|
encrypted_token = xor_byte_strings(one_time_pad, token)
|
145
144
|
masked_token = one_time_pad + encrypted_token
|
146
|
-
|
145
|
+
encode_token(masked_token)
|
147
146
|
end
|
148
147
|
|
149
148
|
# Essentially the inverse of +mask_token+.
|
@@ -168,8 +167,12 @@ module Sidekiq
|
|
168
167
|
::Rack::Utils.secure_compare(token.to_s, decode_token(local).to_s)
|
169
168
|
end
|
170
169
|
|
170
|
+
def encode_token(token)
|
171
|
+
[token].pack("m0").tr("+/", "-_")
|
172
|
+
end
|
173
|
+
|
171
174
|
def decode_token(token)
|
172
|
-
|
175
|
+
token.tr("-_", "+/").unpack1("m0")
|
173
176
|
end
|
174
177
|
|
175
178
|
def xor_byte_strings(s1, s2)
|
data/lib/sidekiq/web/helpers.rb
CHANGED
@@ -21,6 +21,10 @@ module Sidekiq
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
def to_json(x)
|
25
|
+
Sidekiq.dump_json(x)
|
26
|
+
end
|
27
|
+
|
24
28
|
def singularize(str, count)
|
25
29
|
if count == 1 && str.respond_to?(:singularize) # rails
|
26
30
|
str.singularize
|
@@ -292,23 +296,13 @@ module Sidekiq
|
|
292
296
|
elsif rss_kb < 10_000_000
|
293
297
|
"#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
|
294
298
|
else
|
295
|
-
"#{number_with_delimiter((rss_kb / (1024.0 * 1024.0))
|
299
|
+
"#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)), precision: 1)} GB"
|
296
300
|
end
|
297
301
|
end
|
298
302
|
|
299
|
-
def number_with_delimiter(number)
|
300
|
-
|
301
|
-
|
302
|
-
begin
|
303
|
-
Float(number)
|
304
|
-
rescue ArgumentError, TypeError
|
305
|
-
return number
|
306
|
-
end
|
307
|
-
|
308
|
-
options = {delimiter: ",", separator: "."}
|
309
|
-
parts = number.to_s.to_str.split(".")
|
310
|
-
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
|
311
|
-
parts.join(options[:separator])
|
303
|
+
def number_with_delimiter(number, options = {})
|
304
|
+
precision = options[:precision] || 0
|
305
|
+
%(<span data-nwp="#{precision}">#{number.round(precision)}</span>)
|
312
306
|
end
|
313
307
|
|
314
308
|
def h(text)
|
data/sidekiq.gemspec
CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |gem|
|
|
23
23
|
"rubygems_mfa_required" => "true"
|
24
24
|
}
|
25
25
|
|
26
|
-
gem.add_dependency "redis-client", ">= 0.
|
26
|
+
gem.add_dependency "redis-client", ">= 0.19.0"
|
27
27
|
gem.add_dependency "connection_pool", ">= 2.3.0"
|
28
28
|
gem.add_dependency "rack", ">= 2.2.4"
|
29
29
|
gem.add_dependency "concurrent-ruby", "< 2"
|
@@ -33,6 +33,7 @@ function addListeners() {
|
|
33
33
|
|
34
34
|
addShiftClickListeners()
|
35
35
|
updateFuzzyTimes();
|
36
|
+
updateNumbers();
|
36
37
|
setLivePollFromUrl();
|
37
38
|
|
38
39
|
var buttons = document.querySelectorAll(".live-poll");
|
@@ -102,6 +103,20 @@ function updateFuzzyTimes() {
|
|
102
103
|
t.cancel();
|
103
104
|
}
|
104
105
|
|
106
|
+
function updateNumbers() {
|
107
|
+
document.querySelectorAll("[data-nwp]").forEach(node => {
|
108
|
+
let number = parseFloat(node.textContent);
|
109
|
+
let precision = parseInt(node.dataset["nwp"] || 0);
|
110
|
+
if (typeof number === "number") {
|
111
|
+
let formatted = number.toLocaleString(undefined, {
|
112
|
+
minimumFractionDigits: precision,
|
113
|
+
maximumFractionDigits: precision,
|
114
|
+
});
|
115
|
+
node.textContent = formatted;
|
116
|
+
}
|
117
|
+
});
|
118
|
+
}
|
119
|
+
|
105
120
|
function setLivePollFromUrl() {
|
106
121
|
var url_params = new URL(window.location.href).searchParams
|
107
122
|
|
@@ -86,6 +86,7 @@ class RealtimeChart extends DashboardChart {
|
|
86
86
|
updateStatsSummary(this.stats.sidekiq);
|
87
87
|
updateRedisStats(this.stats.redis);
|
88
88
|
updateFooterUTCTime(this.stats.server_utc_time);
|
89
|
+
updateNumbers();
|
89
90
|
pulseBeacon();
|
90
91
|
|
91
92
|
this.stats = stats;
|
@@ -166,3 +167,16 @@ class RealtimeChart extends DashboardChart {
|
|
166
167
|
};
|
167
168
|
}
|
168
169
|
}
|
170
|
+
|
171
|
+
var rc = document.getElementById("realtime-chart")
|
172
|
+
if (rc != null) {
|
173
|
+
var rtc = new RealtimeChart(rc, JSON.parse(rc.textContent))
|
174
|
+
rtc.registerLegend(document.getElementById("realtime-legend"))
|
175
|
+
window.realtimeChart = rtc
|
176
|
+
}
|
177
|
+
|
178
|
+
var hc = document.getElementById("history-chart")
|
179
|
+
if (hc != null) {
|
180
|
+
var htc = new DashboardChart(hc, JSON.parse(hc.textContent))
|
181
|
+
window.historyChart = htc
|
182
|
+
}
|
@@ -1,15 +1,13 @@
|
|
1
1
|
Sidekiq = {};
|
2
2
|
|
3
|
-
var nf = new Intl.NumberFormat();
|
4
|
-
|
5
3
|
var updateStatsSummary = function(data) {
|
6
|
-
document.getElementById("txtProcessed").innerText =
|
7
|
-
document.getElementById("txtFailed").innerText =
|
8
|
-
document.getElementById("txtBusy").innerText =
|
9
|
-
document.getElementById("txtScheduled").innerText =
|
10
|
-
document.getElementById("txtRetries").innerText =
|
11
|
-
document.getElementById("txtEnqueued").innerText =
|
12
|
-
document.getElementById("txtDead").innerText =
|
4
|
+
document.getElementById("txtProcessed").innerText = data.processed;
|
5
|
+
document.getElementById("txtFailed").innerText = data.failed;
|
6
|
+
document.getElementById("txtBusy").innerText = data.busy;
|
7
|
+
document.getElementById("txtScheduled").innerText = data.scheduled;
|
8
|
+
document.getElementById("txtRetries").innerText = data.retries;
|
9
|
+
document.getElementById("txtEnqueued").innerText = data.enqueued;
|
10
|
+
document.getElementById("txtDead").innerText = data.dead;
|
13
11
|
}
|
14
12
|
|
15
13
|
var updateRedisStats = function(data) {
|
@@ -262,3 +262,37 @@ class HistBubbleChart extends BaseChart {
|
|
262
262
|
};
|
263
263
|
}
|
264
264
|
}
|
265
|
+
|
266
|
+
var ch = document.getElementById("job-metrics-overview-chart");
|
267
|
+
if (ch != null) {
|
268
|
+
var jm = new JobMetricsOverviewChart(ch, JSON.parse(ch.textContent));
|
269
|
+
document.querySelectorAll(".metrics-swatch-wrapper > input[type=checkbox]").forEach((imp) => {
|
270
|
+
jm.registerSwatch(imp.id)
|
271
|
+
});
|
272
|
+
window.jobMetricsChart = jm;
|
273
|
+
}
|
274
|
+
|
275
|
+
var htc = document.getElementById("hist-totals-chart");
|
276
|
+
if (htc != null) {
|
277
|
+
var tc = new HistTotalsChart(htc, JSON.parse(htc.textContent));
|
278
|
+
window.histTotalsChart = tc
|
279
|
+
}
|
280
|
+
|
281
|
+
var hbc = document.getElementById("hist-bubble-chart");
|
282
|
+
if (hbc != null) {
|
283
|
+
var bc = new HistBubbleChart(hbc, JSON.parse(hbc.textContent));
|
284
|
+
window.histBubbleChart = bc
|
285
|
+
}
|
286
|
+
|
287
|
+
var form = document.getElementById("metrics-form")
|
288
|
+
document.querySelectorAll("#period-selector").forEach(node => {
|
289
|
+
node.addEventListener("input", debounce(() => form.submit()))
|
290
|
+
})
|
291
|
+
|
292
|
+
function debounce(func, timeout = 300) {
|
293
|
+
let timer;
|
294
|
+
return (...args) => {
|
295
|
+
clearTimeout(timer);
|
296
|
+
timer = setTimeout(() => { func.apply(this, args); }, timeout);
|
297
|
+
};
|
298
|
+
}
|
data/web/views/_summary.erb
CHANGED
@@ -1,39 +1,39 @@
|
|
1
1
|
<ul class="list-unstyled summary row">
|
2
2
|
<li class="processed col-sm-1">
|
3
|
-
<span id="txtProcessed" class="count"><%=
|
3
|
+
<span id="txtProcessed" class="count" data-nwp><%= stats.processed %></span>
|
4
4
|
<span class="desc"><%= t('Processed') %></span>
|
5
5
|
</li>
|
6
6
|
<li class="failed col-sm-1">
|
7
|
-
<span id="txtFailed" class="count"><%=
|
7
|
+
<span id="txtFailed" class="count" data-nwp><%= stats.failed %></span>
|
8
8
|
<span class="desc"><%= t('Failed') %></span>
|
9
9
|
</li>
|
10
10
|
<li class="busy col-sm-1">
|
11
11
|
<a href="<%= root_path %>busy">
|
12
|
-
<span id="txtBusy" class="count"><%=
|
12
|
+
<span id="txtBusy" class="count" data-nwp><%= workset.size %></span>
|
13
13
|
<span class="desc"><%= t('Busy') %></span>
|
14
14
|
</a>
|
15
15
|
</li>
|
16
16
|
<li class="enqueued col-sm-1">
|
17
17
|
<a href="<%= root_path %>queues">
|
18
|
-
<span id="txtEnqueued" class="count"><%=
|
18
|
+
<span id="txtEnqueued" class="count" data-nwp><%= stats.enqueued %></span>
|
19
19
|
<span class="desc"><%= t('Enqueued') %></span>
|
20
20
|
</a>
|
21
21
|
</li>
|
22
22
|
<li class="retries col-sm-1">
|
23
23
|
<a href="<%= root_path %>retries">
|
24
|
-
<span id="txtRetries" class="count"><%=
|
24
|
+
<span id="txtRetries" class="count" data-nwp><%= stats.retry_size %></span>
|
25
25
|
<span class="desc"><%= t('Retries') %></span>
|
26
26
|
</a>
|
27
27
|
</li>
|
28
28
|
<li class="scheduled col-sm-1">
|
29
29
|
<a href="<%= root_path %>scheduled">
|
30
|
-
<span id="txtScheduled" class="count"><%=
|
30
|
+
<span id="txtScheduled" class="count" data-nwp><%= stats.scheduled_size %></span>
|
31
31
|
<span class="desc"><%= t('Scheduled') %></span>
|
32
32
|
</a>
|
33
33
|
</li>
|
34
34
|
<li class="dead col-sm-1">
|
35
35
|
<a href="<%= root_path %>morgue">
|
36
|
-
<span id="txtDead" class="count"><%=
|
36
|
+
<span id="txtDead" class="count" data-nwp><%= stats.dead_size %></span>
|
37
37
|
<span class="desc"><%= t('Dead') %></span>
|
38
38
|
</a>
|
39
39
|
</li>
|
data/web/views/busy.erb
CHANGED
@@ -86,9 +86,9 @@
|
|
86
86
|
<% end %>
|
87
87
|
</td>
|
88
88
|
<td><%= relative_time(Time.at(process['started_at'])) %></td>
|
89
|
-
<td><%= format_memory(process['rss'].to_i) %></td>
|
90
|
-
<td><%= process['concurrency'] %></td>
|
91
|
-
<td><%= process['busy'] %></td>
|
89
|
+
<td class="num"><%= format_memory(process['rss'].to_i) %></td>
|
90
|
+
<td class="num"><%= number_with_delimiter(process['concurrency']) %></td>
|
91
|
+
<td class="num"><%= number_with_delimiter(process['busy']) %></td>
|
92
92
|
<td>
|
93
93
|
<% unless process.embedded? %>
|
94
94
|
<form method="POST">
|
@@ -125,14 +125,14 @@
|
|
125
125
|
<th><%= t('Arguments') %></th>
|
126
126
|
<th><%= t('Started') %></th>
|
127
127
|
</thead>
|
128
|
-
<% @workset.each do |process, thread,
|
129
|
-
<% job =
|
128
|
+
<% @workset.each do |process, thread, work| %>
|
129
|
+
<% job = work.job %>
|
130
130
|
<tr>
|
131
131
|
<td><%= process %></td>
|
132
132
|
<td><%= thread %></td>
|
133
133
|
<td><%= job.jid %></td>
|
134
134
|
<td>
|
135
|
-
<a href="<%= root_path %>queues/<%=
|
135
|
+
<a href="<%= root_path %>queues/<%= work.queue %>"><%= work.queue %></a>
|
136
136
|
</td>
|
137
137
|
<td>
|
138
138
|
<%= job.display_class %>
|
@@ -141,7 +141,7 @@
|
|
141
141
|
<td>
|
142
142
|
<div class="args"><%= display_args(job.display_args) %></div>
|
143
143
|
</td>
|
144
|
-
<td><%= relative_time(
|
144
|
+
<td><%= relative_time(work.run_at) %></td>
|
145
145
|
</tr>
|
146
146
|
<% end %>
|
147
147
|
</table>
|
data/web/views/dashboard.erb
CHANGED
@@ -1,7 +1,3 @@
|
|
1
|
-
<script type="text/javascript" src="<%= root_path %>javascripts/chart.min.js"></script>
|
2
|
-
<script type="text/javascript" src="<%= root_path %>javascripts/chartjs-plugin-annotation.min.js"></script>
|
3
|
-
<script type="text/javascript" src="<%= root_path %>javascripts/base-charts.js"></script>
|
4
|
-
<script type="text/javascript" src="<%= root_path %>javascripts/dashboard-charts.js"></script>
|
5
1
|
<script type="text/javascript" src="<%= root_path %>javascripts/dashboard.js"></script>
|
6
2
|
<div class= "dashboard clearfix">
|
7
3
|
<h3 >
|
@@ -20,26 +16,19 @@
|
|
20
16
|
</div>
|
21
17
|
|
22
18
|
<div class="row chart">
|
23
|
-
<canvas id="realtime-chart"
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
updateUrl: "#{root_path}stats",
|
34
|
-
}) %>
|
35
|
-
)
|
36
|
-
</script>
|
19
|
+
<canvas id="realtime-chart">
|
20
|
+
<%= to_json({
|
21
|
+
processedLabel: t('Processed'),
|
22
|
+
failedLabel: t('Failed'),
|
23
|
+
labels: Array.new(50, ""),
|
24
|
+
processed: Array.new(50),
|
25
|
+
failed: Array.new(50),
|
26
|
+
updateUrl: "#{root_path}stats",
|
27
|
+
}) %>
|
28
|
+
</canvas>
|
37
29
|
|
38
30
|
<!-- start with a space in the legend so the height doesn't change when we add content dynamically -->
|
39
31
|
<div id="realtime-legend"> </div>
|
40
|
-
<script>
|
41
|
-
realtimeChart.registerLegend(document.getElementById("realtime-legend"))
|
42
|
-
</script>
|
43
32
|
</div>
|
44
33
|
|
45
34
|
<div class="row header">
|
@@ -55,18 +44,14 @@
|
|
55
44
|
<a href="<%= root_path %>?days=180" class="history-graph <%= "active" if params[:days] == "180" %>"><%= t('SixMonths') %></a>
|
56
45
|
</div>
|
57
46
|
|
58
|
-
<canvas id="history-chart"
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
failed: @failed_history.to_a.reverse,
|
67
|
-
}) %>
|
68
|
-
)
|
69
|
-
</script>
|
47
|
+
<canvas id="history-chart">
|
48
|
+
<%= to_json({
|
49
|
+
processedLabel: t('Processed'),
|
50
|
+
failedLabel: t('Failed'),
|
51
|
+
processed: @processed_history.to_a.reverse,
|
52
|
+
failed: @failed_history.to_a.reverse,
|
53
|
+
}) %>
|
54
|
+
</canvas>
|
70
55
|
</div>
|
71
56
|
|
72
57
|
<br/>
|
@@ -113,3 +98,8 @@
|
|
113
98
|
<% end %>
|
114
99
|
</div>
|
115
100
|
</div>
|
101
|
+
|
102
|
+
<script type="text/javascript" src="<%= root_path %>javascripts/chart.min.js"></script>
|
103
|
+
<script type="text/javascript" src="<%= root_path %>javascripts/chartjs-plugin-annotation.min.js"></script>
|
104
|
+
<script type="text/javascript" src="<%= root_path %>javascripts/base-charts.js"></script>
|
105
|
+
<script type="text/javascript" src="<%= root_path %>javascripts/dashboard-charts.js"></script>
|
data/web/views/filtering.erb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
<div
|
2
|
-
<%=
|
3
|
-
<form method="POST" action='<%= root_path %>filter/<%= which %>' style="display: inline-block">
|
1
|
+
<div>
|
2
|
+
<form method="post" class="form-inline" action='<%= root_path %>filter/<%= which %>'>
|
4
3
|
<%= csrf_tag %>
|
5
|
-
<
|
4
|
+
<label for="substr"><%= t('Filter') %></label>
|
5
|
+
<input class="search form-control" type="search" name="substr" value="<%= h params[:substr] %>" placeholder="<%= t('AnyJobContent') %>"/>
|
6
6
|
</form>
|
7
7
|
</div>
|
data/web/views/metrics.erb
CHANGED
@@ -1,16 +1,29 @@
|
|
1
1
|
<script type="text/javascript" src="<%= root_path %>javascripts/chart.min.js"></script>
|
2
2
|
<script type="text/javascript" src="<%= root_path %>javascripts/chartjs-plugin-annotation.min.js"></script>
|
3
3
|
<script type="text/javascript" src="<%= root_path %>javascripts/base-charts.js"></script>
|
4
|
-
<script type="text/javascript" src="<%= root_path %>javascripts/metrics.js"></script>
|
5
4
|
|
6
5
|
<div class="header-container">
|
7
6
|
<div class="page-title-container">
|
8
7
|
<h1><%= t('Metrics') %></h1>
|
9
|
-
|
10
8
|
<a target="blank" href="https://github.com/sidekiq/sidekiq/wiki/Metrics"><span class="info-circle" title="Click to learn more about metrics">?</span></a>
|
11
9
|
</div>
|
12
10
|
|
13
|
-
|
11
|
+
<div>
|
12
|
+
<form id="metrics-form" class="form-inline" action="<%= root_path %>filter/metrics" method="post">
|
13
|
+
<%= csrf_tag %>
|
14
|
+
<label for="substr"><%= t('Filter') %></label>
|
15
|
+
<input id="class-filter" class="form-control" type="text" name="substr" placeholder="<%= t('Name') %>" value="<%= params[:substr] %>">
|
16
|
+
<select id="period-selector" class="form-control" name="period">
|
17
|
+
<% @periods.each_key do |code| %>
|
18
|
+
<% if code == @period %>
|
19
|
+
<option selected value="<%= code %>"><%= code %></option>
|
20
|
+
<% else %>
|
21
|
+
<option value="<%= code %>"><%= code %></option>
|
22
|
+
<% end %>
|
23
|
+
<% end %>
|
24
|
+
</select>
|
25
|
+
</form>
|
26
|
+
</div>
|
14
27
|
</div>
|
15
28
|
|
16
29
|
<%
|
@@ -21,22 +34,17 @@
|
|
21
34
|
%>
|
22
35
|
|
23
36
|
<% if job_results.any? %>
|
24
|
-
<canvas id="job-metrics-overview-chart"
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
units: t('Seconds').downcase,
|
36
|
-
markLabel: t('Deploy'),
|
37
|
-
}) %>
|
38
|
-
)
|
39
|
-
</script>
|
37
|
+
<canvas id="job-metrics-overview-chart">
|
38
|
+
<%= to_json({
|
39
|
+
series: job_results.map { |(kls, jr)| [kls, jr.dig("series", "s")] }.to_h,
|
40
|
+
marks: @query_result.marks.map { |m| [m.bucket, m.label] },
|
41
|
+
labels: @query_result.buckets,
|
42
|
+
visibleKls: visible_kls,
|
43
|
+
yLabel: t('TotalExecutionTime'),
|
44
|
+
units: t('Seconds').downcase,
|
45
|
+
markLabel: t('Deploy'),
|
46
|
+
}) %>
|
47
|
+
</canvas>
|
40
48
|
<% end %>
|
41
49
|
|
42
50
|
<div class="table_container">
|
@@ -46,8 +54,8 @@
|
|
46
54
|
<th><%= t('Name') %></th>
|
47
55
|
<th><%= t('Success') %></th>
|
48
56
|
<th><%= t('Failure') %></th>
|
49
|
-
<th><%= t('TotalExecutionTime')
|
50
|
-
<th><%= t('AvgExecutionTime')
|
57
|
+
<th><%= t('TotalExecutionTime') %> (Seconds)</th>
|
58
|
+
<th><%= t('AvgExecutionTime') %> (Seconds)</th>
|
51
59
|
</tr>
|
52
60
|
<% if job_results.any? %>
|
53
61
|
<% job_results.each_with_index do |(kls, jr), i| %>
|
@@ -64,12 +72,11 @@
|
|
64
72
|
/>
|
65
73
|
<code><a href="<%= root_path %>metrics/<%= kls %>?period=<%= @period %>"><%= kls %></a></code>
|
66
74
|
</div>
|
67
|
-
<script>jobMetricsChart.registerSwatch("<%= id %>")</script>
|
68
75
|
</td>
|
69
|
-
<td><%= jr.dig("totals", "p") - jr.dig("totals", "f") %></td>
|
70
|
-
<td><%= jr.dig("totals", "f") %></td>
|
71
|
-
<td><%= jr.dig("totals", "s")
|
72
|
-
<td><%= jr.total_avg("s")
|
76
|
+
<td class="num"><%= number_with_delimiter(jr.dig("totals", "p") - jr.dig("totals", "f")) %></td>
|
77
|
+
<td class="num"><%= number_with_delimiter(jr.dig("totals", "f")) %></td>
|
78
|
+
<td class="num"><%= number_with_delimiter(jr.dig("totals", "s"), precision: 2) %></td>
|
79
|
+
<td class="num"><%= number_with_delimiter(jr.total_avg("s"), precision: 2) %></td>
|
73
80
|
</tr>
|
74
81
|
<% end %>
|
75
82
|
<% else %>
|
@@ -79,4 +86,6 @@
|
|
79
86
|
</table>
|
80
87
|
</div>
|
81
88
|
|
82
|
-
|
89
|
+
<!--p><small>Data from <%= @query_result.starts_at %> to <%= @query_result.ends_at %></small></p-->
|
90
|
+
|
91
|
+
<script type="text/javascript" src="<%= root_path %>javascripts/metrics.js"></script>
|
@@ -1,7 +1,6 @@
|
|
1
1
|
<script type="text/javascript" src="<%= root_path %>javascripts/chart.min.js"></script>
|
2
2
|
<script type="text/javascript" src="<%= root_path %>javascripts/chartjs-plugin-annotation.min.js"></script>
|
3
3
|
<script type="text/javascript" src="<%= root_path %>javascripts/base-charts.js"></script>
|
4
|
-
<script type="text/javascript" src="<%= root_path %>javascripts/metrics.js"></script>
|
5
4
|
|
6
5
|
<%
|
7
6
|
job_result = @query_result.job_results[@name]
|
@@ -24,40 +23,30 @@
|
|
24
23
|
<%= erb :_metrics_period_select, locals: { periods: @periods, period: @period, path: "#{root_path}metrics/#{@name}" } %>
|
25
24
|
</div>
|
26
25
|
|
27
|
-
<canvas id="hist-totals-chart"
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
histIntervals: bucket_intervals,
|
52
|
-
yLabel: t('ExecutionTime'),
|
53
|
-
markLabel: t('Deploy'),
|
54
|
-
yUnits: t('Seconds').downcase,
|
55
|
-
zUnits: t('Jobs').downcase,
|
56
|
-
}) %>
|
57
|
-
)
|
58
|
-
</script>
|
59
|
-
|
60
|
-
<p><small>Data from <%= @query_result.starts_at %> to <%= @query_result.ends_at %></small></p>
|
26
|
+
<canvas id="hist-totals-chart">
|
27
|
+
<%= to_json({
|
28
|
+
series: hist_totals,
|
29
|
+
labels: bucket_labels,
|
30
|
+
xLabel: t('ExecutionTime'),
|
31
|
+
yLabel: t('Jobs'),
|
32
|
+
units: t('Jobs').downcase,
|
33
|
+
}) %>
|
34
|
+
</canvas>
|
35
|
+
|
36
|
+
<canvas id="hist-bubble-chart">
|
37
|
+
<%= to_json({
|
38
|
+
hist: job_result.hist,
|
39
|
+
marks: @query_result.marks.map { |m| [m.bucket, m.label] },
|
40
|
+
labels: @query_result.buckets,
|
41
|
+
histIntervals: bucket_intervals,
|
42
|
+
yLabel: t('ExecutionTime'),
|
43
|
+
markLabel: t('Deploy'),
|
44
|
+
yUnits: t('Seconds').downcase,
|
45
|
+
zUnits: t('Jobs').downcase,
|
46
|
+
}) %>
|
47
|
+
</canvas>
|
48
|
+
|
49
|
+
<!--p><small>Data from <%= @query_result.starts_at %> to <%= @query_result.ends_at %></small></p-->
|
61
50
|
<% else %>
|
62
51
|
<h1>
|
63
52
|
<a href="<%= root_path %>/metrics"><%= t('Metrics') %></a> /
|
@@ -66,3 +55,5 @@
|
|
66
55
|
|
67
56
|
<div class="alert alert-success"><%= t('NoJobMetricsFound') %></div>
|
68
57
|
<% end %>
|
58
|
+
|
59
|
+
<script type="text/javascript" src="<%= root_path %>javascripts/metrics.js"></script>
|
data/web/views/queues.erb
CHANGED
@@ -18,8 +18,12 @@
|
|
18
18
|
<span class="label label-danger"><%= t('Paused') %></span>
|
19
19
|
<% end %>
|
20
20
|
</td>
|
21
|
-
<td><%= number_with_delimiter(queue.size) %> </td>
|
22
|
-
<td
|
21
|
+
<td class="num"><%= number_with_delimiter(queue.size) %> </td>
|
22
|
+
<td class="num">
|
23
|
+
<% queue_latency = queue.latency %>
|
24
|
+
<%= (queue_latency < 60) ? '' : " (#{relative_time(Time.at(Time.now.to_f - queue_latency))})" %>
|
25
|
+
<%= number_with_delimiter(queue_latency, precision: 2) %>
|
26
|
+
</td>
|
23
27
|
<td class="delete-confirm">
|
24
28
|
<form action="<%=root_path %>queues/<%= CGI.escape(queue.name) %>" method="post">
|
25
29
|
<%= csrf_tag %>
|