sidekiq 6.4.2 → 6.5.12

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +89 -0
  3. data/bin/sidekiqload +17 -5
  4. data/lib/sidekiq/api.rb +196 -45
  5. data/lib/sidekiq/cli.rb +46 -32
  6. data/lib/sidekiq/client.rb +6 -6
  7. data/lib/sidekiq/component.rb +65 -0
  8. data/lib/sidekiq/delay.rb +1 -1
  9. data/lib/sidekiq/fetch.rb +18 -16
  10. data/lib/sidekiq/job_retry.rb +60 -39
  11. data/lib/sidekiq/job_util.rb +7 -3
  12. data/lib/sidekiq/launcher.rb +24 -21
  13. data/lib/sidekiq/logger.rb +1 -1
  14. data/lib/sidekiq/manager.rb +23 -20
  15. data/lib/sidekiq/metrics/deploy.rb +47 -0
  16. data/lib/sidekiq/metrics/query.rb +153 -0
  17. data/lib/sidekiq/metrics/shared.rb +94 -0
  18. data/lib/sidekiq/metrics/tracking.rb +134 -0
  19. data/lib/sidekiq/middleware/chain.rb +82 -38
  20. data/lib/sidekiq/middleware/current_attributes.rb +18 -12
  21. data/lib/sidekiq/middleware/i18n.rb +2 -0
  22. data/lib/sidekiq/middleware/modules.rb +21 -0
  23. data/lib/sidekiq/monitor.rb +1 -1
  24. data/lib/sidekiq/paginator.rb +11 -3
  25. data/lib/sidekiq/processor.rb +21 -15
  26. data/lib/sidekiq/rails.rb +12 -13
  27. data/lib/sidekiq/redis_client_adapter.rb +154 -0
  28. data/lib/sidekiq/redis_connection.rb +78 -47
  29. data/lib/sidekiq/ring_buffer.rb +29 -0
  30. data/lib/sidekiq/scheduled.rb +53 -24
  31. data/lib/sidekiq/testing.rb +1 -1
  32. data/lib/sidekiq/transaction_aware_client.rb +45 -0
  33. data/lib/sidekiq/version.rb +1 -1
  34. data/lib/sidekiq/web/action.rb +3 -3
  35. data/lib/sidekiq/web/application.rb +21 -5
  36. data/lib/sidekiq/web/helpers.rb +18 -5
  37. data/lib/sidekiq/web.rb +5 -1
  38. data/lib/sidekiq/worker.rb +8 -4
  39. data/lib/sidekiq.rb +87 -18
  40. data/sidekiq.gemspec +2 -2
  41. data/web/assets/javascripts/application.js +2 -1
  42. data/web/assets/javascripts/chart.min.js +13 -0
  43. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  44. data/web/assets/javascripts/dashboard.js +0 -17
  45. data/web/assets/javascripts/graph.js +16 -0
  46. data/web/assets/javascripts/metrics.js +262 -0
  47. data/web/assets/stylesheets/application.css +44 -1
  48. data/web/locales/el.yml +43 -19
  49. data/web/locales/en.yml +7 -0
  50. data/web/locales/ja.yml +7 -0
  51. data/web/locales/pt-br.yml +27 -9
  52. data/web/locales/zh-cn.yml +36 -11
  53. data/web/locales/zh-tw.yml +32 -7
  54. data/web/views/_nav.erb +1 -1
  55. data/web/views/busy.erb +7 -2
  56. data/web/views/dashboard.erb +1 -0
  57. data/web/views/metrics.erb +69 -0
  58. data/web/views/metrics_for_job.erb +87 -0
  59. data/web/views/queue.erb +5 -1
  60. metadata +34 -9
  61. data/lib/sidekiq/exception_handler.rb +0 -27
  62. data/lib/sidekiq/util.rb +0 -108
@@ -5,8 +5,79 @@ require "redis"
5
5
  require "uri"
6
6
 
7
7
  module Sidekiq
