sidekiq 7.0.9 → 7.1.6

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.
@@ -7,26 +7,32 @@ module Sidekiq
7
7
  # This can be useful for multi-tenancy, i18n locale, timezone, any implicit
8
8
  # per-request attribute. See +ActiveSupport::CurrentAttributes+.
9
9
  #
10
+ # For multiple current attributes, pass an array of current attributes.
11
+ #
10
12
  # @example
11
13
  #
12
14
  # # in your initializer
13
15
  # require "sidekiq/middleware/current_attributes"
14
16
  # Sidekiq::CurrentAttributes.persist("Myapp::Current")
17
+ # # or multiple current attributes
18
+ # Sidekiq::CurrentAttributes.persist(["Myapp::Current", "Myapp::OtherCurrent"])
15
19
  #
16
20
  module CurrentAttributes
17
21
  class Save
18
22
  include Sidekiq::ClientMiddleware
19
23
 
20
- def initialize(cattr)
21
- @strklass = cattr
24
+ def initialize(cattrs)
25
+ @cattrs = cattrs
22
26
  end
23
27
 
24
28
  def call(_, job, _, _)
25
- if !job.has_key?("cattr")
26
- attrs = @strklass.constantize.attributes
27
- # Retries can push the job N times, we don't
28
- # want retries to reset cattr. #5692, #5090
29
- job["cattr"] = attrs if attrs.any?
29
+ @cattrs.each do |(key, strklass)|
30
+ if !job.has_key?(key)
31
+ attrs = strklass.constantize.attributes
32
+ # Retries can push the job N times, we don't
33
+ # want retries to reset cattr. #5692, #5090
34
+ job[key] = attrs if attrs.any?
35
+ end
30
36
  end
31
37
  yield
32
38
  end
@@ -35,22 +41,55 @@ module Sidekiq
35
41
  class Load
36
42
  include Sidekiq::ServerMiddleware
37
43
 
38
- def initialize(cattr)
39
- @strklass = cattr
44
+ def initialize(cattrs)
45
+ @cattrs = cattrs
40
46
  end
41
47
 
42
48
  def call(_, job, _, &block)
43
- if job.has_key?("cattr")
44
- @strklass.constantize.set(job["cattr"], &block)
45
- else
46
- yield
49
+ cattrs_to_reset = []
50
+
51
+ @cattrs.each do |(key, strklass)|
52
+ if job.has_key?(key)
53
+ constklass = strklass.constantize
54
+ cattrs_to_reset << constklass
55
+
56
+ job[key].each do |(attribute, value)|
57
+ constklass.public_send("#{attribute}=", value)
58
+ end
59
+ end
47
60
  end
61
+
62
+ yield
63
+ ensure
64
+ cattrs_to_reset.each(&:reset)
48
65
  end
49
66
  end
50
67
 
51
- def self.persist(klass, config = Sidekiq.default_configuration)
52
- config.client_middleware.add Save, klass.to_s
53
- config.server_middleware.add Load, klass.to_s
68
+ class << self
69
+ def persist(klass_or_array, config = Sidekiq.default_configuration)
70
+ cattrs = build_cattrs_hash(klass_or_array)
71
+
72
+ config.client_middleware.add Save, cattrs
73
+ config.server_middleware.add Load, cattrs
74
+ end
75
+
76
+ private
77
+
78
+ def build_cattrs_hash(klass_or_array)
79
+ if klass_or_array.is_a?(Array)
80
+ {}.tap do |hash|
81
+ klass_or_array.each_with_index do |klass, index|
82
+ hash[key_at(index)] = klass.to_s
83
+ end
84
+ end
85
+ else
86
+ {key_at(0) => klass_or_array.to_s}
87
+ end
88
+ end
89
+
90
+ def key_at(index)
91
+ (index == 0) ? "cattr" : "cattr_#{index}"
92
+ end
54
93
  end
55
94
  end
56
95
  end
@@ -19,7 +19,7 @@ module Sidekiq
19
19
  total_size, items = conn.multi { |transaction|
20
20
  transaction.zcard(key)
21
21
  if rev
22
- transaction.zrevrange(key, starting, ending, withscores: true)
22
+ transaction.zrange(key, starting, ending, "REV", withscores: true)
23
23
  else
24
24
  transaction.zrange(key, starting, ending, withscores: true)
25
25
  end
@@ -148,6 +148,8 @@ module Sidekiq
148
148
 
149
149
  IGNORE_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :never}
