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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +89 -0
  3. data/README.md +2 -2
  4. data/lib/sidekiq/api.rb +7 -7
  5. data/lib/sidekiq/cli.rb +1 -0
  6. data/lib/sidekiq/client.rb +8 -3
  7. data/lib/sidekiq/config.rb +19 -6
  8. data/lib/sidekiq/deploy.rb +1 -1
  9. data/lib/sidekiq/job_retry.rb +19 -3
  10. data/lib/sidekiq/job_util.rb +2 -0
  11. data/lib/sidekiq/metrics/query.rb +3 -1
  12. data/lib/sidekiq/metrics/shared.rb +1 -1
  13. data/lib/sidekiq/middleware/current_attributes.rb +55 -16
  14. data/lib/sidekiq/paginator.rb +2 -2
  15. data/lib/sidekiq/processor.rb +27 -26
  16. data/lib/sidekiq/rails.rb +10 -11
  17. data/lib/sidekiq/redis_client_adapter.rb +17 -2
  18. data/lib/sidekiq/redis_connection.rb +1 -0
  19. data/lib/sidekiq/scheduled.rb +1 -1
  20. data/lib/sidekiq/testing.rb +25 -6
  21. data/lib/sidekiq/version.rb +1 -1
  22. data/lib/sidekiq/web/action.rb +3 -3
  23. data/lib/sidekiq/web/application.rb +72 -6
  24. data/lib/sidekiq/web/csrf_protection.rb +1 -1
  25. data/lib/sidekiq/web/helpers.rb +31 -23
  26. data/lib/sidekiq/web.rb +13 -1
  27. data/web/assets/javascripts/application.js +16 -0
  28. data/web/assets/javascripts/dashboard-charts.js +17 -1
  29. data/web/assets/javascripts/dashboard.js +7 -9
  30. data/web/assets/javascripts/metrics.js +34 -0
  31. data/web/assets/stylesheets/application.css +9 -0
  32. data/web/locales/en.yml +2 -0
  33. data/web/locales/pt-br.yml +20 -0
  34. data/web/views/_job_info.erb +1 -1
  35. data/web/views/_metrics_period_select.erb +1 -1
  36. data/web/views/_summary.erb +7 -7
  37. data/web/views/busy.erb +3 -3
  38. data/web/views/dashboard.erb +23 -33
  39. data/web/views/filtering.erb +7 -0
  40. data/web/views/metrics.erb +36 -27
  41. data/web/views/metrics_for_job.erb +26 -35
  42. data/web/views/queues.erb +6 -2
  43. 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 isn't supported by redis-client. " \
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)
@@ -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", nx: true, ex: 60) }
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|
@@ -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 :__test_mode
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
- current_mode = __test_mode
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.__test_mode = current_mode
25
+ self.__local_test_mode = nil
19
26
  end
20
27
  else
21
- self.__test_mode = mode
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 raw_push(payloads)
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))
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "7.1.0"
4
+ VERSION = "7.2.0"
5
5
  MAJOR = 7
6
6
  end
@@ -15,11 +15,11 @@ module Sidekiq
15
15
  end
16
16
 
17
17
  def halt(res)
18
- throw :halt, [res, {"content-type" => "text/plain"}, [res.to_s]]
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, {"location" => "#{request.base_url}#{location}"}, []]
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, {"content-type" => "application/json", "cache-control" => "private, no-store"}, [Sidekiq.dump_json(payload)]]
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: 'unsafe-inline'",
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, {"content-type" => "text/plain", "x-cascade" => "pass"}, ["Not Found"]] unless action
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
- "content-type" => "text/html",
351
- "cache-control" => "private, no-store",
352
- "content-language" => action.locale,
353
- "content-security-policy" => CSP_HEADER
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]]
@@ -62,7 +62,7 @@ module Sidekiq
62
62
 
63
63
  def deny(env)
64
64
  logger(env).warn "attack prevented by #{self.class}"
65
- [403, {"Content-Type" => "text/plain"}, ["Forbidden"]]
65
+ [403, {Rack::CONTENT_TYPE => "text/plain"}, ["Forbidden"]]
66
66
  end
67
67
 
68
68
  def session(env)
@@ -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
- # This is a hook for a Sidekiq Pro feature. Please don't touch.
53
- def filtering(*)
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)).round(1))} GB"
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
- return "" if number.nil?
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, {"cache-control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
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
- this.delay = parseInt(localStorage.sidekiqTimeInterval) || 5000;
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 = nf.format(data.processed);
7
- document.getElementById("txtFailed").innerText = nf.format(data.failed);
8
- document.getElementById("txtBusy").innerText = nf.format(data.busy);
9
- document.getElementById("txtScheduled").innerText = nf.format(data.scheduled);
10
- document.getElementById("txtRetries").innerText = nf.format(data.retries);
11
- document.getElementById("txtEnqueued").innerText = nf.format(data.enqueued);
12
- document.getElementById("txtDead").innerText = nf.format(data.dead);
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
+ }
@@ -370,6 +370,15 @@ img.smallogo {
370
370
  .stat p {
371
371
  font-size: 0.9em;
372
372
  }
373
+
374
+ .num {
375
+ font-family: monospace;
376
+ }
377
+
378
+ td.num {
379
+ text-align: right;
380
+ }
381
+
373
382
  @media (max-width: 767px) {
374
383
  .stats-container {
375
384
  display: block;
data/web/locales/en.yml CHANGED
@@ -97,3 +97,5 @@ en:
97
97
  Context: Context
98
98
  Bucket: Bucket
99
99
  NoJobMetricsFound: No recent job metrics were found
100
+ Filter: Filter
101
+ AnyJobContent: Any job content
@@ -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
@@ -37,7 +37,7 @@
37
37
  <tr>
38
38
  <th>BID</th>
39
39
  <td>
40
- <a href="<%= root_path %>batches/<%= job.bid %>"><%= job.bid %>
40
+ <a href="<%= root_path %>batches/<%= job.bid %>"><%= job.bid %></a>
41
41
  </td>
42
42
  </tr>
43
43
  <% end %>
@@ -1,5 +1,5 @@
1
1
  <div>
2
- <select class="form-control" onchange="window.location.href = '<%= path %>?period=' + event.target.value">
2
+ <select class="form-control" data-metric-period="<%= path %>">
3
3
  <% periods.each_key do |code| %>
4
4
 
5
5
  <% if code == period %>
@@ -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"><%= number_with_delimiter(stats.processed) %></span>
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"><%= number_with_delimiter(stats.failed) %></span>
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"><%= number_with_delimiter(workset.size) %></span>
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"><%= number_with_delimiter(stats.enqueued) %></span>
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"><%= number_with_delimiter(stats.retry_size) %></span>
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"><%= number_with_delimiter(stats.scheduled_size) %></span>
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"><%= number_with_delimiter(stats.dead_size) %></span>
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>