sidekiq 6.3.1 → 6.4.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

@@ -2,9 +2,12 @@
2
2
 
3
3
  require "securerandom"
4
4
  require "sidekiq/middleware/chain"
5
+ require "sidekiq/job_util"
5
6
 
6
7
  module Sidekiq
7
8
  class Client
9
+ include Sidekiq::JobUtil
10
+
8
11
  ##
9
12
  # Define client-side middleware:
10
13
  #
@@ -100,7 +103,7 @@ module Sidekiq
100
103
 
101
104
  normed = normalize_item(items)
102
105
  payloads = args.map.with_index { |job_args, index|
103
- copy = normed.merge("args" => job_args, "jid" => SecureRandom.hex(12), "enqueued_at" => Time.now.to_f)
106
+ copy = normed.merge("args" => job_args, "jid" => SecureRandom.hex(12))
104
107
  copy["at"] = (at.is_a?(Array) ? at[index] : at) if at
105
108
 
106
109
  result = process_single(items["class"], copy)
@@ -186,8 +189,23 @@ module Sidekiq
186
189
 
187
190
  def raw_push(payloads)
188
191
  @redis_pool.with do |conn|
189
- conn.pipelined do
190
- atomic_push(conn, payloads)
192
+ retryable = true
193
+ begin
194
+ conn.pipelined do |pipeline|
195
+ atomic_push(pipeline, payloads)
196
+ end
197
+ rescue Redis::BaseError => ex
198
+ # 2550 Failover can cause the server to become a replica, need
199
+ # to disconnect and reopen the socket to get back to the primary.
200
+ # 4495 Use the same logic if we have a "Not enough replicas" error from the primary
201
+ # 4985 Use the same logic when a blocking command is force-unblocked
202
+ # The retry logic is copied from sidekiq.rb
203
+ if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
204
+ conn.disconnect!
205
+ retryable = false
206
+ retry
207
+ end
208
+ raise
191
209
  end
192
210
  end
193
211
  true
@@ -218,42 +236,5 @@ module Sidekiq
218
236
  item
219
237
  end
220
238
  end
221
-
222
- def validate(item)
223
- raise(ArgumentError, "Job must be a Hash with 'class' and 'args' keys: `#{item}`") unless item.is_a?(Hash) && item.key?("class") && item.key?("args")
224
- raise(ArgumentError, "Job args must be an Array: `#{item}`") unless item["args"].is_a?(Array)
225
- raise(ArgumentError, "Job class must be either a Class or String representation of the class name: `#{item}`") unless item["class"].is_a?(Class) || item["class"].is_a?(String)
226
- raise(ArgumentError, "Job 'at' must be a Numeric timestamp: `#{item}`") if item.key?("at") && !item["at"].is_a?(Numeric)
227
- raise(ArgumentError, "Job tags must be an Array: `#{item}`") if item["tags"] && !item["tags"].is_a?(Array)
228
- end
229
-
230
- def normalize_item(item)
231
- validate(item)
232
- # raise(ArgumentError, "Arguments must be native JSON types, see https://github.com/mperham/sidekiq/wiki/Best-Practices") unless JSON.load(JSON.dump(item['args'])) == item['args']
233
-
234
- # merge in the default sidekiq_options for the item's class and/or wrapped element
235
- # this allows ActiveJobs to control sidekiq_options too.
236
- defaults = normalized_hash(item["class"])
237
- defaults = defaults.merge(item["wrapped"].get_sidekiq_options) if item["wrapped"].respond_to?("get_sidekiq_options")
238
- item = defaults.merge(item)
239
-
240
- raise(ArgumentError, "Job must include a valid queue name") if item["queue"].nil? || item["queue"] == ""
241
-
242
- item["class"] = item["class"].to_s
243
- item["queue"] = item["queue"].to_s
244
- item["jid"] ||= SecureRandom.hex(12)
245
- item["created_at"] ||= Time.now.to_f
246
-
247
- item
248
- end
249
-
250
- def normalized_hash(item_class)
251
- if item_class.is_a?(Class)
252
- raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item_class.ancestors.inspect}") unless item_class.respond_to?("get_sidekiq_options")
253
- item_class.get_sidekiq_options
254
- else
255
- Sidekiq.default_worker_options
256
- end
257
- end
258
239
  end