150
150
  private_constant :IGNORE_SHUTDOWN_INTERRUPTS
151
+ ALLOW_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :immediate}
152
+ private_constant :ALLOW_SHUTDOWN_INTERRUPTS
151
153
 
152
154
  def process(uow)
153
155
  jobstr = uow.job
@@ -171,36 +173,35 @@ module Sidekiq
171
173
  end
172
174
 
173
175
  ack = false
174
- begin
175
- dispatch(job_hash, queue, jobstr) do |inst|
176
- config.server_middleware.invoke(inst, job_hash, queue) do
177
- execute_job(inst, job_hash["args"])
176
+ Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
177
+ Thread.handle_interrupt(ALLOW_SHUTDOWN_INTERRUPTS) do
178
+ dispatch(job_hash, queue, jobstr) do |inst|
179
+ config.server_middleware.invoke(inst, job_hash, queue) do
180
+ execute_job(inst, job_hash["args"])
181
+ end
178
182
  end
183
+ ack = true
184
+ rescue Sidekiq::Shutdown
185
+ # Had to force kill this job because it didn't finish
186
+ # within the timeout. Don't acknowledge the work since
187
+ # we didn't properly finish it.
188
+ rescue Sidekiq::JobRetry::Handled => h
189
+ # this is the common case: job raised error and Sidekiq::JobRetry::Handled
190
+ # signals that we created a retry successfully. We can acknowlege the job.
191
+ ack = true
192
+ e = h.cause || h
193
+ handle_exception(e, {context: "Job raised exception", job: job_hash})
194
+ raise e
195
+ rescue Exception => ex
196
+ # Unexpected error! This is very bad and indicates an exception that got past
197
+ # the retry subsystem (e.g. network partition). We won't acknowledge the job
198
+ # so it can be rescued when using Sidekiq Pro.
199
+ handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
200
+ raise ex
179
201
  end
180
- ack = true
181
- rescue Sidekiq::Shutdown
182
- # Had to force kill this job because it didn't finish
183
- # within the timeout. Don't acknowledge the work since
184
- # we didn't properly finish it.
185
- rescue Sidekiq::JobRetry::Handled => h
186
- # this is the common case: job raised error and Sidekiq::JobRetry::Handled
187
- # signals that we created a retry successfully. We can acknowlege the job.
188
- ack = true
189
- e = h.cause || h
190
- handle_exception(e, {context: "Job raised exception", job: job_hash})
191
- raise e
192
- rescue Exception => ex
193
- # Unexpected error! This is very bad and indicates an exception that got past
194
- # the retry subsystem (e.g. network partition). We won't acknowledge the job
195
- # so it can be rescued when using Sidekiq Pro.
196
- handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
197
- raise ex
198
202
  ensure
199
203
  if ack
200
- # We don't want a shutdown signal to interrupt job acknowledgment.
201
- Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
202
- uow.acknowledge
203
- end
204
+ uow.acknowledge
204
205
  end
205
206
  end
206
207
  end
data/lib/sidekiq/rails.rb CHANGED
@@ -39,14 +39,9 @@ module Sidekiq
39
39
  end
40
40
  end
41
41
 
42
- initializer "sidekiq.rails_logger" do
42
+ initializer "sidekiq.backtrace_cleaner" do
43
43
  Sidekiq.configure_server do |config|
44
- # This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
45
- # it will appear in the Sidekiq console with all of the job context. See #5021 and
46
- # https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
47
- unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
48
- ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
49
- end
44
+ config[:backtrace_cleaner] = ->(backtrace) { ::Rails.backtrace_cleaner.clean(backtrace) }
50
45
  end
51
46
  end
52
47
 
@@ -57,6 +52,16 @@ module Sidekiq
57
52
  config.after_initialize do
58
53
  Sidekiq.configure_server do |config|
59
54
  config[:reloader] = Sidekiq::Rails::Reloader.new
55
+
56
+ # This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
57
+ # it will appear in the Sidekiq console with all of the job context.
58
+ unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
59
+ if ::Rails::VERSION::STRING < "7.1"
60
+ ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
61
+ else
62
+ ::Rails.logger.broadcast_to(config.logger)
63
+ end
64
+ end
60
65
  end
61
66
  end
62
67
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "set"
3
4
  require "redis_client"
4
5
  require "redis_client/decorator"
5
6
 
