sidekiq 8.0.8 → 8.1.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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +42 -0
  3. data/README.md +15 -0
  4. data/bin/lint-herb +13 -0
  5. data/lib/generators/sidekiq/templates/job.rb.erb +1 -1
  6. data/lib/sidekiq/api.rb +28 -5
  7. data/lib/sidekiq/capsule.rb +4 -0
  8. data/lib/sidekiq/cli.rb +16 -4
  9. data/lib/sidekiq/config.rb +8 -2
  10. data/lib/sidekiq/fetch.rb +1 -0
  11. data/lib/sidekiq/job/iterable.rb +2 -2
  12. data/lib/sidekiq/job.rb +2 -0
  13. data/lib/sidekiq/job_logger.rb +4 -2
  14. data/lib/sidekiq/job_retry.rb +7 -3
  15. data/lib/sidekiq/launcher.rb +18 -9
  16. data/lib/sidekiq/middleware/i18n.rb +2 -0
  17. data/lib/sidekiq/monitor.rb +4 -8
  18. data/lib/sidekiq/profiler.rb +1 -0
  19. data/lib/sidekiq/redis_connection.rb +2 -2
  20. data/lib/sidekiq/ring_buffer.rb +1 -0
  21. data/lib/sidekiq/scheduled.rb +7 -5
  22. data/lib/sidekiq/version.rb +1 -1
  23. data/lib/sidekiq/web/action.rb +1 -1
  24. data/lib/sidekiq/web/application.rb +10 -0
  25. data/lib/sidekiq/web/config.rb +3 -6
  26. data/lib/sidekiq/web/helpers.rb +3 -11
  27. data/lib/sidekiq/web.rb +23 -4
  28. data/sidekiq.gemspec +5 -5
  29. data/web/assets/javascripts/application.js +17 -1
  30. data/web/assets/stylesheets/style.css +17 -2
  31. data/web/locales/ar.yml +1 -0
  32. data/web/locales/cs.yml +1 -0
  33. data/web/locales/da.yml +1 -0
  34. data/web/locales/de.yml +1 -0
  35. data/web/locales/el.yml +1 -0
  36. data/web/locales/en.yml +1 -0
  37. data/web/locales/es.yml +1 -0
  38. data/web/locales/fa.yml +1 -0
  39. data/web/locales/fr.yml +2 -1
  40. data/web/locales/gd.yml +1 -0
  41. data/web/locales/he.yml +1 -0
  42. data/web/locales/hi.yml +1 -0
  43. data/web/locales/it.yml +1 -0
  44. data/web/locales/ja.yml +1 -0
  45. data/web/locales/ko.yml +1 -0
  46. data/web/locales/lt.yml +1 -0
  47. data/web/locales/nb.yml +1 -0
  48. data/web/locales/nl.yml +1 -0
  49. data/web/locales/pl.yml +1 -0
  50. data/web/locales/pt-BR.yml +1 -0
  51. data/web/locales/pt.yml +1 -0
  52. data/web/locales/ru.yml +1 -0
  53. data/web/locales/sv.yml +1 -0
  54. data/web/locales/ta.yml +1 -0
  55. data/web/locales/tr.yml +1 -0
  56. data/web/locales/uk.yml +1 -0
  57. data/web/locales/ur.yml +1 -0
  58. data/web/locales/vi.yml +1 -0
  59. data/web/locales/zh-CN.yml +1 -0
  60. data/web/locales/zh-TW.yml +1 -0
  61. data/web/views/{_footer.erb → _footer.html.erb} +1 -1
  62. data/web/views/{_metrics_period_select.erb → _metrics_period_select.html.erb} +1 -1
  63. data/web/views/{_paging.erb → _paging.html.erb} +0 -1
  64. data/web/views/_poll_link.html.erb +4 -0
  65. data/web/views/{busy.erb → busy.html.erb} +4 -8
  66. data/web/views/{dashboard.erb → dashboard.html.erb} +3 -3
  67. data/web/views/{dead.erb → dead.html.erb} +3 -3
  68. data/web/views/filtering.html.erb +6 -0
  69. data/web/views/{layout.erb → layout.html.erb} +7 -7
  70. data/web/views/{metrics.erb → metrics.html.erb} +9 -8
  71. data/web/views/{morgue.erb → morgue.html.erb} +8 -4
  72. data/web/views/{queue.erb → queue.html.erb} +2 -2
  73. data/web/views/{queues.erb → queues.html.erb} +4 -4
  74. data/web/views/{retries.erb → retries.html.erb} +9 -5
  75. data/web/views/{retry.erb → retry.html.erb} +2 -2
  76. data/web/views/{scheduled.erb → scheduled.html.erb} +8 -4
  77. data/web/views/{scheduled_job_info.erb → scheduled_job_info.html.erb} +2 -2
  78. metadata +34 -34
  79. data/lib/sidekiq/web/csrf_protection.rb +0 -183
  80. data/web/views/_poll_link.erb +0 -4
  81. data/web/views/filtering.erb +0 -6
  82. /data/web/views/{_job_info.erb → _job_info.html.erb} +0 -0
  83. /data/web/views/{_nav.erb → _nav.html.erb} +0 -0
  84. /data/web/views/{_summary.erb → _summary.html.erb} +0 -0
  85. /data/web/views/{metrics_for_job.erb → metrics_for_job.html.erb} +0 -0
  86. /data/web/views/{profiles.erb → profiles.html.erb} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a1e8c90888b4135f21e4dfef66fd9ad9ac8c72ab267599a8452f4bd172234a2