259
240
  end
data/lib/sidekiq/delay.rb CHANGED
@@ -3,6 +3,8 @@
3
3
  module Sidekiq
4
4
  module Extensions
5
5
  def self.enable_delay!
6
+ warn "Sidekiq's Delayed Extensions will be removed in Sidekiq 7.0", uplevel: 1
7
+
6
8
  if defined?(::ActiveSupport)
7
9
  require "sidekiq/extensions/active_record"
8
10
  require "sidekiq/extensions/action_mailer"
data/lib/sidekiq/fetch.rb CHANGED
@@ -59,9 +59,9 @@ module Sidekiq
59
59
  end
60
60
 
61
61
  Sidekiq.redis do |conn|
62
- conn.pipelined do
62
+ conn.pipelined do |pipeline|
63
63
  jobs_to_requeue.each do |queue, jobs|
64
- conn.rpush(queue, jobs)
64
+ pipeline.rpush(queue, jobs)
65
65
  end
66
66
  end
67
67
  end
@@ -12,46 +12,34 @@ module Sidekiq
12
12
 
13
13
  yield
14
14
 
15
- with_elapsed_time_context(start) do
16
- @logger.info("done")
17
- end
15
+ Sidekiq::Context.add(:elapsed, elapsed(start))
16
+ @logger.info("done")
18
17
  rescue Exception
19
- with_elapsed_time_context(start) do
20
- @logger.info("fail")
21
- end
18
+ Sidekiq::Context.add(:elapsed, elapsed(start))
19
+ @logger.info("fail")
22
20
 
23
21
  raise
24
22
  end
25
23
 
26
24
  def prepare(job_hash, &block)
27
- level = job_hash["log_level"]
28
- if level
29
- @logger.log_at(level) do
30
- Sidekiq::Context.with(job_hash_context(job_hash), &block)
31
- end
32
- else
33
- Sidekiq::Context.with(job_hash_context(job_hash), &block)
34
- end
35
- end
36
-
37
- def job_hash_context(job_hash)
38
25
  # If we're using a wrapper class, like ActiveJob, use the "wrapped"
39
26
  # attribute to expose the underlying thing.
40
27
  h = {
41
28
  class: job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"],
42
29
  jid: job_hash["jid"]
43
30
  }
44
- h[:bid] = job_hash["bid"] if job_hash["bid"]
45
- h[:tags] = job_hash["tags"] if job_hash["tags"]
46
- h
47
- end
48
-
49
- def with_elapsed_time_context(start, &block)
50
- Sidekiq::Context.with(elapsed_time_context(start), &block)
51
- end
31
+ h[:bid] = job_hash["bid"] if job_hash.has_key?("bid")
32
+ h[:tags] = job_hash["tags"] if job_hash.has_key?("tags")
52
33
 
53
- def elapsed_time_context(start)
54
- {elapsed: elapsed(start).to_s}
34
+ Thread.current[:sidekiq_context] = h
35
+ level = job_hash["log_level"]
36
+ if level
37
+ @logger.log_at(level, &block)
38
+ else
39
+ yield
40
+ end
41
+ ensure
42
+ Thread.current[:sidekiq_context] = nil
55
43
  end
56
44
 
57
45
  private
@@ -34,9 +34,10 @@ module Sidekiq
34
34
  # The job will be retried this number of times before giving up. (If simply
35
35
  # 'true', Sidekiq retries 25 times)
36
36
  #
37
- # We'll add a bit more data to the job to support retries:
37
+ # Relevant options for job retries:
38
38
  #
39
- # * 'queue' - the queue to use
39
+ # * 'queue' - the queue for the initial job
40
+ # * 'retry_queue' - if job retries should be pushed to a different (e.g. lower priority) queue
40
41
  # * 'retry_count' - number of times we've retried so far.
41
42
  # * 'error_message' - the message from the exception
42
43
  # * 'error_class' - the exception class
@@ -52,11 +53,12 @@ module Sidekiq
52
53
  #
53
54
  # Sidekiq.options[:max_retries] = 7
54
55
  #
55
- # or limit the number of retries for a particular worker with:
56
+ # or limit the number of retries for a particular worker and send retries to
57
+ # a low priority queue with:
56
58
  #
57
59
  # class MyWorker
58
60
  # include Sidekiq::Worker