@@ -8,13 +9,14 @@ module Sidekiq
8
9
  BaseError = RedisClient::Error
9
10
  CommandError = RedisClient::CommandError
10
11
 
12
+ # You can add/remove items or clear the whole thing if you don't want deprecation warnings.
13
+ DEPRECATED_COMMANDS = %i[rpoplpush zrangebyscore zrevrange zrevrangebyscore getset hmset setex setnx].to_set
14
+
11
15
  module CompatMethods
12
- # TODO Deprecate and remove this
13
16
  def info
14
17
  @client.call("INFO") { |i| i.lines(chomp: true).map { |l| l.split(":", 2) }.select { |l| l.size == 2 }.to_h }
15
18
  end
16
19
 
17
- # TODO Deprecate and remove this
18
20
  def evalsha(sha, keys, argv)
19
21
  @client.call("EVALSHA", sha, keys.size, *keys, *argv)
20
22
  end
@@ -24,6 +26,7 @@ module Sidekiq
24
26
  # this allows us to use methods like `conn.hmset(...)` instead of having to use
25
27
  # redis-client's native `conn.call("hmset", ...)`
26
28
  def method_missing(*args, &block)
29
+ warn("[sidekiq#5788] Redis has deprecated the `#{args.first}`command, called at #{caller(1..1)}") if DEPRECATED_COMMANDS.include?(args.first)
27
30
  @client.call(*args, *block)
28
31
  end
29
32
  ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
@@ -39,28 +42,6 @@ module Sidekiq
39
42
  def config
40
43
  @client.config
41
44
  end
42
-
43
- def message
44
- yield nil, @queue.pop
45
- end
46
-
47
- # NB: this method does not return
48
- def subscribe(chan)
49
- @queue = ::Queue.new
50
-
51
- pubsub = @client.pubsub
52
- pubsub.call("subscribe", chan)
53
-
54
- loop do
55
- evt = pubsub.next_event
56
- next if evt.nil?
57
- next unless evt[0] == "message" && evt[1] == chan
58
-
59
- (_, _, msg) = evt
60
- @queue << msg
61
- yield self
62
- end
63
- end
64
45
  end
65
46
 
66
47
  def initialize(options)
@@ -82,8 +63,7 @@ module Sidekiq
82
63
  opts = options.dup
83
64
 
84
65
  if opts[:namespace]
85
- raise ArgumentError, "Your Redis configuration uses the namespace '#{opts[:namespace]}' but this feature isn't supported by redis-client. " \
86
- "Either use the redis adapter or remove the namespace."
66
+ 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."
87
67
  end
88
68
 
89
69
  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)
@@ -12,7 +12,7 @@ module Sidekiq
12
12
 
13
13
  LUA_ZPOPBYSCORE = <<~LUA
14
14
  local key, now = KEYS[1], ARGV[1]
15
- local jobs = redis.call("zrangebyscore", key, "-inf", now, "limit", 0, 1)
15
+ local jobs = redis.call("zrange", key, "-inf", now, "byscore", "limit", 0, 1)
16
16
  if jobs[1] then
17
17
  redis.call("zrem", key, jobs[1])
18
18
  return jobs[1]
@@ -6,22 +6,35 @@ require "sidekiq"
6
6
  module Sidekiq
7
7
  class Testing
8
8
  class << self
9
- attr_accessor :__test_mode
9
+ attr_accessor :__global_test_mode
10
10
 
11
+ # Calling without a block sets the global test mode, affecting
12
+ # all threads. Calling with a block only affects the current Thread.
11
13
  def __set_test_mode(mode)
12
14
  if block_given?
13
- current_mode = __test_mode
14
15
  begin
15
- self.__test_mode = mode
16
+ self.__local_test_mode = mode
16
17
  yield
17
18
  ensure
18
- self.__test_mode = current_mode
19
+ self.__local_test_mode = nil
19
20
  end
20
21
  else
21
- self.__test_mode = mode
22
+ self.__global_test_mode = mode
22
23
  end
23
24
  end
24
25
 
26
+ def __test_mode
27
+ __local_test_mode || __global_test_mode
28
+ end
29
+
30
+ def __local_test_mode
31
+ Thread.current[:__sidekiq_test_mode]
32
+ end
33
+
34
+ def __local_test_mode=(value)
35
+ Thread.current[:__sidekiq_test_mode] = value
36
+ end
37
+
25
38
  def disable!(&block)
