sidekiq 7.1.6 → 7.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sidekiq might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Changes.md +35 -0
- data/lib/sidekiq/api.rb +3 -3
- data/lib/sidekiq/config.rb +4 -4
- data/lib/sidekiq/deploy.rb +1 -1
- data/lib/sidekiq/job_retry.rb +2 -1
- data/lib/sidekiq/metrics/query.rb +3 -1
- data/lib/sidekiq/paginator.rb +2 -2
- data/lib/sidekiq/redis_client_adapter.rb +16 -0
- data/lib/sidekiq/scheduled.rb +1 -1
- data/lib/sidekiq/testing.rb +7 -1
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/application.rb +17 -1
- data/lib/sidekiq/web/helpers.rb +8 -14
- 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 +3 -3
- 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 +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30f824346db9b0ebf8ee13c6ac0101494e5fd6d05b4ed2ef3ad97c5b17ccbc10
|
4
|
+
data.tar.gz: bed22f02925116256550bbc34ef9decd70bdbca356d5d61abedd4d14a7dcac45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 347e82cf6f215a1e4bd09c3f12d382be79d96bb83ccd1d09f928db19b936c2dd72c0f2e3a8988160086da7928a7911d84eddd005428c7b6a337763c4b60a3492
|
7
|
+
data.tar.gz: ef0a03f45d4d35e832f36b7a09ae0694838cd0cf3582dc20d53956bb5a61ba07811d410600b43a9bdf777e68dd2549910a125d23885afca8388b8513bb7ac8d1
|
data/Changes.md
CHANGED
@@ -2,6 +2,36 @@
|
|
2
2
|
|
3
3
|
[Sidekiq Changes](https://github.com/sidekiq/sidekiq/blob/main/Changes.md) | [Sidekiq Pro Changes](https://github.com/sidekiq/sidekiq/blob/main/Pro-Changes.md) | [Sidekiq Enterprise Changes](https://github.com/sidekiq/sidekiq/blob/main/Ent-Changes.md)
|
4
4
|
|
5
|
+
7.2.0
|
6
|
+
----------
|
7
|
+
|
8
|
+
- `sidekiq_retries_exhausted` can return `:discard` to avoid the deadset
|
9
|
+
and all death handlers [#6091]
|
10
|
+
- Metrics filtering by job class in Web UI [#5974]
|
11
|
+
- Better readability and formatting for numbers within the Web UI [#6080]
|
12
|
+
- Add explicit error if user code tries to nest test modes [#6078]
|
13
|
+
```ruby
|
14
|
+
Sidekiq::Testing.inline! # global setting
|
15
|
+
Sidekiq::Testing.fake! do # override within block
|
16
|
+
# ok
|
17
|
+
Sidekiq::Testing.inline! do # can't override the override
|
18
|
+
# not ok, nested
|
19
|
+
end
|
20
|
+
end
|
21
|
+
```
|
22
|
+
- **SECURITY** Forbid inline JavaScript execution in Web UI [#6074]
|
23
|
+
- Adjust redis-client adapter to avoid `method_missing` [#6083]
|
24
|
+
This can result in app code breaking if your app's Redis API usage was
|
25
|
+
depending on Sidekiq's adapter to correct invalid redis-client API usage.
|
26
|
+
One example:
|
27
|
+
```ruby
|
28
|
+
# bad, not redis-client native
|
29
|
+
# Unsupported command argument type: TrueClass (TypeError)
|
30
|
+
Sidekiq.redis { |c| c.set("key", "value", nx: true, ex: 15) }
|
31
|
+
# good
|
32
|
+
Sidekiq.redis { |c| c.set("key", "value", "nx", "ex", 15) }
|
33
|
+
```
|
34
|
+
|
5
35
|
7.1.6
|
6
36
|
----------
|
7
37
|
|
@@ -150,6 +180,11 @@ end
|
|
150
180
|
- Job Execution metrics!!!
|
151
181
|
- See `docs/7.0-Upgrade.md` for release notes
|
152
182
|
|
183
|
+
6.5.{10,11,12}
|
184
|
+
----------
|
185
|
+
|
186
|
+
- Fixes for Rails 7.1 [#6067, #6070]
|
187
|
+
|
153
188
|
6.5.9
|
154
189
|
----------
|
155
190
|
|
data/lib/sidekiq/api.rb
CHANGED
@@ -679,7 +679,7 @@ module Sidekiq
|
|
679
679
|
range_start = page * page_size + offset_size
|
680
680
|
range_end = range_start + page_size - 1
|
681
681
|
elements = Sidekiq.redis { |conn|
|
682
|
-
conn.zrange name, range_start, range_end, withscores
|
682
|
+
conn.zrange name, range_start, range_end, "withscores"
|
683
683
|
}
|
684
684
|
break if elements.empty?
|
685
685
|
page -= 1
|
@@ -706,7 +706,7 @@ module Sidekiq
|
|
706
706
|
end
|
707
707
|
|
708
708
|
elements = Sidekiq.redis { |conn|
|
709
|
-
conn.zrange(name, begin_score, end_score, "BYSCORE", withscores
|
709
|
+
conn.zrange(name, begin_score, end_score, "BYSCORE", "withscores")
|
710
710
|
}
|
711
711
|
|
712
712
|
elements.each_with_object([]) do |element, result|
|
@@ -881,7 +881,7 @@ module Sidekiq
|
|
881
881
|
# @api private
|
882
882
|
def cleanup
|
883
883
|
# dont run cleanup more than once per minute
|
884
|
-
return 0 unless Sidekiq.redis { |conn| conn.set("process_cleanup", "1",
|
884
|
+
return 0 unless Sidekiq.redis { |conn| conn.set("process_cleanup", "1", "NX", "EX", "60") }
|
885
885
|
|
886
886
|
count = 0
|
887
887
|
Sidekiq.redis do |conn|
|
data/lib/sidekiq/config.rb
CHANGED
@@ -258,9 +258,9 @@ module Sidekiq
|
|
258
258
|
@logger = logger
|
259
259
|
end
|
260
260
|
|
261
|
-
private def
|
262
|
-
|
263
|
-
|
261
|
+
private def parameter_size(handler)
|
262
|
+
target = handler.is_a?(Proc) ? handler : handler.method(:call)
|
263
|
+
target.parameters.size
|
264
264
|
end
|
265
265
|
|
266
266
|
# INTERNAL USE ONLY
|
@@ -269,7 +269,7 @@ module Sidekiq
|
|
269
269
|
p ["!!!!!", ex]
|
270
270
|
end
|
271
271
|
@options[:error_handlers].each do |handler|
|
272
|
-
if
|
272
|
+
if parameter_size(handler) == 2
|
273
273
|
# TODO Remove in 8.0
|
274
274
|
logger.info { "DEPRECATION: Sidekiq exception handlers now take three arguments, see #{handler}" }
|
275
275
|
handler.call(ex, {_config: self}.merge(ctx))
|
data/lib/sidekiq/deploy.rb
CHANGED
@@ -44,7 +44,7 @@ module Sidekiq
|
|
44
44
|
|
45
45
|
@pool.with do |c|
|
46
46
|
# only allow one deploy mark for a given label for the next minute
|
47
|
-
lock = c.set("deploylock-#{label}", stamp, nx
|
47
|
+
lock = c.set("deploylock-#{label}", stamp, "nx", "ex", "60")
|
48
48
|
if lock
|
49
49
|
c.multi do |pipe|
|
50
50
|
pipe.hsetnx(key, stamp, label)
|
data/lib/sidekiq/job_retry.rb
CHANGED
@@ -226,7 +226,7 @@ module Sidekiq
|
|
226
226
|
end
|
227
227
|
|
228
228
|
def retries_exhausted(jobinst, msg, exception)
|
229
|
-
begin
|
229
|
+
rv = begin
|
230
230
|
block = jobinst&.sidekiq_retries_exhausted_block
|
231
231
|
|
232
232
|
# the sidekiq_retries_exhausted_block can be defined in a wrapped class (ActiveJob for instance)
|
@@ -239,6 +239,7 @@ module Sidekiq
|
|
239
239
|
handle_exception(e, {context: "Error calling retries_exhausted", job: msg})
|
240
240
|
end
|
241
241
|
|
242
|
+
return if rv == :discard # poof!
|
242
243
|
send_to_morgue(msg) unless msg["dead"] == false
|
243
244
|
|
244
245
|
@capsule.config.death_handlers.each do |handler|
|
@@ -20,7 +20,8 @@ module Sidekiq
|
|
20
20
|
end
|
21
21
|
|
22
22
|
# Get metric data for all jobs from the last hour
|
23
|
-
|
23
|
+
# +class_filter+: return only results for classes matching filter
|
24
|
+
def top_jobs(class_filter: nil, minutes: 60)
|
24
25
|
result = Result.new
|
25
26
|
|
26
27
|
time = @time
|
@@ -39,6 +40,7 @@ module Sidekiq
|
|
39
40
|
redis_results.each do |hash|
|
40
41
|
hash.each do |k, v|
|
41
42
|
kls, metric = k.split("|")
|
43
|
+
next if class_filter && !class_filter.match?(kls)
|
42
44
|
result.job_results[kls].add_metric metric, time, v.to_i
|
43
45
|
end
|
44
46
|
time -= 60
|
data/lib/sidekiq/paginator.rb
CHANGED
@@ -19,9 +19,9 @@ module Sidekiq
|
|
19
19
|
total_size, items = conn.multi { |transaction|
|
20
20
|
transaction.zcard(key)
|
21
21
|
if rev
|
22
|
-
transaction.zrange(key, starting, ending, "REV", withscores
|
22
|
+
transaction.zrange(key, starting, ending, "REV", "withscores")
|
23
23
|
else
|
24
|
-
transaction.zrange(key, starting, ending, withscores
|
24
|
+
transaction.zrange(key, starting, ending, "withscores")
|
25
25
|
end
|
26
26
|
}
|
27
27
|
[current_page, total_size, items]
|
@@ -21,6 +21,22 @@ module Sidekiq
|
|
21
21
|
@client.call("EVALSHA", sha, keys.size, *keys, *argv)
|
22
22
|
end
|
23
23
|
|
24
|
+
# this is the set of Redis commands used by Sidekiq. Not guaranteed
|
25
|
+
# to be comprehensive, we use this as a performance enhancement to
|
26
|
+
# avoid calling method_missing on most commands
|
27
|
+
USED_COMMANDS = %w[bitfield bitfield_ro del exists expire flushdb
|
28
|
+
get hdel hget hgetall hincrby hlen hmget hset hsetnx incr incrby
|
29
|
+
lindex llen lmove lpop lpush lrange lrem mget mset ping pttl
|
30
|
+
publish rpop rpush sadd scard script set sismember smembers
|
31
|
+
srem ttl type unlink zadd zcard zincrby zrange zrem
|
32
|
+
zremrangebyrank zremrangebyscore]
|
33
|
+
|
34
|
+
USED_COMMANDS.each do |name|
|
35
|
+
define_method(name) do |*args|
|
36
|
+
@client.call(name, *args)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
24
40
|
private
|
25
41
|
|
26
42
|
# this allows us to use methods like `conn.hmset(...)` instead of having to use
|
data/lib/sidekiq/scheduled.rb
CHANGED
@@ -193,7 +193,7 @@ module Sidekiq
|
|
193
193
|
# should never depend on sidekiq/api.
|
194
194
|
def cleanup
|
195
195
|
# dont run cleanup more than once per minute
|
196
|
-
return 0 unless redis { |conn| conn.set("process_cleanup", "1",
|
196
|
+
return 0 unless redis { |conn| conn.set("process_cleanup", "1", "NX", "EX", "60") }
|
197
197
|
|
198
198
|
count = 0
|
199
199
|
redis do |conn|
|
data/lib/sidekiq/testing.rb
CHANGED
@@ -5,6 +5,7 @@ require "sidekiq"
|
|
5
5
|
|
6
6
|
module Sidekiq
|
7
7
|
class Testing
|
8
|
+
class TestModeAlreadySetError < RuntimeError; end
|
8
9
|
class << self
|
9
10
|
attr_accessor :__global_test_mode
|
10
11
|
|
@@ -12,8 +13,13 @@ module Sidekiq
|
|
12
13
|
# all threads. Calling with a block only affects the current Thread.
|
13
14
|
def __set_test_mode(mode)
|
14
15
|
if block_given?
|
16
|
+
# Reentrant testing modes will lead to a rat's nest of code which is
|
17
|
+
# hard to reason about. You can set the testing mode once globally and
|
18
|
+
# you can override that global setting once per-thread.
|
19
|
+
raise TestModeAlreadySetError, "Nesting test modes is not supported" if __local_test_mode
|
20
|
+
|
21
|
+
self.__local_test_mode = mode
|
15
22
|
begin
|
16
|
-
self.__local_test_mode = mode
|
17
23
|
yield
|
18
24
|
ensure
|
19
25
|
self.__local_test_mode = nil
|
data/lib/sidekiq/version.rb
CHANGED
@@ -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 != ""
|
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)
|
@@ -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">
|
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 %>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.
|
4
|
+
version: 7.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Perham
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis-client
|