59
- # sidekiq_options :retry => 10
61
+ # sidekiq_options retry: 10, retry_queue: 'low'
60
62
  # end
61
63
  #
62
64
  class JobRetry
@@ -0,0 +1,65 @@
1
+ require "securerandom"
2
+ require "time"
3
+
4
+ module Sidekiq
5
+ module JobUtil
6
+ # These functions encapsulate various job utilities.
7
+ # They must be simple and free from side effects.
8
+
9
+ def validate(item)
10
+ raise(ArgumentError, "Job must be a Hash with 'class' and 'args' keys: `#{item}`") unless item.is_a?(Hash) && item.key?("class") && item.key?("args")
11
+ raise(ArgumentError, "Job args must be an Array: `#{item}`") unless item["args"].is_a?(Array)
12
+ raise(ArgumentError, "Job class must be either a Class or String representation of the class name: `#{item}`") unless item["class"].is_a?(Class) || item["class"].is_a?(String)
13
+ raise(ArgumentError, "Job 'at' must be a Numeric timestamp: `#{item}`") if item.key?("at") && !item["at"].is_a?(Numeric)
14
+ raise(ArgumentError, "Job tags must be an Array: `#{item}`") if item["tags"] && !item["tags"].is_a?(Array)
15
+
16
+ if Sidekiq.options[:on_complex_arguments] == :raise
17
+ msg = <<~EOM
18
+ Job arguments to #{item["class"]} must be native JSON types, see https://github.com/mperham/sidekiq/wiki/Best-Practices.
19
+ To disable this error, remove `Sidekiq.strict_args!` from your initializer.
20
+ EOM
21
+ raise(ArgumentError, msg) unless json_safe?(item)
22
+ elsif Sidekiq.options[:on_complex_arguments] == :warn
23
+ Sidekiq.logger.warn <<~EOM unless json_safe?(item)
24
+ Job arguments to #{item["class"]} do not serialize to JSON safely. This will raise an error in
25
+ Sidekiq 7.0. See https://github.com/mperham/sidekiq/wiki/Best-Practices or raise an error today
26
+ by calling `Sidekiq.strict_args!` during Sidekiq initialization.
27
+ EOM
28
+ end
29
+ end
30
+
31
+ def normalize_item(item)
32
+ validate(item)
33
+
34
+ # merge in the default sidekiq_options for the item's class and/or wrapped element
35
+ # this allows ActiveJobs to control sidekiq_options too.
36
+ defaults = normalized_hash(item["class"])
37
+ defaults = defaults.merge(item["wrapped"].get_sidekiq_options) if item["wrapped"].respond_to?(:get_sidekiq_options)
38
+ item = defaults.merge(item)
39
+
40
+ raise(ArgumentError, "Job must include a valid queue name") if item["queue"].nil? || item["queue"] == ""
41
+
42
+ item["class"] = item["class"].to_s
43
+ item["queue"] = item["queue"].to_s
44
+ item["jid"] ||= SecureRandom.hex(12)
45
+ item["created_at"] ||= Time.now.to_f
46
+
47
+ item
48
+ end
49
+
50
+ def normalized_hash(item_class)
51
+ if item_class.is_a?(Class)
52
+ raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item_class.ancestors.inspect}") unless item_class.respond_to?(:get_sidekiq_options)
53
+ item_class.get_sidekiq_options
54
+ else
55
+ Sidekiq.default_worker_options
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def json_safe?(item)
62
+ JSON.parse(JSON.dump(item["args"])) == item["args"]
63
+ end
64
+ end
65
+ end
@@ -84,9 +84,9 @@ module Sidekiq
84
84
  # Note we don't stop the heartbeat thread; if the process
85
85
  # doesn't actually exit, it'll reappear in the Web UI.
86
86
  Sidekiq.redis do |conn|
87
- conn.pipelined do
88
- conn.srem("processes", identity)
89
- conn.unlink("#{identity}:workers")
87
+ conn.pipelined do |pipeline|
88
+ pipeline.srem("processes", identity)
89
+ pipeline.unlink("#{identity}:workers")
90
90
  end
91
91
  end
92
92
  rescue
@@ -107,14 +107,14 @@ module Sidekiq
107
107
  nowdate = Time.now.utc.strftime("%Y-%m-%d")