8
- class RedisConnection
8
+ module RedisConnection
9
+ class RedisAdapter
10
+ BaseError = Redis::BaseError
11
+ CommandError = Redis::CommandError
12
+
13
+ def initialize(options)
14
+ warn("Usage of the 'redis' gem within Sidekiq itself is deprecated, Sidekiq 7.0 will only use the new, simpler 'redis-client' gem", caller) if ENV["SIDEKIQ_REDIS_CLIENT"] == "1"
15
+ @options = options
16
+ end
17
+
18
+ def new_client
19
+ namespace = @options[:namespace]
20
+
21
+ client = Redis.new client_opts(@options)
22
+ if namespace
23
+ begin
24
+ require "redis/namespace"
25
+ Redis::Namespace.new(namespace, redis: client)
26
+ rescue LoadError
27
+ Sidekiq.logger.error("Your Redis configuration uses the namespace '#{namespace}' but the redis-namespace gem is not included in the Gemfile." \
28
+ "Add the gem to your Gemfile to continue using a namespace. Otherwise, remove the namespace parameter.")
29
+ exit(-127)
30
+ end
31
+ else
32
+ client
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def client_opts(options)
39
+ opts = options.dup
40
+ if opts[:namespace]
41
+ opts.delete(:namespace)
42
+ end
43
+
44
+ if opts[:network_timeout]
45
+ opts[:timeout] = opts[:network_timeout]
46
+ opts.delete(:network_timeout)
47
+ end
48
+
49
+ # Issue #3303, redis-rb will silently retry an operation.
50
+ # This can lead to duplicate jobs if Sidekiq::Client's LPUSH
51
+ # is performed twice but I believe this is much, much rarer
52
+ # than the reconnect silently fixing a problem; we keep it
53
+ # on by default.
54
+ opts[:reconnect_attempts] ||= 1
55
+
56
+ opts
57
+ end
58
+ end
59
+
60
+ @adapter = RedisAdapter
61
+
9
62
  class << self
63
+ attr_reader :adapter
64
+
65
+ # RedisConnection.adapter = :redis
66
+ # RedisConnection.adapter = :redis_client
67
+ def adapter=(adapter)
68
+ raise "no" if adapter == self
69
+ result = case adapter
70
+ when :redis
71
+ RedisAdapter
72
+ when Class
73
+ adapter
74
+ else
75
+ require "sidekiq/#{adapter}_adapter"
76
+ nil
77
+ end
78
+ @adapter = result if result
79
+ end
80
+
10
81
  def create(options = {})
11
82
  symbolized_options = options.transform_keys(&:to_sym)
12
83
 
@@ -19,20 +90,21 @@ module Sidekiq
19
90
  elsif Sidekiq.server?
20
91
  # Give ourselves plenty of connections. pool is lazy
21
92
  # so we won't create them until we need them.
22
- Sidekiq.options[:concurrency] + 5
93
+ Sidekiq[:concurrency] + 5
23
94
  elsif ENV["RAILS_MAX_THREADS"]
24
95
  Integer(ENV["RAILS_MAX_THREADS"])
25
96
  else
26
97
  5
27
98
  end
28
99
 
29
- verify_sizing(size, Sidekiq.options[:concurrency]) if Sidekiq.server?
100
+ verify_sizing(size, Sidekiq[:concurrency]) if Sidekiq.server?
30
101
 
31
102
  pool_timeout = symbolized_options[:pool_timeout] || 1
32
103
  log_info(symbolized_options)
33
104
 
105
+ redis_config = adapter.new(symbolized_options)
34
106
  ConnectionPool.new(timeout: pool_timeout, size: size) do
35
- build_client(symbolized_options)
107
+ redis_config.new_client
36
108
  end
37
109
  end
38
110
 
@@ -50,47 +122,6 @@ module Sidekiq
50
122
  raise ArgumentError, "Your Redis connection pool is too small for Sidekiq. Your pool has #{size} connections but must have at least #{concurrency + 2}" if size < (concurrency + 2)
51
123
  end
52
124
 
53
- def build_client(options)
54
- namespace = options[:namespace]
55
-
56
- client = Redis.new client_opts(options)
57
- if namespace
58
- begin
59
- require "redis/namespace"
60
- Redis::Namespace.new(namespace, redis: client)
61
- rescue LoadError
62
- Sidekiq.logger.error("Your Redis configuration uses the namespace '#{namespace}' but the redis-namespace gem is not included in the Gemfile." \
63
- "Add the gem to your Gemfile to continue using a namespace. Otherwise, remove the namespace parameter.")
64
- exit(-127)
65
- end
66
- else
67
- client
68
- end
69
- end
70
-
71
- def client_opts(options)
72
- opts = options.dup
73
- if opts[:namespace]
74
- opts.delete(:namespace)
75
- end
76
-
77
- if opts[:network_timeout]
78
- opts[:timeout] = opts[:network_timeout]
79
- opts.delete(:network_timeout)
80
- end
81
-
82
- opts[:driver] ||= Redis::Connection.drivers.last || "ruby"
83
-
84
- # Issue #3303, redis-rb will silently retry an operation.
85
- # This can lead to duplicate jobs if Sidekiq::Client's LPUSH
86
- # is performed twice but I believe this is much, much rarer
87
- # than the reconnect silently fixing a problem; we keep it
88
- # on by default.
89
- opts[:reconnect_attempts] ||= 1
90
-
91
- opts
92
- end
93
-
94
125
  def log_info(options)