26
39
  __set_test_mode(:disable, &block)
27
40
  end
@@ -64,7 +77,7 @@ module Sidekiq
64
77
  class EmptyQueueError < RuntimeError; end
65
78
 
66
79
  module TestingClient
67
- def raw_push(payloads)
80
+ def atomic_push(conn, payloads)
68
81
  if Sidekiq::Testing.fake?
69
82
  payloads.each do |job|
70
83
  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.0.9"
4
+ VERSION = "7.1.6"
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)
@@ -328,9 +328,59 @@ module Sidekiq
328
328
  json Sidekiq::Stats.new.queues
329
329
  end
330
330
 
331
+ ########
332
+ # Filtering
333
+ get "/filter/retries" do
334
+ x = params[:substr]
335
+ return redirect "#{root_path}retries" unless x && x != ""
336
+
337
+ @retries = search(Sidekiq::RetrySet.new, params[:substr])
338
+ erb :retries
339
+ end
340
+
341
+ post "/filter/retries" do
342
+ x = params[:substr]
343
+ return redirect "#{root_path}retries" unless x && x != ""
344
+
345
+ @retries = search(Sidekiq::RetrySet.new, params[:substr])
346
+ erb :retries
347
+ end
348
+
349
+ get "/filter/scheduled" do
350
+ x = params[:substr]
351
+ return redirect "#{root_path}scheduled" unless x && x != ""
352
+
353
+ @scheduled = search(Sidekiq::ScheduledSet.new, params[:substr])
354
+ erb :scheduled
355
+ end
356
+
357
+ post "/filter/scheduled" do
358
+ x = params[:substr]
359
+ return redirect "#{root_path}scheduled" unless x && x != ""
360
+
361
+ @scheduled = search(Sidekiq::ScheduledSet.new, params[:substr])
362
+ erb :scheduled
363
+ end
364
+
365
+ get "/filter/dead" do
366
+ x = params[:substr]
367
+ return redirect "#{root_path}morgue" unless x && x != ""
368
+
369
+ @dead = search(Sidekiq::DeadSet.new, params[:substr])
370
+ erb :morgue
371
+ end
372
+
373
+ post "/filter/dead" do
374
+ x = params[:substr]
375
+ return redirect "#{root_path}morgue" unless x && x != ""
376
+
377
+ @dead = search(Sidekiq::DeadSet.new, params[:substr])
378
+ erb :morgue
379
+ end
380
+
331
381
  def call(env)
332
382
  action = self.class.match(env)
333
- return [404, {"content-type" => "text/plain", "x-cascade" => "pass"}, ["Not Found"]] unless action
383
+ return [404, {Rack::CONTENT_TYPE => "text/plain", Web::X_CASCADE => "pass"}, ["Not Found"]] unless action
334
384
 
335
385
  app = @klass
336
386
  resp = catch(:halt) do
@@ -347,10 +397,10 @@ module Sidekiq
347
397
  else
348
398
  # rendered content goes here
349
399
  headers = {
350
- "content-type" => "text/html",
351
- "cache-control" => "private, no-store",
352
- "content-language" => action.locale,
353
- "content-security-policy" => CSP_HEADER
400
+ Rack::CONTENT_TYPE => "text/html",
401
+ Rack::CACHE_CONTROL => "private, no-store",
402
+ Web::CONTENT_LANGUAGE => action.locale,
403
+ Web::CONTENT_SECURITY_POLICY => CSP_HEADER
354
404
  }
355
405
  # we'll let Rack calculate Content-Length for us.
356
406
  [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)