108
108
  begin
109
109
  Sidekiq.redis do |conn|
110
- conn.pipelined do
111
- conn.incrby("stat:processed", procd)
112
- conn.incrby("stat:processed:#{nowdate}", procd)
113
- conn.expire("stat:processed:#{nowdate}", STATS_TTL)
114
-
115
- conn.incrby("stat:failed", fails)
116
- conn.incrby("stat:failed:#{nowdate}", fails)
117
- conn.expire("stat:failed:#{nowdate}", STATS_TTL)
110
+ conn.pipelined do |pipeline|
111
+ pipeline.incrby("stat:processed", procd)
112
+ pipeline.incrby("stat:processed:#{nowdate}", procd)
113
+ pipeline.expire("stat:processed:#{nowdate}", STATS_TTL)
114
+
115
+ pipeline.incrby("stat:failed", fails)
116
+ pipeline.incrby("stat:failed:#{nowdate}", fails)
117
+ pipeline.expire("stat:failed:#{nowdate}", STATS_TTL)
118
118
  end
119
119
  end
120
120
  rescue => ex
@@ -138,20 +138,20 @@ module Sidekiq
138
138
  nowdate = Time.now.utc.strftime("%Y-%m-%d")
139
139
 
140
140
  Sidekiq.redis do |conn|
141
- conn.multi do
142
- conn.incrby("stat:processed", procd)
143
- conn.incrby("stat:processed:#{nowdate}", procd)
144
- conn.expire("stat:processed:#{nowdate}", STATS_TTL)
141
+ conn.multi do |transaction|
142
+ transaction.incrby("stat:processed", procd)
143
+ transaction.incrby("stat:processed:#{nowdate}", procd)
144
+ transaction.expire("stat:processed:#{nowdate}", STATS_TTL)
145
145
 
146
- conn.incrby("stat:failed", fails)
147
- conn.incrby("stat:failed:#{nowdate}", fails)
148
- conn.expire("stat:failed:#{nowdate}", STATS_TTL)
146
+ transaction.incrby("stat:failed", fails)
147
+ transaction.incrby("stat:failed:#{nowdate}", fails)
148
+ transaction.expire("stat:failed:#{nowdate}", STATS_TTL)
149
149
 
150
- conn.unlink(workers_key)
150
+ transaction.unlink(workers_key)
151
151
  curstate.each_pair do |tid, hash|
152
- conn.hset(workers_key, tid, Sidekiq.dump_json(hash))
152
+ transaction.hset(workers_key, tid, Sidekiq.dump_json(hash))
153
153
  end
154
- conn.expire(workers_key, 60)
154
+ transaction.expire(workers_key, 60)
155
155
  end
156
156
  end
157
157
 
@@ -161,17 +161,17 @@ module Sidekiq
161
161
  kb = memory_usage(::Process.pid)
162
162
 
