sidekiq 7.1.0 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changes.md +89 -0
- data/README.md +2 -2
- data/lib/sidekiq/api.rb +7 -7
- data/lib/sidekiq/cli.rb +1 -0
- data/lib/sidekiq/client.rb +8 -3
- data/lib/sidekiq/config.rb +19 -6
- data/lib/sidekiq/deploy.rb +1 -1
- data/lib/sidekiq/job_retry.rb +19 -3
- data/lib/sidekiq/job_util.rb +2 -0
- data/lib/sidekiq/metrics/query.rb +3 -1
- data/lib/sidekiq/metrics/shared.rb +1 -1
- data/lib/sidekiq/middleware/current_attributes.rb +55 -16
- data/lib/sidekiq/paginator.rb +2 -2
- data/lib/sidekiq/processor.rb +27 -26
- data/lib/sidekiq/rails.rb +10 -11
- data/lib/sidekiq/redis_client_adapter.rb +17 -2
- data/lib/sidekiq/redis_connection.rb +1 -0
- data/lib/sidekiq/scheduled.rb +1 -1
- data/lib/sidekiq/testing.rb +25 -6
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/action.rb +3 -3
- data/lib/sidekiq/web/application.rb +72 -6
- data/lib/sidekiq/web/csrf_protection.rb +1 -1
- data/lib/sidekiq/web/helpers.rb +31 -23
- data/lib/sidekiq/web.rb +13 -1
- data/web/assets/javascripts/application.js +16 -0
- data/web/assets/javascripts/dashboard-charts.js +17 -1
- 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/locales/en.yml +2 -0
- data/web/locales/pt-br.yml +20 -0
- data/web/views/_job_info.erb +1 -1
- 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 +7 -0
- data/web/views/metrics.erb +36 -27
- data/web/views/metrics_for_job.erb +26 -35
- data/web/views/queues.erb +6 -2
- metadata +4 -3
|
@@ -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
|
|
@@ -63,8 +79,7 @@ module Sidekiq
|
|
|
63
79
|
opts = options.dup
|
|
64
80
|
|
|
65
81
|
if opts[:namespace]
|
|
66
|
-
raise ArgumentError, "Your Redis configuration uses the namespace '#{opts[:namespace]}' but this feature
|
|
67
|
-
"Either use the redis adapter or remove the namespace."
|
|
82
|
+
raise ArgumentError, "Your Redis configuration uses the namespace '#{opts[:namespace]}' but this feature is no longer supported in Sidekiq 7+. See https://github.com/sidekiq/sidekiq/blob/main/docs/7.0-Upgrade.md#redis-namespace."
|
|
68
83
|
end
|
|
69
84
|
|
|
70
85
|
opts.delete(:size)
|
|
@@ -14,6 +14,7 @@ module Sidekiq
|
|
|
14
14
|
logger = symbolized_options.delete(:logger)
|
|
15
15
|
logger&.info { "Sidekiq #{Sidekiq::VERSION} connecting to Redis with options #{scrub(symbolized_options)}" }
|
|
16
16
|
|
|
17
|
+
raise "Sidekiq 7+ does not support Redis protocol 2" if symbolized_options[:protocol] == 2
|
|
17
18
|
size = symbolized_options.delete(:size) || 5
|
|
18
19
|
pool_timeout = symbolized_options.delete(:pool_timeout) || 1
|
|
19
20
|
pool_name = symbolized_options.delete(:pool_name)
|
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,23 +5,42 @@ require "sidekiq"
|
|
|
5
5
|
|
|
6
6
|
module Sidekiq
|
|
7
7
|
class Testing
|
|
8
|
+
class TestModeAlreadySetError < RuntimeError; end
|
|
8
9
|
class << self
|
|
9
|
-
attr_accessor :
|
|
10
|
+
attr_accessor :__global_test_mode
|
|
10
11
|
|
|
12
|
+
# Calling without a block sets the global test mode, affecting
|
|
13
|
+
# all threads. Calling with a block only affects the current Thread.
|
|
11
14
|
def __set_test_mode(mode)
|
|
12
15
|
if block_given?
|
|
13
|
-
|
|
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
|
|
14
22
|
begin
|
|
15
|
-
self.__test_mode = mode
|
|
16
23
|
yield
|
|
17
24
|
ensure
|
|
18
|
-
self.
|
|
25
|
+
self.__local_test_mode = nil
|
|
19
26
|
end
|
|
20
27
|
else
|
|
21
|
-
self.
|
|
28
|
+
self.__global_test_mode = mode
|
|
22
29
|
end
|
|
23
30
|
end
|
|
24
31
|
|
|
32
|
+
def __test_mode
|
|
33
|
+
__local_test_mode || __global_test_mode
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def __local_test_mode
|
|
37
|
+
Thread.current[:__sidekiq_test_mode]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def __local_test_mode=(value)
|
|
41
|
+
Thread.current[:__sidekiq_test_mode] = value
|
|
42
|
+
end
|
|
43
|
+
|
|
25
44
|
def disable!(&block)
|
|
26
45
|
__set_test_mode(:disable, &block)
|
|
27
46
|
end
|
|
@@ -64,7 +83,7 @@ module Sidekiq
|
|
|
64
83
|
class EmptyQueueError < RuntimeError; end
|
|
65
84
|
|
|
66
85
|
module TestingClient
|
|
67
|
-
def
|
|
86
|
+
def atomic_push(conn, payloads)
|
|
68
87
|
if Sidekiq::Testing.fake?
|
|
69
88
|
payloads.each do |job|
|
|
70
89
|
job = Sidekiq.load_json(Sidekiq.dump_json(job))
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web/action.rb
CHANGED
|
@@ -15,11 +15,11 @@ module Sidekiq
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def halt(res)
|
|
18
|
-
throw :halt, [res, {
|
|
18
|
+
throw :halt, [res, {Rack::CONTENT_TYPE => "text/plain"}, [res.to_s]]
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def redirect(location)
|
|
22
|
-
throw :halt, [302, {
|
|
22
|
+
throw :halt, [302, {Web::LOCATION => "#{request.base_url}#{location}"}, []]
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def params
|
|
@@ -68,7 +68,7 @@ module Sidekiq
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def json(payload)
|
|
71
|
-
[200, {
|
|
71
|
+
[200, {Rack::CONTENT_TYPE => "application/json", Rack::CACHE_CONTROL => "private, no-store"}, [Sidekiq.dump_json(payload)]]
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def initialize(env, block)
|
|
@@ -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'"
|
|
@@ -328,9 +328,75 @@ module Sidekiq
|
|
|
328
328
|
json Sidekiq::Stats.new.queues
|
|
329
329
|
end
|
|
330
330
|
|
|
331
|
+
########
|
|
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
|
+
|
|
349
|
+
get "/filter/retries" do
|
|
350
|
+
x = params[:substr]
|
|
351
|
+
return redirect "#{root_path}retries" unless x && x != ""
|
|
352
|
+
|
|
353
|
+
@retries = search(Sidekiq::RetrySet.new, params[:substr])
|
|
354
|
+
erb :retries
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
post "/filter/retries" do
|
|
358
|
+
x = params[:substr]
|
|
359
|
+
return redirect "#{root_path}retries" unless x && x != ""
|
|
360
|
+
|
|
361
|
+
@retries = search(Sidekiq::RetrySet.new, params[:substr])
|
|
362
|
+
erb :retries
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
get "/filter/scheduled" do
|
|
366
|
+
x = params[:substr]
|
|
367
|
+
return redirect "#{root_path}scheduled" unless x && x != ""
|
|
368
|
+
|
|
369
|
+
@scheduled = search(Sidekiq::ScheduledSet.new, params[:substr])
|
|
370
|
+
erb :scheduled
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
post "/filter/scheduled" do
|
|
374
|
+
x = params[:substr]
|
|
375
|
+
return redirect "#{root_path}scheduled" unless x && x != ""
|
|
376
|
+
|
|
377
|
+
@scheduled = search(Sidekiq::ScheduledSet.new, params[:substr])
|
|
378
|
+
erb :scheduled
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
get "/filter/dead" do
|
|
382
|
+
x = params[:substr]
|
|
383
|
+
return redirect "#{root_path}morgue" unless x && x != ""
|
|
384
|
+
|
|
385
|
+
@dead = search(Sidekiq::DeadSet.new, params[:substr])
|
|
386
|
+
erb :morgue
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
post "/filter/dead" do
|
|
390
|
+
x = params[:substr]
|
|
391
|
+
return redirect "#{root_path}morgue" unless x && x != ""
|
|
392
|
+
|
|
393
|
+
@dead = search(Sidekiq::DeadSet.new, params[:substr])
|
|
394
|
+
erb :morgue
|
|
395
|
+
end
|
|
396
|
+
|
|
331
397
|
def call(env)
|
|
332
398
|
action = self.class.match(env)
|
|
333
|
-
return [404, {
|
|
399
|
+
return [404, {Rack::CONTENT_TYPE => "text/plain", Web::X_CASCADE => "pass"}, ["Not Found"]] unless action
|
|
334
400
|
|
|
335
401
|
app = @klass
|
|
336
402
|
resp = catch(:halt) do
|
|
@@ -347,10 +413,10 @@ module Sidekiq
|
|
|
347
413
|
else
|
|
348
414
|
# rendered content goes here
|
|
349
415
|
headers = {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
416
|
+
Rack::CONTENT_TYPE => "text/html",
|
|
417
|
+
Rack::CACHE_CONTROL => "private, no-store",
|
|
418
|
+
Web::CONTENT_LANGUAGE => action.locale,
|
|
419
|
+
Web::CONTENT_SECURITY_POLICY => CSP_HEADER
|
|
354
420
|
}
|
|
355
421
|
# we'll let Rack calculate Content-Length for us.
|
|
356
422
|
[200, headers, [resp]]
|
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
|
|
@@ -49,8 +53,29 @@ module Sidekiq
|
|
|
49
53
|
locale_files.select { |file| file =~ /\/#{lang}\.yml$/ }
|
|
50
54
|
end
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
def search(jobset, substr)
|
|
57
|
+
resultset = jobset.scan(substr).to_a
|
|
58
|
+
@current_page = 1
|
|
59
|
+
@count = @total_size = resultset.size
|
|
60
|
+
resultset
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def filtering(which)
|
|
64
|
+
erb(:filtering, locals: {which: which})
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def filter_link(jid, within = "retries")
|
|
68
|
+
if within.nil?
|
|
69
|
+
::Rack::Utils.escape_html(jid)
|
|
70
|
+
else
|
|
71
|
+
"<a href='#{root_path}filter/#{within}?substr=#{jid}'>#{::Rack::Utils.escape_html(jid)}</a>"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def display_tags(job, within = "retries")
|
|
76
|
+
job.tags.map { |tag|
|
|
77
|
+
"<span class='label label-info jobtag'>#{filter_link(tag, within)}</span>"
|
|
78
|
+
}.join(" ")
|
|
54
79
|
end
|
|
55
80
|
|
|
56
81
|
# This view helper provide ability display you html code in
|
|
@@ -111,13 +136,6 @@ module Sidekiq
|
|
|
111
136
|
end
|
|
112
137
|
end
|
|
113
138
|
|
|
114
|
-
# within is used by Sidekiq Pro
|
|
115
|
-
def display_tags(job, within = nil)
|
|
116
|
-
job.tags.map { |tag|
|
|
117
|
-
"<span class='label label-info jobtag'>#{::Rack::Utils.escape_html(tag)}</span>"
|
|
118
|
-
}.join(" ")
|
|
119
|
-
end
|
|
120
|
-
|
|
121
139
|
# sidekiq/sidekiq#3243
|
|
122
140
|
def unfiltered?
|
|
123
141
|
yield unless env["PATH_INFO"].start_with?("/filter/")
|
|
@@ -278,23 +296,13 @@ module Sidekiq
|
|
|
278
296
|
elsif rss_kb < 10_000_000
|
|
279
297
|
"#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
|
|
280
298
|
else
|
|
281
|
-
"#{number_with_delimiter((rss_kb / (1024.0 * 1024.0))
|
|
299
|
+
"#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)), precision: 1)} GB"
|
|
282
300
|
end
|
|
283
301
|
end
|
|
284
302
|
|
|
285
|
-
def number_with_delimiter(number)
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
begin
|
|
289
|
-
Float(number)
|
|
290
|
-
rescue ArgumentError, TypeError
|
|
291
|
-
return number
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
options = {delimiter: ",", separator: "."}
|
|
295
|
-
parts = number.to_s.to_str.split(".")
|
|
296
|
-
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
|
|
297
|
-
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>)
|
|
298
306
|
end
|
|
299
307
|
|
|
300
308
|
def h(text)
|
data/lib/sidekiq/web.rb
CHANGED
|
@@ -34,6 +34,18 @@ module Sidekiq
|
|
|
34
34
|
"Metrics" => "metrics"
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
|
|
38
|
+
CONTENT_LANGUAGE = "Content-Language"
|
|
39
|
+
CONTENT_SECURITY_POLICY = "Content-Security-Policy"
|
|
40
|
+
LOCATION = "Location"
|
|
41
|
+
X_CASCADE = "X-Cascade"
|
|
42
|
+
else
|
|
43
|
+
CONTENT_LANGUAGE = "content-language"
|
|
44
|
+
CONTENT_SECURITY_POLICY = "content-security-policy"
|
|
45
|
+
LOCATION = "location"
|
|
46
|
+
X_CASCADE = "x-cascade"
|
|
47
|
+
end
|
|
48
|
+
|
|
37
49
|
class << self
|
|
38
50
|
def settings
|
|
39
51
|
self
|
|
@@ -137,7 +149,7 @@ module Sidekiq
|
|
|
137
149
|
m = middlewares
|
|
138
150
|
|
|
139
151
|
rules = []
|
|
140
|
-
rules = [[:all, {
|
|
152
|
+
rules = [[:all, {Rack::CACHE_CONTROL => "private, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
|
|
141
153
|
|
|
142
154
|
::Rack::Builder.new do
|
|
143
155
|
use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
|
|
@@ -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
|
|
|
@@ -140,6 +155,7 @@ function checkResponse(resp) {
|
|
|
140
155
|
|
|
141
156
|
function scheduleLivePoll() {
|
|
142
157
|
let ti = parseInt(localStorage.sidekiqTimeInterval) || 5000;
|
|
158
|
+
if (ti < 2000) { ti = 2000 }
|
|
143
159
|
livePollTimer = setTimeout(livePollCallback, ti);
|
|
144
160
|
}
|
|
145
161
|
|
|
@@ -57,7 +57,9 @@ class DashboardChart extends BaseChart {
|
|
|
57
57
|
class RealtimeChart extends DashboardChart {
|
|
58
58
|
constructor(el, options) {
|
|
59
59
|
super(el, options);
|
|
60
|
-
|
|
60
|
+
let d = parseInt(localStorage.sidekiqTimeInterval) || 5000;
|
|
61
|
+
if (d < 2000) { d = 2000; }
|
|
62
|
+
this.delay = d
|
|
61
63
|
this.startPolling();
|
|
62
64
|
document.addEventListener("interval:update", this.handleUpdate.bind(this));
|
|
63
65
|
}
|
|
@@ -84,6 +86,7 @@ class RealtimeChart extends DashboardChart {
|
|
|
84
86
|
updateStatsSummary(this.stats.sidekiq);
|
|
85
87
|
updateRedisStats(this.stats.redis);
|
|
86
88
|
updateFooterUTCTime(this.stats.server_utc_time);
|
|
89
|
+
updateNumbers();
|
|
87
90
|
pulseBeacon();
|
|
88
91
|
|
|
89
92
|
this.stats = stats;
|
|
@@ -164,3 +167,16 @@ class RealtimeChart extends DashboardChart {
|
|
|
164
167
|
};
|
|
165
168
|
}
|
|
166
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/locales/en.yml
CHANGED
data/web/locales/pt-br.yml
CHANGED
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
AreYouSureDeleteJob: Deseja deletar esta tarefa?
|
|
6
6
|
AreYouSureDeleteQueue: Deseja deletar a fila %{queue}? Isso irá deletar todas as tarefas desta fila.
|
|
7
7
|
Arguments: Argumentos
|
|
8
|
+
AvgExecutionTime: Tempo médio de execução
|
|
8
9
|
BackToApp: De volta ao aplicativo
|
|
10
|
+
Bucket: Bucket
|
|
9
11
|
Busy: Ocupados
|
|
10
12
|
Class: Classe
|
|
11
13
|
Connections: Conexões
|
|
14
|
+
Context: Contexto
|
|
12
15
|
CreatedAt: Criado em
|
|
13
16
|
CurrentMessagesInQueue: Mensagens atualmente na <span class='title'>%{queue}</span>
|
|
14
17
|
Dashboard: Painel
|
|
@@ -16,13 +19,16 @@
|
|
|
16
19
|
DeadJobs: Tarefas mortas
|
|
17
20
|
Delete: Apagar
|
|
18
21
|
DeleteAll: Apagar tudo
|
|
22
|
+
Deploy: Deploy
|
|
19
23
|
Enqueued: Na fila
|
|
20
24
|
Error: Erro
|
|
21
25
|
ErrorBacktrace: Rastreamento do erro
|
|
22
26
|
ErrorClass: Classe de erro
|
|
23
27
|
ErrorMessage: Mensagem de erro
|
|
28
|
+
ExecutionTime: Tempo de execução
|
|
24
29
|
Extras: Extras
|
|
25
30
|
Failed: Falhas
|
|
31
|
+
Failure: Falha
|
|
26
32
|
Failures: Falhas
|
|
27
33
|
GoBack: ← Voltar
|
|
28
34
|
History: Histórico
|
|
@@ -34,10 +40,13 @@
|
|
|
34
40
|
Latency: Latência
|
|
35
41
|
LivePoll: Live Poll
|
|
36
42
|
MemoryUsage: Uso de memória
|
|
43
|
+
Metrics: Métricas
|
|
37
44
|
Name: Nome
|
|
38
45
|
Namespace: Namespace
|
|
39
46
|
NextRetry: Próxima Tentativa
|
|
47
|
+
NoDataFound: Nenhum dado encontrado
|
|
40
48
|
NoDeadJobsFound: Nenhuma tarefa morta foi encontrada
|
|
49
|
+
NoJobMetricsFound: Nenhuma métrica de tarefa encontrada
|
|
41
50
|
NoRetriesFound: Nenhuma tentativa encontrada
|
|
42
51
|
NoScheduledFound: Nenhuma tarefa agendada foi encontrada
|
|
43
52
|
NotYetEnqueued: Ainda não enfileirado
|
|
@@ -47,6 +56,7 @@
|
|
|
47
56
|
Pause: Pausar
|
|
48
57
|
Paused: Pausado
|
|
49
58
|
PeakMemoryUsage: Pico de uso de memória
|
|
59
|
+
Uptime: Tempo de atividade
|
|
50
60
|
Plugins: Plug-ins
|
|
51
61
|
PollingInterval: Intervalo de Polling
|
|
52
62
|
Process: Processo
|
|
@@ -63,14 +73,24 @@
|
|
|
63
73
|
RetryNow: Tentar novamente agora
|
|
64
74
|
Scheduled: Agendados
|
|
65
75
|
ScheduledJobs: Tarefas agendadas
|
|
76
|
+
idle: Ocioso
|
|
77
|
+
active: Ativo
|
|
78
|
+
Seconds: Segundos
|
|
66
79
|
ShowAll: Mostrar todos
|
|
67
80
|
SixMonths: 6 meses
|
|
68
81
|
Size: Tamanho
|
|
82
|
+
Started: Iniciado
|
|
69
83
|
Stop: Parar
|
|
70
84
|
StopAll: Parar Todos
|
|
71
85
|
StopPolling: Parar Polling
|
|
86
|
+
Success: Sucesso
|
|
87
|
+
Summary: Resumo
|
|
72
88
|
Thread: Thread
|
|
73
89
|
Threads: Threads
|
|
90
|
+
ThreeMonths: 3 meses
|
|
91
|
+
TotalExecutionTime: Tempo total de execução
|
|
74
92
|
Unpause: Despausar
|
|
75
93
|
Utilization: Utilização
|
|
94
|
+
Version: Versão
|
|
95
|
+
When: Quando
|
|
76
96
|
Worker: Trabalhador
|
data/web/views/_job_info.erb
CHANGED
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>
|