4
- data.tar.gz: fa83e70c818fc3b25441e946c1a093a2d9a5f4515ee2bd88522cbafcb8097973
3
+ metadata.gz: 63658318932ac6b7045211590b07f84ed9e1e6398f908ace790a0df20d5ac1f0
4
+ data.tar.gz: 0c3f5e69ada7529bdac7392acb1c56ef2a19b8ed13210be8c36aac19858c6df4
5
5
  SHA512:
6
- metadata.gz: 0ea8ac9585538723d941af6ce9150933669705cf1e3459dcea8989d073d234c4e233e74314a5a682e3111e1290a69671051e847b5ee97970c1edd08293e14bbc
7
- data.tar.gz: 0b253cd035132f786613fbb97440f5d16f48717aedaa7f50865e0e261ce0fc17b64885bea3cdeb5aabe72258c06ed013891d83655bdcf842fc1a261d1b0aa985
6
+ metadata.gz: 9a5e95a2faccbbf42f8e651c4882c9495ad72b0500bb8fe77fa2a93fb4aa6f8cb96fc820621cd1910fb4b00accf35d75a99d548b3cc8ccdd41a18b5620a9ad73
7
+ data.tar.gz: 1bc66490ee07c548c71f680fb280e876efa7eeb8c6f234277df73211ce05c5ae29f5c9bc13064972da945568d1df6634252a6c22a7c5298bdd436b7266246c53
data/Changes.md CHANGED
@@ -2,6 +2,48 @@
2
2
 