95
126
  redacted = "REDACTED"
96
127
 
@@ -110,9 +141,9 @@ module Sidekiq
110
141
  sentinel[:password] = redacted if sentinel[:password]
111
142
  end
112
143
  if Sidekiq.server?
113
- Sidekiq.logger.info("Booting Sidekiq #{Sidekiq::VERSION} with redis options #{scrubbed_options}")
144
+ Sidekiq.logger.info("Booting Sidekiq #{Sidekiq::VERSION} with #{adapter.name} options #{scrubbed_options}")
114
145
  else
115
- Sidekiq.logger.debug("#{Sidekiq::NAME} client with redis options #{scrubbed_options}")
146
+ Sidekiq.logger.debug("#{Sidekiq::NAME} client with #{adapter.name} options #{scrubbed_options}")
116
147
  end
117
148
  end
118
149
 
@@ -0,0 +1,29 @@
1
+ require "forwardable"
2
+
3
+ module Sidekiq
4
+ class RingBuffer
5
+ include Enumerable
6
+ extend Forwardable
7
+ def_delegators :@buf, :[], :each, :size
8
+
9
+ def initialize(size, default = 0)
10
+ @size = size
11
+ @buf = Array.new(size, default)
12
+ @index = 0
13
+ end
14
+
15
+ def <<(element)
16
+ @buf[@index % @size] = element
17
+ @index += 1
18
+ element
19
+ end
20
+
21
+ def buffer
22
+ @buf
23
+ end
24
+
25
+ def reset(default = 0)
26
+ @buf.fill(default)
27
+ end
28
+ end
29
+ end
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "sidekiq"
4
- require "sidekiq/util"
5
- require "sidekiq/api"
4
+ require "sidekiq/component"
6
5
 
7
6
  module Sidekiq
8
7
  module Scheduled
@@ -52,8 +51,8 @@ module Sidekiq
52
51
  @lua_zpopbyscore_sha = raw_conn.script(:load, LUA_ZPOPBYSCORE)
53
52
  end
54
53
 
55
- conn.evalsha(@lua_zpopbyscore_sha, keys: keys, argv: argv)
56
- rescue Redis::CommandError => e
54
+ conn.evalsha(@lua_zpopbyscore_sha, keys, argv)
55
+ rescue RedisConnection.adapter::CommandError => e
57
56
  raise unless e.message.start_with?("NOSCRIPT")
58
57
 
59
58
  @lua_zpopbyscore_sha = nil
@@ -67,12 +66,13 @@ module Sidekiq
67
66
  # just pops the job back onto its original queue so the
68
67
  # workers can pick it up like any other job.
69
68
  class Poller
70
- include Util
69
+ include Sidekiq::Component
71
70
 
72
71
  INITIAL_WAIT = 10
73
72
 
74
- def initialize
75
- @enq = (Sidekiq.options[:scheduled_enq] || Sidekiq::Scheduled::Enq).new
73
+ def initialize(options)
74
+ @config = options
75
+ @enq = (options[:scheduled_enq] || Sidekiq::Scheduled::Enq).new
76
76
  @sleeper = ConnectionPool::TimedStack.new
77
77
  @done = false
78
78
  @thread = nil
@@ -100,7 +100,7 @@ module Sidekiq
100
100
  enqueue
101
101
  wait
102
102
  end
103
- Sidekiq.logger.info("Scheduler exiting...")
103
+ logger.info("Scheduler exiting...")
104
104
  }
105
105
  end
106
106
 
@@ -147,13 +147,16 @@ module Sidekiq
147
147
  # As we run more processes, the scheduling interval average will approach an even spread
148
148
  # between 0 and poll interval so we don't need this artifical boost.
149
149
  #
150
- if process_count < 10
150
+ count = process_count
151
+ interval = poll_interval_average(count)
152
+
153
+ if count < 10
151
154
  # For small clusters, calculate a random interval that is ±50% the desired average.
152
- poll_interval_average * rand + poll_interval_average.to_f / 2
155
+ interval * rand + interval.to_f / 2
153
156
  else