163
163
  _, exists, _, _, msg = Sidekiq.redis { |conn|
164
- conn.multi {
165
- conn.sadd("processes", key)
166
- conn.exists?(key)
167
- conn.hmset(key, "info", to_json,
164
+ conn.multi { |transaction|
165
+ transaction.sadd("processes", key)
166
+ transaction.exists?(key)
167
+ transaction.hmset(key, "info", to_json,
168
168
  "busy", curstate.size,
169
169
  "beat", Time.now.to_f,
170
170
  "rtt_us", rtt,
171
171
  "quiet", @done,
172
172
  "rss", kb)
173
- conn.expire(key, 60)
174
- conn.rpop("#{key}-signals")
173
+ transaction.expire(key, 60)
174
+ transaction.rpop("#{key}-signals")
175
175
  }
176
176
  }
177
177
 
@@ -16,6 +16,10 @@ module Sidekiq
16
16
  def self.current
17
17
  Thread.current[:sidekiq_context] ||= {}
18
18
  end
19
+
20
+ def self.add(k, v)
21
+ Thread.current[:sidekiq_context][k] = v
22
+ end
19
23
  end
20
24
 
21
25
  module LoggingUtils
@@ -55,9 +55,6 @@ module Sidekiq
55
55
  fire_event(:quiet, reverse: true)
56
56
  end
57
57
 
58
- # hack for quicker development / testing environment #2774
59
- PAUSE_TIME = $stdout.tty? ? 0.1 : 0.5
60
-
61
58
  def stop(deadline)
62
59
  quiet
63
60
  fire_event(:shutdown, reverse: true)
@@ -69,12 +66,7 @@ module Sidekiq
69
66
  return if @workers.empty?
70
67
 
71
68
  logger.info { "Pausing to allow workers to finish..." }
72
- remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
73
- while remaining > PAUSE_TIME
74
- return if @workers.empty?
75
- sleep PAUSE_TIME
76
- remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
77
- end
69
+ wait_for(deadline) { @workers.empty? }
78
70
  return if @workers.empty?
79
71
 
80
72
  hard_shutdown
@@ -130,6 +122,12 @@ module Sidekiq
130
122
  cleanup.each do |processor|
131
123
  processor.kill
132
124
  end
125
+
126
+ # when this method returns, we immediately call `exit` which may not give
127
+ # the remaining threads time to run `ensure` blocks, etc. We pause here up
128
+ # to 3 seconds to give threads a minimal amount of time to run `ensure` blocks.
129
+ deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + 3
130
+ wait_for(deadline) { @workers.empty? }
133
131
  end
134
132
  end
135
133
  end
@@ -20,7 +20,12 @@ module Sidekiq
20
20
  end
21
21
 
22
22
  def call(_, job, _, _)
23
- job["cattr"] = @klass.attributes
23
+ attrs = @klass.attributes
24
+ if job.has_key?("cattr")
25
+ job["cattr"].merge!(attrs)
26
+ else
27
+ job["cattr"] = attrs
28
+ end
24
29
  yield
25
30
  end
26
31
  end
@@ -16,22 +16,22 @@ module Sidekiq
16
16
 
17
17
  case type
18
18
  when "zset"
19
- total_size, items = conn.multi {
20
- conn.zcard(key)
19
+ total_size, items = conn.multi { |transaction|
20
+ transaction.zcard(key)
21
21
  if rev
22
- conn.zrevrange(key, starting, ending, with_scores: true)
22
+ transaction.zrevrange(key, starting, ending, with_scores: true)
23
23
  else
24
- conn.zrange(key, starting, ending, with_scores: true)
24
+ transaction.zrange(key, starting, ending, with_scores: true)
25
25
  end
26
26
  }
27
27
  [current_page, total_size, items]
28
28
  when "list"
29
- total_size, items = conn.multi {
30
- conn.llen(key)
29
+ total_size, items = conn.multi { |transaction|
30
+ transaction.llen(key)
31
31
  if rev
32
- conn.lrange(key, -ending - 1, -starting - 1)
32
+ transaction.lrange(key, -ending - 1, -starting - 1)
33
33
  else
34
- conn.lrange(key, starting, ending)
34
+ transaction.lrange(key, starting, ending)
35
35
  end
36
36
  }
37
37
  items.reverse! if rev
data/lib/sidekiq/rails.rb CHANGED
@@ -42,7 +42,7 @@ module Sidekiq
42
42
  # This is the integration code necessary so that if code uses `Rails.logger.info "Hello"`,
43
43
  # it will appear in the Sidekiq console with all of the job context. See #5021 and
44
44
  # https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
45
- unless ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
45
+ unless ::Rails.logger == ::Sidekiq.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
46
46
  ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(::Sidekiq.logger))
47
47
  end
48
48
  end
@@ -19,10 +19,11 @@ module Sidekiq
19
19
  LUA
20
20
 
21
21
  def initialize
22
+ @done = false
22
23
  @lua_zpopbyscore_sha = nil
23
24
  end
24
25
 
25
- def enqueue_jobs(now = Time.now.to_f.to_s, sorted_sets = SETS)
26
+ def enqueue_jobs(sorted_sets = SETS)
26
27
  # A job's "score" in Redis is the time at which it should be processed.
27
28
  # Just check Redis for the set of jobs with a timestamp before now.
28
29
  Sidekiq.redis do |conn|
@@ -31,7 +32,7 @@ module Sidekiq
31
32
  # We need to go through the list one at a time to reduce the risk of something
32
33
  # going wrong between the time jobs are popped from the scheduled queue and when
33
34
  # they are pushed onto a work queue and losing the jobs.
34
- while (job = zpopbyscore(conn, keys: [sorted_set], argv: [now]))
35
+ while !@done && (job = zpopbyscore(conn, keys: [sorted_set], argv: [Time.now.to_f.to_s]))
35
36
  Sidekiq::Client.push(Sidekiq.load_json(job))
36
37
  Sidekiq.logger.debug { "enqueued #{sorted_set}: #{job}" }
37
38
  end
@@ -39,10 +40,17 @@ module Sidekiq
39
40
  end
40
41
  end
41
42
 
43
+ def terminate
44
+ @done = true
45
+ end
46
+
42
47
  private
43
48
 
44
49
  def zpopbyscore(conn, keys: nil, argv: nil)
45
- @lua_zpopbyscore_sha = conn.script(:load, LUA_ZPOPBYSCORE) if @lua_zpopbyscore_sha.nil?
50
+ if @lua_zpopbyscore_sha.nil?
51
+ raw_conn = conn.respond_to?(:redis) ? conn.redis : conn
52
+ @lua_zpopbyscore_sha = raw_conn.script(:load, LUA_ZPOPBYSCORE)
53
+ end
46
54
 
47
55
  conn.evalsha(@lua_zpopbyscore_sha, keys: keys, argv: argv)
48
56
  rescue Redis::CommandError => e
@@ -74,6 +82,8 @@ module Sidekiq
74
82
  # Shut down this instance, will pause until the thread is dead.
75
83
  def terminate
76
84
  @done = true
85
+ @enq.terminate if @enq.respond_to?(:terminate)
86
+
77
87
  if @thread
78
88
  t = @thread
79
89
  @thread = nil
data/lib/sidekiq/util.rb CHANGED
@@ -39,6 +39,19 @@ module Sidekiq
39
39
  module Util
40
40
  include ExceptionHandler
41
41
 
42
+ # hack for quicker development / testing environment #2774
43
+ PAUSE_TIME = $stdout.tty? ? 0.1 : 0.5
44
+
45
+ # Wait for the orblock to be true or the deadline passed.
46
+ def wait_for(deadline, &condblock)
47
+ remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
48
+ while remaining > PAUSE_TIME
49
+ return if condblock.call
50
+ sleep PAUSE_TIME
51
+ remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
52
+ end
53
+ end
54
+
42
55
  def watchdog(last_words)
43
56
  yield
44
57
  rescue Exception => ex
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "6.3.1"
4
+ VERSION = "6.4.1"
5
5
  end
@@ -50,7 +50,10 @@ module Sidekiq
50
50
 
51
51
  get "/" do
52
52
  @redis_info = redis_info.select { |k, v| REDIS_KEYS.include? k }
53
- stats_history = Sidekiq::Stats::History.new((params["days"] || 30).to_i)
53
+ days = (params["days"] || 30).to_i
54
+ return halt(401) if days < 1 || days > 180
55
+
56
+ stats_history = Sidekiq::Stats::History.new(days)
54
57
  @processed_history = stats_history.processed
55
58
  @failed_history = stats_history.failed
56
59
 
@@ -299,7 +302,7 @@ module Sidekiq
299
302
  return [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found"]] unless action
300
303
 
301
304
  app = @klass
302
- resp = catch(:halt) do # rubocop:disable Standard/SemanticBlocks
305
+ resp = catch(:halt) do
303
306
  self.class.run_befores(app, action)
304
307
  action.instance_exec env, &action.block
305
308
  ensure
@@ -242,7 +242,7 @@ module Sidekiq
242
242
  queue class args retry_count retried_at failed_at
243
243
  jid error_message error_class backtrace
244
244
  error_backtrace enqueued_at retry wrapped
245
- created_at tags
245
+ created_at tags display_class
246
246
  ])
247
247
 
248
248
  def retry_extra_items(retry_job)
data/lib/sidekiq/web.rb CHANGED
@@ -148,9 +148,9 @@ module Sidekiq
148
148
 
149
149
  ::Rack::Builder.new do
150
150
  use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
151
- root: ASSETS,
152
- cascade: true,
153
- header_rules: rules
151
+ root: ASSETS,
152
+ cascade: true,
153
+ header_rules: rules
154
154
  m.each { |middleware, block| use(*middleware, &block) }
155
155
  use Sidekiq::Web::CsrfProtection unless $TESTING
156
156
  run WebApplication.new(klass)