3
3
  [Sidekiq Changes](https://github.com/sidekiq/sidekiq/blob/main/Changes.md) | [Sidekiq Pro Changes](https://github.com/sidekiq/sidekiq/blob/main/Pro-Changes.md) | [Sidekiq Enterprise Changes](https://github.com/sidekiq/sidekiq/blob/main/Ent-Changes.md)
4
4
 
5
+ 8.1.0
6
+ ----------
7
+
8
+ - `retry_for` and `retry` are now mutually exclusive [#6878, Saidbek]
9
+ - `perform_inline` now enforces `strict_args!` [#6718, Saidbek]
10
+ - Integrate Herb linting for ERB templates [#6760, Saidbek]
11
+ - Remove CSRF code, use `Sec-Fetch-Site` header [#6874, deve1212]
12
+ - Allow custom Web UI `assets_path` for CDN purposes [#6865, stanhu]
13
+ - Upgrade to connection_pool 3.0
14
+ - Allow idle connection reaping after N seconds.
15
+ You can activate this **beta** feature like below.
16
+ Feedback requested: is this feature stable and useful for you in production?
17
+ This feature may or may not be enabled by default in Sidekiq 9.0.
18
+ ```ruby
19
+ Sidekiq.configure_server do |cfg|
20
+ cfg.reap_idle_redis_connections(60)
21
+ end
22
+ ```
23
+
24
+ 8.0.10
25
+ ----------
26
+
27
+ - Add confirm dialog for Delete All buttons in Web UI [#6853]
28
+ - Adjust scheduler to run closer to poll average [#6866]
29
+ - Forward compatibility changes for connection_pool 3.0.0
30
+ - Backwards compatibility fix for <8.0.9 process data in Redis [#6870]
31
+ - Backtrace dump can now be triggered with the INFO signal, since Puma uses the
32
+ same signal [#6857]
33
+
34
+ 8.0.9
35
+ ----------
36
+
37
+ - Implement idle Redis connection reaping, will be activated in 8.1 [#6663]
38
+ - Updated `Sidekiq::Process` API to provide capsule data. The `queues` and `weights`
39
+ data will be removed from Redis in Sidekiq 8.1, as this data can now be found in the
40
+ `capsules` element. [#6295]
41
+ - Restore bulk action buttons on Scheduled, Retry and Dead tabs [#6833, deve1212]
42
+ - Support logging additional job attributes [#6846, bschrag620]
43
+ - Fix display of long job args [#6836]
44
+ - Create development lifecycle (`docs/sdlc.md`) and security (`docs/SECURITY.md`) policy
45
+ documentation for Sidekiq's current workflows
46
+
5
47
  8.0.8
6
48
  ----------
7
49
 
data/README.md CHANGED
@@ -97,6 +97,21 @@ Contributing
97
97
 
98
98
  See [the contributing guidelines](https://github.com/sidekiq/sidekiq/blob/main/.github/contributing.md).
99
99
 
100
+ ### ERB Linting with HERB
101
+
102
+ This project uses [HERB](https://herb-tools.dev/) for ERB file linting and formatting. All ERB files have been renamed to use the `.html.erb` extension for better tooling support.
103
+
104
+ **Local Development:**
105
+ ```bash
106
+ # Run HERB linting
107
+ bundle exec rake lint:herb
108
+ # or
109
+ bin/lint-herb
110
+ ```
111
+
112
+ **CI Integration:**
113
+ HERB linting is automatically run in CI to ensure all ERB files are properly formatted and free of parse errors.
114
+
100
115
  License
101
116
  -----------------
102
117
 
data/bin/lint-herb ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # HERB Linting Script
5
+ # Run this script to lint all ERB files in the project
6
+ # Usage: bin/lint-herb
7
+
8
+ require "bundler/setup"
9
+
10
+ puts "🔍 Running HERB linting on ERB files..."
11
+ puts
12
+
13
+ exec("bundle exec herb analyze web/views -n --no-log-file")
@@ -6,4 +6,4 @@ class <%= class_name %>Job
6
6
  # Do something
7
7
  end
8
8
  end
9
- <% end -%>
9
+ <% end -%>
data/lib/sidekiq/api.rb CHANGED
@@ -1059,9 +1059,9 @@ module Sidekiq
1059
1059
  # 'started_at' => <process start time>,
1060
1060
  # 'pid' => 12345,
1061
1061
  # 'tag' => 'myapp'
1062
- # 'concurrency' => 25,
1063
- # 'queues' => ['default', 'low'],
1064
- # 'busy' => 10,
1062
+ # 'concurrency' => 5,
1063
+ # 'capsules' => {"default" => {"mode" => "weighted", "concurrency" => 5, "weights" => {"default" => 2, "low" => 1}}},
1064
+ # 'busy' => 3,
1065
1065
  # 'beat' => <last heartbeat>,
1066
1066
  # 'identity' => <unique string identifying the process>,
1067
1067
  # 'embedded' => true,
@@ -1089,12 +1089,35 @@ module Sidekiq
1089
1089
  self["identity"]
1090
1090
  end
1091
1091
 
1092
+ # deprecated, use capsules below
1092
1093
  def queues
1093
- self["queues"]
1094
+ # Backwards compatibility with <8.0.8
1095
+ if !self["capsules"]
1096
+ self["queues"]
1097
+ else
1098
+ capsules.values.flat_map { |x| x["weights"].keys }.uniq
1099
+ end
1094
1100
  end
1095
1101
 
1102
+ # deprecated, use capsules below
1096
1103
  def weights
1097
- self["weights"]
1104
+ # Backwards compatibility with <8.0.8
1105
+ if !self["capsules"]
1106
+ self["weights"]
1107
+ else
1108
+ hash = {}
1109
+ capsules.values.each do |cap|
1110
+ # Note: will lose data if two capsules are processing the same named queue
1111
+ cap["weights"].each_pair do |queue, weight|
1112
+ hash[queue] = weight
1113
+ end
1114
+ end
1115
+ hash
1116
+ end
1117
+ end
1118
+
1119
+ def capsules
1120
+ self["capsules"]
1098
1121
  end
1099
1122
 
1100
1123
  def version
@@ -38,6 +38,10 @@ module Sidekiq
38
38
  @mode = :strict
39
39
  end
40
40
 
41
+ def to_h
42
+ {concurrency: concurrency, mode: mode, weights: weights}
43
+ end
44
+
41
45
  def fetcher
42
46
  @fetcher ||= begin
43
47
  instance = (config[:fetch_class] || Sidekiq::BasicFetch).new(self)
data/lib/sidekiq/cli.rb CHANGED
@@ -49,7 +49,7 @@ module Sidekiq # :nodoc:
49
49
  logger.info "Booted Rails #{::Rails.version} application in #{environment} environment" if rails_app?
50
50
 
51
51
  self_read, self_write = IO.pipe
52
- sigs = %w[INT TERM TTIN TSTP]
52
+ sigs = %w[INT TERM INFO TTIN TSTP]
53
53
  # USR1 and USR2 don't work on the JVM
54
54
  sigs << "USR2" if Sidekiq.pro? && !jruby?
55
55
  sigs.each do |sig|
@@ -201,7 +201,19 @@ module Sidekiq # :nodoc:
201
201
  cli.logger.info "Received TSTP, no longer accepting new work"
202
202
  cli.launcher.quiet
203
203
  },
204
+ # deprecated, use INFO
204
205
  "TTIN" => ->(cli) {
206
+ cli.logger.error { "DEPRECATED: Please use the INFO signal for backtraces, support for TTIN will be removed in Sidekiq 9.0." }
207
+ Thread.list.each do |thread|
208
+ cli.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
209
+ if thread.backtrace
210
+ cli.logger.warn thread.backtrace.join("\n")
211
+ else
212
+ cli.logger.warn "<no backtrace available>"
213
+ end
214
+ end
215
+ },
216
+ "INFO" => ->(cli) {
205
217
  Thread.list.each do |thread|
206
218
  cli.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
207
219
  if thread.backtrace
@@ -212,12 +224,12 @@ module Sidekiq # :nodoc:
212
224
  end
213
225
  }
214
226
  }
215
- UNHANDLED_SIGNAL_HANDLER = ->(cli) { cli.logger.info "No signal handler registered, ignoring" }
216
- SIGNAL_HANDLERS.default = UNHANDLED_SIGNAL_HANDLER
217
227
 
218
228
  def handle_signal(sig)
219
229
  logger.debug "Got #{sig} signal"
220
- SIGNAL_HANDLERS[sig].call(self)
230
+ hndlr = SIGNAL_HANDLERS[sig]
231
+ hndlr ? hndlr.call(self) :
232
+ logger.warn("No #{sig} signal handler registered, ignoring")
221
233
  end
222
234
 
223
235
  private
@@ -35,7 +35,9 @@ module Sidekiq
35
35
  dead_max_jobs: 10_000,
36
36
  dead_timeout_in_seconds: 180 * 24 * 60 * 60, # 6 months
37
37
  reloader: proc { |&block| block.call },
38
- backtrace_cleaner: ->(backtrace) { backtrace }
38
+ backtrace_cleaner: ->(backtrace) { backtrace },
39
+ logged_job_attributes: ["bid", "tags"],
40
+ redis_idle_timeout: nil
39
41
  }
40
42
 
41
43
  ERROR_HANDLER = ->(ex, ctx, cfg = Sidekiq.default_configuration) {
@@ -144,11 +146,15 @@ module Sidekiq
144
146
  @redis_config = @redis_config.merge(hash)
145
147
  end
146
148
 
149
+ def reap_idle_redis_connections(timeout = 60)
150
+ self[:redis_idle_timeout] = timeout
151
+ end
152
+
147
153
  def redis_pool
148
154
  Thread.current[:sidekiq_redis_pool] || Thread.current[:sidekiq_capsule]&.redis_pool || local_redis_pool
149
155
  end
150
156
 
151
- private def local_redis_pool
157
+ def local_redis_pool
152
158
  # this is our internal client/housekeeping pool. each capsule has its
153
159
  # own pool for executing threads.
154
160
  @redis ||= new_redis_pool(10, "internal")
data/lib/sidekiq/fetch.rb CHANGED
@@ -7,6 +7,7 @@ require "sidekiq/capsule"
7
7
  module Sidekiq # :nodoc:
8
8
  class BasicFetch
9
9
  include Sidekiq::Component
10
+
10
11
  # We want the fetch operation to timeout every few seconds so the thread
11
12
  # can check if the process is shutting down.
12
13
  TIMEOUT = 2
@@ -55,7 +55,7 @@ module Sidekiq
55
55
  def cancel!
56
56
  return @_cancelled if cancelled?
57
57
 
58
- key = "it-#{jid}"
58
+ key = iteration_key
59
59
  _, result, _ = Sidekiq.redis do |c|
60
60
  c.pipelined do |p|
61
61
  p.hsetnx(key, "cancelled", Time.now.to_i)
@@ -177,7 +177,7 @@ module Sidekiq
177
177
  private
178
178
 
179
179
  def is_cancelled?
180
- @_cancelled = Sidekiq.redis { |c| c.hget("it-#{jid}", "cancelled") }
180
+ @_cancelled = Sidekiq.redis { |c| c.hget(iteration_key, "cancelled") }
181
181
  end
182
182
 
183
183
  def fetch_previous_iteration_state
data/lib/sidekiq/job.rb CHANGED
@@ -226,6 +226,8 @@ module Sidekiq
226
226
  end
227
227
  return nil unless result
228
228
 
229
+ verify_json(item)
230
+
229
231
  # round-trip the payload via JSON
230
232
  msg = Sidekiq.load_json(Sidekiq.dump_json(item))
231
233
 
@@ -29,8 +29,10 @@ module Sidekiq
29
29
  jid: job_hash["jid"],
30
30
  class: job_hash["wrapped"] || job_hash["class"]
31
31
  }
32
- h[:bid] = job_hash["bid"] if job_hash.has_key?("bid")
33
- h[:tags] = job_hash["tags"] if job_hash.has_key?("tags")
32
+
33
+ @config[:logged_job_attributes].each do |attr|
34
+ h[attr.to_sym] = job_hash[attr] if job_hash.has_key?(attr)
35
+ end
34
36
 
35
37
  Thread.current[:sidekiq_context] = h
36
38
  level = job_hash["log_level"]
@@ -178,10 +178,14 @@ module Sidekiq
178
178
  msg["error_backtrace"] = compress_backtrace(lines)
179
179
  end
180
180
 
181
- return retries_exhausted(jobinst, msg, exception) if count >= max_retry_attempts
182
-
181
+ # retry_for and retry are mutually exclusive - if retry_for is set,
182
+ # we exclusively use duration-based retry logic and ignore count-based logic
183
183
  rf = msg["retry_for"]
184
- return retries_exhausted(jobinst, msg, exception) if rf && (time_for(msg["failed_at"]) + rf) < Time.now
184
+ if rf
185
+ return retries_exhausted(jobinst, msg, exception) if (time_for(msg["failed_at"]) + rf) < Time.now
186
+ elsif count >= max_retry_attempts
187
+ return retries_exhausted(jobinst, msg, exception)
188
+ end
185
189
 
186
190
  strategy, delay = delay_for(jobinst, count, exception, msg)
187
191
  case strategy
@@ -142,6 +142,12 @@ module Sidekiq
142
142
  key = identity
143
143
  fails = procd = 0
144
144
 
145
+ idle_timeout = config[:redis_idle_timeout]
146
+ if idle_timeout
147
+ config.capsules.each_value { |cap| cap.local_redis_pool.reap(idle_seconds: idle_timeout, &:close) }
148
+ config.local_redis_pool.reap(idle_seconds: idle_timeout, &:close)
149
+ end
150
+
145
151
  begin
146
152
  flush_stats
147
153
 
@@ -214,11 +220,11 @@ module Sidekiq
214
220
  # Log a warning if it's a disaster.
215
221
  if RTT_READINGS.all? { |x| x > RTT_WARNING_LEVEL }
216
222
  logger.warn <<~EOM
217
- Your Redis network connection is performing extremely poorly.
223
+ Your Redis network connection appears to be performing poorly.
218
224
  Last RTT readings were #{RTT_READINGS.buffer.inspect}, ideally these should be < 1000.
219
- Ensure Redis is running in the same AZ or datacenter as Sidekiq.
220
- If these values are close to 100,000, that means your Sidekiq process may be
221
- CPU-saturated; reduce your concurrency and/or see https://github.com/sidekiq/sidekiq/discussions/5039
225
+ Ensure Redis is running in the same AZ or datacenter as Sidekiq and that
226
+ your Sidekiq process is not CPU-saturated; reduce your concurrency and/or
227
+ see https://github.com/sidekiq/sidekiq/discussions/5039
222
228
  EOM
223
229
  RTT_READINGS.reset
224
230
  end
@@ -252,8 +258,15 @@ module Sidekiq
252
258
  "pid" => ::Process.pid,
253
259
  "tag" => @config[:tag] || "",
254
260
  "concurrency" => @config.total_concurrency,
261
+ "capsules" => @config.capsules.each_with_object({}) { |(name, cap), memo|
262
+ memo[name] = cap.to_h
263
+ },
264
+ #####
265
+ # TODO deprecated, remove in 9.0
266
+ # This data is now found in the `capsules` element above
255
267
  "queues" => @config.capsules.values.flat_map { |cap| cap.queues }.uniq,
256
- "weights" => to_weights,
268
+ "weights" => @config.capsules.values.map(&:weights),
269
+ #####
257
270
  "labels" => @config[:labels].to_a,
258
271
  "identity" => identity,
259
272
  "version" => Sidekiq::VERSION,
@@ -261,10 +274,6 @@ module Sidekiq
261
274
  }
262
275
  end
263
276
 
264
- def to_weights
265
- @config.capsules.values.map(&:weights)
266
- end
267
-
268
277
  def to_json
269
278
  # this data changes infrequently so dump it to a string
270
279
  # now so we don't need to dump it every heartbeat.
@@ -11,6 +11,7 @@ module Sidekiq::Middleware::I18n
11
11
  # to be sent to Sidekiq.
12
12
  class Client
13
13
  include Sidekiq::ClientMiddleware
14
+
14
15
  def call(_jobclass, job, _queue, _redis)
15
16
  job["locale"] ||= I18n.locale
16
17
  yield
@@ -20,6 +21,7 @@ module Sidekiq::Middleware::I18n
20
21
  # Pull the msg locale out and set the current thread to use it.
21
22
  class Server
22
23
  include Sidekiq::ServerMiddleware
24
+
23
25
  def call(_jobclass, job, _queue, &block)
24
26
  I18n.with_locale(job.fetch("locale", I18n.default_locale), &block)
25
27
  end
@@ -49,14 +49,10 @@ class Sidekiq::Monitor
49
49
  puts "---- Processes (#{process_set.size}) ----"
50
50
  process_set.each_with_index do |process, index|
51
51
  # Keep compatibility with legacy versions since we don't want to break sidekiqmon during rolling upgrades or downgrades.
52
- #
53
- # Before:
54
- # ["default", "critical"]
55
- #
56
- # After:
57
- # {"default" => 1, "critical" => 10}
58
52
  queues =
59
- if process["weights"]
53
+ if process["capsules"] # 8.0.6+
54
+ process["capsules"].values.map { |x| x["weights"].keys.join(", ") }
55
+ elsif process["weights"]
60
56
  process["weights"].sort_by { |queue| queue[0] }.map { |capsule| capsule.map { |name, weight| (weight > 0) ? "#{name}: #{weight}" : name }.join(", ") }
61
57
  else
62
58
  process["queues"].sort
@@ -105,7 +101,7 @@ class Sidekiq::Monitor
105
101
  out << line
106
102
  line = " " * pad
107
103
  end
108
- line << value + ", "
104
+ line << value + "; "
109
105
  end
110
106
  out << line[0..-3]
111
107
  out.join("\n")
@@ -11,6 +11,7 @@ module Sidekiq
11
11
  }
12
12
 
13
13
  include Sidekiq::Component
14
+
14
15
  def initialize(config)
15
16
  @config = config
16
17
  @vernier_output_dir = ENV.fetch("VERNIER_OUTPUT_DIR") { Dir.tmpdir }
@@ -23,7 +23,7 @@ module Sidekiq
23
23
 
24
24
  size = symbolized_options.delete(:size) || 5
25
25
  pool_timeout = symbolized_options.delete(:pool_timeout) || 1
26
- pool_name = symbolized_options.delete(:pool_name)
26
+ symbolized_options.delete(:pool_name)
27
27
 
28
28
  # Default timeout in redis-client is 1 second, which can be too aggressive
29
29
  # if the Sidekiq process is CPU-bound. With 10-15 threads and a thread quantum of 100ms,
@@ -33,7 +33,7 @@ module Sidekiq
33
33
  symbolized_options[:timeout] ||= 3
34
34
 
35
35
  redis_config = Sidekiq::RedisClientAdapter.new(symbolized_options)
36
- ConnectionPool.new(timeout: pool_timeout, size: size, name: pool_name) do
36
+ ConnectionPool.new(timeout: pool_timeout, size: size) do
37
37
  redis_config.new_client
38
38
  end
39
39
  end
@@ -6,6 +6,7 @@ module Sidekiq
6
6
  class RingBuffer
7
7
  include Enumerable
8
8
  extend Forwardable
9
+
9
10
  def_delegators :@buf, :[], :each, :size
10
11
 
11
12
  def initialize(size, default = 0)
@@ -72,6 +72,7 @@ module Sidekiq
72
72
  include Sidekiq::Component
73
73
 
74
74
  INITIAL_WAIT = 10
75
+ attr_accessor :rnd
75
76
 
76
77
  def initialize(config)
77
78
  @config = config
@@ -80,6 +81,7 @@ module Sidekiq
80
81
  @done = false
81
82
  @thread = nil
82
83
  @count_calls = 0
84
+ @rnd = Random.new
83
85
  end
84
86
 
85
87
  # Shut down this instance, will pause until the thread is dead.
@@ -115,9 +117,9 @@ module Sidekiq
115
117
  private
116
118
 
117
119
  def wait
118
- @sleeper.pop(random_poll_interval)
120
+ @sleeper.pop(timeout: random_poll_interval)
119
121
  rescue Timeout::Error
120
- # expected
122
+ # TODO move to exception: false
121
123
  rescue => ex
122
124
  # if poll_interval_average hasn't been calculated yet, we can
123
125
  # raise an error trying to reach Redis.
@@ -151,11 +153,11 @@ module Sidekiq
151
153
 
152
154
  if count < 10
153
155
  # For small clusters, calculate a random interval that is ±50% the desired average.
154
- interval * rand + interval.to_f / 2
156
+ interval * @rnd.rand + interval.to_f / 2
155
157
  else
156
158
  # With 10+ processes, we should have enough randomness to get decent polling
157
159
  # across the entire timespan
158
- interval * rand
160
+ interval * @rnd.rand * 2
159
161
  end
160
162
  end
161
163
 
@@ -223,7 +225,7 @@ module Sidekiq
223
225
  total += INITIAL_WAIT unless @config[:poll_interval_average]
224
226
  total += (5 * rand)
225
227
 
226
- @sleeper.pop(total)
228
+ @sleeper.pop(timeout: total)
227
229
  rescue Timeout::Error
228
230
  ensure
229
231
  # periodically clean out the `processes` set in Redis which can collect
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "8.0.8"
4
+ VERSION = "8.1.0"
5
5
  MAJOR = 8
6
6
 
7
7
  def self.gem_version
@@ -117,7 +117,7 @@ module Sidekiq
117
117
  if content.is_a? Symbol
118
118
  unless respond_to?(:"_erb_#{content}")
119
119
  views = options[:views] || Web.views
120
- filename = "#{views}/#{content}.erb"
120
+ filename = "#{views}/#{content}.html.erb"
121
121
  src = ERB.new(File.read(filename)).src
122
122
 
123
123
  # Need to use lineno less by 1 because erb generates a
@@ -318,6 +318,16 @@ module Sidekiq
318
318
  redirect_with_query("#{root_path}scheduled")
319
319
  end
320
320
 
321
+ post "/scheduled/all/delete" do
322
+ Sidekiq::ScheduledSet.new.clear
323
+ redirect "#{root_path}scheduled"
324
+ end
325
+
326
+ post "/scheduled/all/add_to_queue" do
327
+ Sidekiq::ScheduledSet.new.each(&:add_to_queue)
328
+ redirect "#{root_path}scheduled"
329
+ end
330
+
321
331
  get "/dashboard/stats" do
322
332
  redirect "#{root_path}stats"
323
333
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "sidekiq/web/csrf_protection"
4
-
5
3
  module Sidekiq
6
4
  class Web
7
5
  ##
@@ -24,10 +22,7 @@ module Sidekiq
24
22
  # and very difficult for us to vendor or provide ourselves. If you are worried
25
23
  # about data security and wish to self-host, you can change these URLs.
26
24
  profile_view_url: "https://profiler.firefox.com/public/%s",
27
- profile_store_url: "https://api.profiler.firefox.com/compressed-store",
28
- # Will be false in Sidekiq 9.0.
29
- # CSRF is unnecessary if you are using SameSite=(Strict|Lax) cookies.
30
- csrf: true
25
+ profile_store_url: "https://api.profiler.firefox.com/compressed-store"
31
26
  }
32
27
 
33
28
  ##
@@ -54,11 +49,13 @@ module Sidekiq
54
49
 
55
50
  # Adds the "Back to App" link in the header
56
51
  attr_accessor :app_url
52
+ attr_accessor :assets_path
57
53
 
58
54
  def initialize
59
55
  @options = OPTIONS.dup
60
56
  @locales = LOCALES
61
57
  @views = VIEWS
58
+ @assets_path = ASSETS
62
59
  @tabs = DEFAULT_TABS.dup
63
60
  @middlewares = []
64
61
  @custom_job_info_rows = []
@@ -122,8 +122,8 @@ module Sidekiq
122
122
  resultset
123
123
  end
124
124
 
125
- def filtering(which)
126
- erb(:filtering, locals: {which: which})
125
+ def filtering(which, placeholder_key: "AnyJobContent", label_key: "Filter")
126
+ erb(:filtering, locals: {which:, placeholder_key:, label_key:})
127
127
  end
128
128
 
129
129
  def filter_link(jid, within = "retries")
@@ -256,14 +256,6 @@ module Sidekiq
256
256
  end
257
257
  end
258
258
 
259
- def busy_weights(capsule_weights)
260
- # backwards compat with 7.0.0, remove in 7.1
261
- cw = [capsule_weights].flatten
262
- cw.map { |hash|
263
- hash.map { |name, weight| (weight > 0) ? +name << ": " << weight.to_s : name }.join(", ")
264
- }.join("; ")
265
- end
266
-
267
259
  def stats
268
260
  @stats ||= Sidekiq::Stats.new
269
261
  end
@@ -337,7 +329,7 @@ module Sidekiq
337
329
  end
338
330
 
339
331
  def csrf_tag
340
- "<input type='hidden' name='authenticity_token' value='#{env[:csrf_token]}'/>"
332
+ ""
341
333
  end
342
334
 
343
335
  def csp_nonce