154
157
  # With 10+ processes, we should have enough randomness to get decent polling
155
158
  # across the entire timespan
156
- poll_interval_average * rand
159
+ interval * rand
157
160
  end
158
161
  end
159
162
 
@@ -170,38 +173,64 @@ module Sidekiq
170
173
  # the same time: the thundering herd problem.
171
174
  #
172
175
  # We only do this if poll_interval_average is unset (the default).
173
- def poll_interval_average
174
- Sidekiq.options[:poll_interval_average] ||= scaled_poll_interval
176
+ def poll_interval_average(count)
177
+ @config[:poll_interval_average] || scaled_poll_interval(count)
175
178
  end
176
179
 
177
180
  # Calculates an average poll interval based on the number of known Sidekiq processes.
178
181
  # This minimizes a single point of failure by dispersing check-ins but without taxing
179
182
  # Redis if you run many Sidekiq processes.
180
- def scaled_poll_interval
181
- process_count * Sidekiq.options[:average_scheduled_poll_interval]
183
+ def scaled_poll_interval(process_count)
184
+ process_count * @config[:average_scheduled_poll_interval]
182
185
  end
183
186
 
184
187
  def process_count
185
- # The work buried within Sidekiq::ProcessSet#cleanup can be
186
- # expensive at scale. Cut it down by 90% with this counter.
187
- # NB: This method is only called by the scheduler thread so we
188
- # don't need to worry about the thread safety of +=.
189
- pcount = Sidekiq::ProcessSet.new(@count_calls % 10 == 0).size
188
+ pcount = Sidekiq.redis { |conn| conn.scard("processes") }
190
189
  pcount = 1 if pcount == 0
191
- @count_calls += 1
192
190
  pcount
193
191
  end
194
192
 
193
+ # A copy of Sidekiq::ProcessSet#cleanup because server
194
+ # should never depend on sidekiq/api.
195
+ def cleanup
196
+ # dont run cleanup more than once per minute
197
+ return 0 unless Sidekiq.redis { |conn| conn.set("process_cleanup", "1", nx: true, ex: 60) }
198
+
199
+ count = 0
200
+ Sidekiq.redis do |conn|
201
+ procs = conn.sscan_each("processes").to_a
202
+ heartbeats = conn.pipelined { |pipeline|
203
+ procs.each do |key|
204
+ pipeline.hget(key, "info")
205
+ end
206
+ }
207
+
208
+ # the hash named key has an expiry of 60 seconds.
209
+ # if it's not found, that means the process has not reported
210
+ # in to Redis and probably died.
211
+ to_prune = procs.select.with_index { |proc, i|
212
+ heartbeats[i].nil?
213
+ }
214
+ count = conn.srem("processes", to_prune) unless to_prune.empty?
215
+ end
216
+ count
217
+ end
218
+
195
219
  def initial_wait
196
- # Have all processes sleep between 5-15 seconds. 10 seconds
197
- # to give time for the heartbeat to register (if the poll interval is going to be calculated by the number
220
+ # Have all processes sleep between 5-15 seconds. 10 seconds to give time for
221
+ # the heartbeat to register (if the poll interval is going to be calculated by the number
198
222
  # of workers), and 5 random seconds to ensure they don't all hit Redis at the same time.
199
223
  total = 0
200
- total += INITIAL_WAIT unless Sidekiq.options[:poll_interval_average]
224
+ total += INITIAL_WAIT unless @config[:poll_interval_average]
201
225
  total += (5 * rand)
202
226
 
203
227
  @sleeper.pop(total)
204
228
  rescue Timeout::Error
229
+ ensure
230
+ # periodically clean out the `processes` set in Redis which can collect
231
+ # references to dead processes over time. The process count affects how
232
+ # often we scan for scheduled jobs.
233
+ cleanup
205
234
  end
206
235
  end
207
236
  end
@@ -188,7 +188,7 @@ module Sidekiq
188
188
  end
189
189
 
190
190
  def clear_for(queue, klass)
191
- jobs_by_queue[queue].clear
191
+ jobs_by_queue[queue.to_s].clear
192
192
  jobs_by_class[klass].clear
193
193
  end