@@ -49,8 +49,29 @@ module Sidekiq
49
49
  locale_files.select { |file| file =~ /\/#{lang}\.yml$/ }
50
50
  end
51
51
 
52
- # This is a hook for a Sidekiq Pro feature. Please don't touch.
53
- def filtering(*)
52
+ def search(jobset, substr)
53
+ resultset = jobset.scan(substr).to_a
54
+ @current_page = 1
55
+ @count = @total_size = resultset.size
56
+ resultset
57
+ end
58
+
59
+ def filtering(which)
60
+ erb(:filtering, locals: {which: which})
61
+ end
62
+
63
+ def filter_link(jid, within = "retries")
64
+ if within.nil?
65
+ ::Rack::Utils.escape_html(jid)
66
+ else
67
+ "<a href='#{root_path}filter/#{within}?substr=#{jid}'>#{::Rack::Utils.escape_html(jid)}</a>"
68
+ end
69
+ end
70
+
71
+ def display_tags(job, within = "retries")
72
+ job.tags.map { |tag|
73
+ "<span class='label label-info jobtag'>#{filter_link(tag, within)}</span>"
74
+ }.join(" ")
54
75
  end
55
76
 
56
77
  # This view helper provide ability display you html code in
@@ -111,13 +132,6 @@ module Sidekiq
111
132
  end
112
133
  end
113
134
 
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
135
  # sidekiq/sidekiq#3243
122
136
  def unfiltered?
123
137
  yield unless env["PATH_INFO"].start_with?("/filter/")
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"],
data/sidekiq.gemspec CHANGED
@@ -23,17 +23,8 @@ Gem::Specification.new do |gem|
23
23
  "rubygems_mfa_required" => "true"
24
24
  }
25
25
 
26
- gem.add_dependency "redis-client", ">= 0.11.0"
26
+ gem.add_dependency "redis-client", ">= 0.14.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"
30
- gem.post_install_message = <<~EOM
31
-
32
- Welcome to Sidekiq 7.0!
33
-
34
- 1. Use `gem 'sidekiq', '<7'` in your Gemfile if you don't want this new version.
35
- 2. Read the release notes at https://github.com/sidekiq/sidekiq/blob/main/docs/7.0-Upgrade.md
36
- 3. If you have problems, search for open/closed issues at https://github.com/sidekiq/sidekiq/issues/
37
-
38
- EOM
39
30
  end
@@ -140,6 +140,7 @@ function checkResponse(resp) {
140
140
 
141
141
  function scheduleLivePoll() {
142
142
  let ti = parseInt(localStorage.sidekiqTimeInterval) || 5000;
143
+ if (ti < 2000) { ti = 2000 }
143
144
  livePollTimer = setTimeout(livePollCallback, ti);
144
145
  }
145
146
 
@@ -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
  }
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
data/web/locales/fr.yml CHANGED
@@ -17,14 +17,17 @@ fr:
17
17
  DeadJobs: Tâches mortes
18
18
  Delete: Supprimer
19
19
  DeleteAll: Tout supprimer
20
+ Deploy: Déploiement
20
21
  Enqueued: En attente
21
22
  Error: Erreur
22
23
  ErrorBacktrace: Backtrace d’erreur
23
24
  ErrorClass: Classe d’erreur
24
25
  ErrorMessage: Message d’erreur
26
+ ExecutionTime: Temps d'exécution
25
27
  Extras: Extras
26
28
  Failed: Échouées
27
29
  Failures: Echecs
30
+ Failure: Echec
28
31
  GoBack: ← Retour
29
32
  History: Historique
30
33
  Job: Tâche
@@ -35,6 +38,7 @@ fr:
35
38
  Latency: Latence
36
39
  LivePoll: Temps réel
37
40
  MemoryUsage: Mémoire utilisée
41
+ Name: Nom
38
42
  Namespace: Namespace
39
43
  NextRetry: Prochain essai
40
44
  NoDeadJobsFound: Aucune tâche morte n'a été trouvée
@@ -63,6 +67,7 @@ fr:
63
67
  RetryNow: Réessayer maintenant
64
68
  Scheduled: Planifiées
65
69
  ScheduledJobs: Tâches planifiées
70
+ Seconds: Secondes
66
71
  ShowAll: Tout montrer
67
72
  SixMonths: 6 mois
68
73
  Size: Taille
@@ -71,6 +76,8 @@ fr:
71
76
  Stop: Arrêter
72
77
  StopAll: Tout arrêter
73
78
  StopPolling: Arrêt du temps réel
79
+ Success: Succès
80
+ Summary: Résumé
74
81
  Thread: Thread
75
82
  Threads: Threads
76
83
  ThreeMonths: 3 mois
@@ -83,3 +90,10 @@ fr:
83
90
  Worker: Travailleur
84
91
  active: actif
85
92
  idle: inactif
93
+ Metrics: Métriques
94
+ NoDataFound: Aucune donnée disponible
95
+ TotalExecutionTime: Temps d'exécution total
96
+ AvgExecutionTime: Temps d'exécution moyen
97
+ Context: Contexte
98
+ Bucket: Bucket
99
+ NoJobMetricsFound: Aucune statistique de tâche récente n'a été trouvée