194
194
 
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require "sidekiq/client"
5
+
6
+ module Sidekiq
7
+ class TransactionAwareClient
8
+ def initialize(redis_pool)
9
+ @redis_client = Client.new(redis_pool)
10
+ end
11
+
12
+ def push(item)
13
+ # pre-allocate the JID so we can return it immediately and
14
+ # save it to the database as part of the transaction.
15
+ item["jid"] ||= SecureRandom.hex(12)
16
+ AfterCommitEverywhere.after_commit { @redis_client.push(item) }
17
+ item["jid"]
18
+ end
19
+
20
+ ##
21
+ # We don't provide transactionality for push_bulk because we don't want
22
+ # to hold potentially hundreds of thousands of job records in memory due to
23
+ # a long running enqueue process.
24
+ def push_bulk(items)
25
+ @redis_client.push_bulk(items)
26
+ end
27
+ end
28
+ end
29
+
30
+ ##
31
+ # Use `Sidekiq.transactional_push!` in your sidekiq.rb initializer
32
+ module Sidekiq
33
+ def self.transactional_push!
34
+ begin
35
+ require "after_commit_everywhere"
36
+ rescue LoadError
37
+ Sidekiq.logger.error("You need to add after_commit_everywhere to your Gemfile to use Sidekiq's transactional client")
38
+ raise
39
+ end
40
+
41
+ default_job_options["client_class"] = Sidekiq::TransactionAwareClient
42
+ Sidekiq::JobUtil::TRANSIENT_ATTRIBUTES << "client_class"
43
+ true
44
+ end
45
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "6.4.2"
4
+ VERSION = "6.5.12"
5
5
  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, {"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, {"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, {"content-type" => "application/json", "cache-control" => "private, no-store"}, [Sidekiq.dump_json(payload)]]
72
72
  end
73
73
 
74
74
  def initialize(env, block)
@@ -60,7 +60,23 @@ module Sidekiq
60
60
  erb(:dashboard)
61
61
  end
62
62
 
63
+ get "/metrics" do
64
+ q = Sidekiq::Metrics::Query.new
65
+ @query_result = q.top_jobs
66
+ erb(:metrics)
67
+ end
68
+
69
+ get "/metrics/:name" do
70
+ @name = route_params[:name]
71
+ q = Sidekiq::Metrics::Query.new
72
+ @query_result = q.for_job(@name)
73
+ erb(:metrics_for_job)
74
+ end
75
+
63
76
  get "/busy" do
77
+ @count = (params["count"] || 100).to_i
78
+ (@current_page, @total_size, @workset) = page_items(workset, params["page"], @count)
79
+
64
80
  erb(:busy)
65
81
  end
66
82
 
@@ -299,7 +315,7 @@ module Sidekiq
299
315
 
300
316
  def call(env)
301
317
  action = self.class.match(env)
302
- return [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found"]] unless action
318
+ return [404, {"content-type" => "text/plain", "x-cascade" => "pass"}, ["Not Found"]] unless action
303
319
 
304
320
  app = @klass
305
321
  resp = catch(:halt) do
@@ -316,10 +332,10 @@ module Sidekiq
316
332
  else
317
333
  # rendered content goes here
318
334
  headers = {
319
- "Content-Type" => "text/html",
320
- "Cache-Control" => "private, no-store",
321
- "Content-Language" => action.locale,
322
- "Content-Security-Policy" => CSP_HEADER
335
+ "content-type" => "text/html",
336
+ "cache-control" => "private, no-store",
337
+ "content-language" => action.locale,
338
+ "content-security-policy" => CSP_HEADER
323
339
  }
324
340
  # we'll let Rack calculate Content-Length for us.
325
341
  [200, headers, [resp]]
@@ -15,7 +15,7 @@ module Sidekiq
15
15
  # so extensions can be localized
16
16
  @strings[lang] ||= settings.locales.each_with_object({}) do |path, global|
17
17
  find_locale_files(lang).each do |file|
18
- strs = YAML.load(File.open(file))
18
+ strs = YAML.safe_load(File.open(file))
19
19
  global.merge!(strs[lang])
20
20
  end
21
21
  end
@@ -137,7 +137,7 @@ module Sidekiq
137
137
  end
138
138
 
139
139
  def sort_direction_label
140
- params[:direction] == "asc" ? "&uarr;" : "&darr;"
140
+ (params[:direction] == "asc") ? "&uarr;" : "&darr;"
141
141
  end
142
142
 
143
143
  def workset
@@ -148,6 +148,19 @@ module Sidekiq
148
148
  @processes ||= Sidekiq::ProcessSet.new
149
149
  end
150
150
 
151
+ # Sorts processes by hostname following the natural sort order
152
+ def sorted_processes
153
+ @sorted_processes ||= begin
154
+ return processes unless processes.all? { |p| p["hostname"] }
155
+
156
+ processes.to_a.sort_by do |process|
157
+ # Kudos to `shurikk` on StackOverflow
158
+ # https://stackoverflow.com/a/15170063/575547
159
+ process["hostname"].split(/(\d+)/).map { |a| /\d+/.match?(a) ? a.to_i : a }
160
+ end
161
+ end
162
+ end
163
+
151
164
  def stats
152
165
  @stats ||= Sidekiq::Stats.new
153
166
  end
@@ -175,7 +188,7 @@ module Sidekiq
175
188
  end
176
189
 
177
190
  def current_status
178
- workset.size == 0 ? "idle" : "active"
191
+ (workset.size == 0) ? "idle" : "active"
179
192
  end
180
193
 
181
194
  def relative_time(time)
@@ -208,7 +221,7 @@ module Sidekiq
208
221
  end
209
222
 
210
223
  def truncate(text, truncate_after_chars = 2000)
211
- truncate_after_chars && text.size > truncate_after_chars ? "#{text[0..truncate_after_chars]}..." : text
224
+ (truncate_after_chars && text.size > truncate_after_chars) ? "#{text[0..truncate_after_chars]}..." : text
212
225
  end
213
226
 
214
227
  def display_args(args, truncate_after_chars = 2000)
@@ -301,7 +314,7 @@ module Sidekiq
301
314
  end
302
315
 
303
316
  def environment_title_prefix
304
- environment = Sidekiq.options[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
317
+ environment = Sidekiq[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
305
318
 
306
319
  "[#{environment.upcase}] " unless environment == "production"
307
320
  end
data/lib/sidekiq/web.rb CHANGED
@@ -33,6 +33,10 @@ module Sidekiq
33
33
  "Dead" => "morgue"
34
34
  }
35
35
 
36
+ if ENV["SIDEKIQ_METRICS_BETA"] == "1"
37
+ DEFAULT_TABS["Metrics"] = "metrics"
38
+ end
39
+
36
40
  class << self
37
41
  def settings
38
42
  self
@@ -144,7 +148,7 @@ module Sidekiq
144
148
  m = middlewares
145
149
 
146
150
  rules = []
147
- rules = [[:all, {"Cache-Control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
151
+ rules = [[:all, {"cache-control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
148
152
 
149
153
  ::Rack::Builder.new do
150
154
  use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
@@ -257,7 +257,7 @@ module Sidekiq
257
257
  def at(interval)
258
258
  int = interval.to_f
259
259
  now = Time.now.to_f
260
- ts = (int < 1_000_000_000 ? now + int : int)
260
+ ts = ((int < 1_000_000_000) ? now + int : int)
261
261
  # Optimization to enqueue something now that is scheduled to go out now or in the past
262
262
  @opts["at"] = ts if ts > now
263
263
  self
@@ -324,7 +324,7 @@ module Sidekiq
324
324
  def perform_in(interval, *args)
325
325
  int = interval.to_f
326
326
  now = Time.now.to_f
327
- ts = (int < 1_000_000_000 ? now + int : int)
327
+ ts = ((int < 1_000_000_000) ? now + int : int)
328
328
 
329
329
  item = {"class" => self, "args" => args}
330
330
 
@@ -340,7 +340,7 @@ module Sidekiq
340
340
  # Legal options:
341
341
  #
342
342
  # queue - use a named queue for this Worker, default 'default'
343
- # retry - enable the RetryJobs middleware for this Worker, *true* to use the default
343
+ # retry - enable retries via JobRetry, *true* to use the default
344
344
  # or *Integer* count
345
345
  # backtrace - whether to save any error backtrace in the retry payload to display in web UI,
346
346
  # can be true, false or an integer number of lines to save, default *false*
@@ -348,6 +348,9 @@ module Sidekiq
348
348
  #
349
349
  # In practice, any option is allowed. This is the main mechanism to configure the
350
350
  # options for a specific job.
351
+ #
352
+ # These options will be saved into the serialized job when enqueued by
353
+ # the client.
351
354
  def sidekiq_options(opts = {})
352
355
  super
353
356
  end
@@ -359,7 +362,8 @@ module Sidekiq
359
362
 
360
363
  def build_client # :nodoc:
361
364
  pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
362
- Sidekiq::Client.new(pool)
365
+ client_class = get_sidekiq_options["client_class"] || Sidekiq::Client
366
+ client_class.new(pool)
363
367
  end
364
368
  end
365
369
  end