sidekiq 4.2.10 → 7.3.2

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 (158) hide show
  1. checksums.yaml +5 -5
  2. data/Changes.md +859 -7
  3. data/LICENSE.txt +9 -0
  4. data/README.md +49 -50
  5. data/bin/multi_queue_bench +271 -0
  6. data/bin/sidekiq +22 -3
  7. data/bin/sidekiqload +212 -119
  8. data/bin/sidekiqmon +11 -0
  9. data/lib/generators/sidekiq/job_generator.rb +59 -0
  10. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  11. data/lib/generators/sidekiq/templates/job_spec.rb.erb +6 -0
  12. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  13. data/lib/sidekiq/api.rb +680 -315
  14. data/lib/sidekiq/capsule.rb +132 -0
  15. data/lib/sidekiq/cli.rb +268 -248
  16. data/lib/sidekiq/client.rb +136 -101
  17. data/lib/sidekiq/component.rb +68 -0
  18. data/lib/sidekiq/config.rb +293 -0
  19. data/lib/sidekiq/deploy.rb +64 -0
  20. data/lib/sidekiq/embedded.rb +63 -0
  21. data/lib/sidekiq/fetch.rb +49 -42
  22. data/lib/sidekiq/iterable_job.rb +55 -0
  23. data/lib/sidekiq/job/interrupt_handler.rb +24 -0
  24. data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
  25. data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
  26. data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
  27. data/lib/sidekiq/job/iterable.rb +231 -0
  28. data/lib/sidekiq/job.rb +385 -0
  29. data/lib/sidekiq/job_logger.rb +62 -0
  30. data/lib/sidekiq/job_retry.rb +305 -0
  31. data/lib/sidekiq/job_util.rb +109 -0
  32. data/lib/sidekiq/launcher.rb +208 -108
  33. data/lib/sidekiq/logger.rb +131 -0
  34. data/lib/sidekiq/manager.rb +43 -47
  35. data/lib/sidekiq/metrics/query.rb +158 -0
  36. data/lib/sidekiq/metrics/shared.rb +97 -0
  37. data/lib/sidekiq/metrics/tracking.rb +148 -0
  38. data/lib/sidekiq/middleware/chain.rb +113 -56
  39. data/lib/sidekiq/middleware/current_attributes.rb +113 -0
  40. data/lib/sidekiq/middleware/i18n.rb +7 -7
  41. data/lib/sidekiq/middleware/modules.rb +23 -0
  42. data/lib/sidekiq/monitor.rb +147 -0
  43. data/lib/sidekiq/paginator.rb +28 -16
  44. data/lib/sidekiq/processor.rb +188 -98
  45. data/lib/sidekiq/rails.rb +46 -97
  46. data/lib/sidekiq/redis_client_adapter.rb +114 -0
  47. data/lib/sidekiq/redis_connection.rb +71 -73
  48. data/lib/sidekiq/ring_buffer.rb +31 -0
  49. data/lib/sidekiq/scheduled.rb +140 -51
  50. data/lib/sidekiq/sd_notify.rb +149 -0
  51. data/lib/sidekiq/systemd.rb +26 -0
  52. data/lib/sidekiq/testing/inline.rb +6 -5
  53. data/lib/sidekiq/testing.rb +95 -85
  54. data/lib/sidekiq/transaction_aware_client.rb +51 -0
  55. data/lib/sidekiq/version.rb +3 -1
  56. data/lib/sidekiq/web/action.rb +22 -16
  57. data/lib/sidekiq/web/application.rb +230 -86
  58. data/lib/sidekiq/web/csrf_protection.rb +183 -0
  59. data/lib/sidekiq/web/helpers.rb +241 -104
  60. data/lib/sidekiq/web/router.rb +23 -19
  61. data/lib/sidekiq/web.rb +118 -110
  62. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  63. data/lib/sidekiq.rb +96 -185
  64. data/sidekiq.gemspec +26 -27
  65. data/web/assets/images/apple-touch-icon.png +0 -0
  66. data/web/assets/javascripts/application.js +157 -61
  67. data/web/assets/javascripts/base-charts.js +106 -0
  68. data/web/assets/javascripts/chart.min.js +13 -0
  69. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  70. data/web/assets/javascripts/dashboard-charts.js +192 -0
  71. data/web/assets/javascripts/dashboard.js +37 -280
  72. data/web/assets/javascripts/metrics.js +298 -0
  73. data/web/assets/stylesheets/application-dark.css +147 -0
  74. data/web/assets/stylesheets/application-rtl.css +163 -0
  75. data/web/assets/stylesheets/application.css +173 -198
  76. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  77. data/web/assets/stylesheets/bootstrap.css +2 -2
  78. data/web/locales/ar.yml +87 -0
  79. data/web/locales/cs.yml +62 -62
  80. data/web/locales/da.yml +60 -53
  81. data/web/locales/de.yml +65 -53
  82. data/web/locales/el.yml +43 -24
  83. data/web/locales/en.yml +86 -64
  84. data/web/locales/es.yml +70 -53
  85. data/web/locales/fa.yml +65 -64
  86. data/web/locales/fr.yml +83 -62
  87. data/web/locales/gd.yml +99 -0
  88. data/web/locales/he.yml +80 -0
  89. data/web/locales/hi.yml +59 -59
  90. data/web/locales/it.yml +53 -53
  91. data/web/locales/ja.yml +75 -62
  92. data/web/locales/ko.yml +52 -52
  93. data/web/locales/lt.yml +83 -0
  94. data/web/locales/nb.yml +61 -61
  95. data/web/locales/nl.yml +52 -52
  96. data/web/locales/pl.yml +45 -45
  97. data/web/locales/pt-br.yml +83 -55
  98. data/web/locales/pt.yml +51 -51
  99. data/web/locales/ru.yml +68 -63
  100. data/web/locales/sv.yml +53 -53
  101. data/web/locales/ta.yml +60 -60
  102. data/web/locales/tr.yml +101 -0
  103. data/web/locales/uk.yml +62 -61
  104. data/web/locales/ur.yml +80 -0
  105. data/web/locales/vi.yml +83 -0
  106. data/web/locales/zh-cn.yml +43 -16
  107. data/web/locales/zh-tw.yml +42 -8
  108. data/web/views/_footer.erb +21 -3
  109. data/web/views/_job_info.erb +21 -4
  110. data/web/views/_metrics_period_select.erb +12 -0
  111. data/web/views/_nav.erb +5 -19
  112. data/web/views/_paging.erb +3 -1
  113. data/web/views/_poll_link.erb +3 -6
  114. data/web/views/_summary.erb +7 -7
  115. data/web/views/busy.erb +85 -31
  116. data/web/views/dashboard.erb +50 -20
  117. data/web/views/dead.erb +3 -3
  118. data/web/views/filtering.erb +7 -0
  119. data/web/views/layout.erb +17 -6
  120. data/web/views/metrics.erb +91 -0
  121. data/web/views/metrics_for_job.erb +59 -0
  122. data/web/views/morgue.erb +14 -15
  123. data/web/views/queue.erb +34 -24
  124. data/web/views/queues.erb +20 -4
  125. data/web/views/retries.erb +19 -16
  126. data/web/views/retry.erb +3 -3
  127. data/web/views/scheduled.erb +19 -17
  128. metadata +91 -198
  129. data/.github/contributing.md +0 -32
  130. data/.github/issue_template.md +0 -9
  131. data/.gitignore +0 -12
  132. data/.travis.yml +0 -18
  133. data/3.0-Upgrade.md +0 -70
  134. data/4.0-Upgrade.md +0 -53
  135. data/COMM-LICENSE +0 -95
  136. data/Ent-Changes.md +0 -173
  137. data/Gemfile +0 -29
  138. data/LICENSE +0 -9
  139. data/Pro-2.0-Upgrade.md +0 -138
  140. data/Pro-3.0-Upgrade.md +0 -44
  141. data/Pro-Changes.md +0 -628
  142. data/Rakefile +0 -12
  143. data/bin/sidekiqctl +0 -99
  144. data/code_of_conduct.md +0 -50
  145. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +0 -6
  146. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  147. data/lib/sidekiq/core_ext.rb +0 -119
  148. data/lib/sidekiq/exception_handler.rb +0 -31
  149. data/lib/sidekiq/extensions/action_mailer.rb +0 -57
  150. data/lib/sidekiq/extensions/active_record.rb +0 -40
  151. data/lib/sidekiq/extensions/class_methods.rb +0 -40
  152. data/lib/sidekiq/extensions/generic_proxy.rb +0 -25
  153. data/lib/sidekiq/logging.rb +0 -106
  154. data/lib/sidekiq/middleware/server/active_record.rb +0 -13
  155. data/lib/sidekiq/middleware/server/logging.rb +0 -31
  156. data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
  157. data/lib/sidekiq/util.rb +0 -63
  158. data/lib/sidekiq/worker.rb +0 -121
@@ -1,43 +1,124 @@
1
1
  # frozen_string_literal: true
2
- require 'uri'
3
- require 'set'
4
- require 'yaml'
5
- require 'cgi'
2
+
3
+ require "uri"
4
+ require "set"
5
+ require "yaml"
6
+ require "cgi"
6
7
 
7
8
  module Sidekiq
8
- # This is not a public API
9
+ # These methods are available to pages within the Web UI and UI extensions.
10
+ # They are not public APIs for applications to use.
9
11
  module WebHelpers
12
+ def style_tag(location, **kwargs)
13
+ global = location.match?(/:\/\//)
14
+ location = root_path + location if !global && !location.start_with?(root_path)
15
+ attrs = {
16
+ type: "text/css",
17
+ media: "screen",
18
+ rel: "stylesheet",
19
+ nonce: csp_nonce,
20
+ href: location
21
+ }
22
+ html_tag(:link, attrs.merge(kwargs))
23
+ end
24
+
25
+ def script_tag(location, **kwargs)
26
+ global = location.match?(/:\/\//)
27
+ location = root_path + location if !global && !location.start_with?(root_path)
28
+ attrs = {
29
+ type: "text/javascript",
30
+ nonce: csp_nonce,
31
+ src: location
32
+ }
33
+ html_tag(:script, attrs.merge(kwargs)) {}
34
+ end
35
+
36
+ # NB: keys and values are not escaped; do not allow user input
37
+ # in the attributes
38
+ private def html_tag(tagname, attrs)
39
+ s = +"<#{tagname}"
40
+ attrs.each_pair do |k, v|
41
+ next unless v
42
+ s << " #{k}=\"#{v}\""
43
+ end
44
+ if block_given?
45
+ s << ">"
46
+ yield s
47
+ s << "</#{tagname}>"
48
+ else
49
+ s << " />"
50
+ end
51
+ s
52
+ end
53
+
10
54
  def strings(lang)
11
- @@strings ||= {}
12
- @@strings[lang] ||= begin
13
- # Allow sidekiq-web extensions to add locale paths
14
- # so extensions can be localized
15
- settings.locales.each_with_object({}) do |path, global|
16
- find_locale_files(lang).each do |file|
17
- strs = YAML.load(File.open(file))
18
- global.deep_merge!(strs[lang])
19
- end
55
+ @strings ||= {}
56
+
57
+ # Allow sidekiq-web extensions to add locale paths
58
+ # so extensions can be localized
59
+ @strings[lang] ||= settings.locales.each_with_object({}) do |path, global|
60
+ find_locale_files(lang).each do |file|
61
+ strs = YAML.safe_load(File.read(file))
62
+ global.merge!(strs[lang])
20
63
  end
21
64
  end
22
65
  end
23
66
 
67
+ def to_json(x)
68
+ Sidekiq.dump_json(x)
69
+ end
70
+
71
+ def singularize(str, count)
72
+ if count == 1 && str.respond_to?(:singularize) # rails
73
+ str.singularize
74
+ else
75
+ str
76
+ end
77
+ end
78
+
24
79
  def clear_caches
25
- @@strings = nil
26
- @@locale_files = nil
80
+ @strings = nil
81
+ @locale_files = nil
82
+ @available_locales = nil
27
83
  end
28
84
 
29
85
  def locale_files
30
- @@locale_files ||= settings.locales.flat_map do |path|
86
+ @locale_files ||= settings.locales.flat_map { |path|
31
87
  Dir["#{path}/*.yml"]
32
- end
88
+ }
89
+ end
90
+
91
+ def available_locales
92
+ @available_locales ||= Set.new(locale_files.map { |path| File.basename(path, ".yml") })
33
93
  end
34
94
 
35
95
  def find_locale_files(lang)
36
96
  locale_files.select { |file| file =~ /\/#{lang}\.yml$/ }
37
97
  end
38
98
 
39
- # This is a hook for a Sidekiq Pro feature. Please don't touch.
40
- def filtering(*)
99
+ def search(jobset, substr)
100
+ resultset = jobset.scan(substr).to_a
101
+ @current_page = 1
102
+ @count = @total_size = resultset.size
103
+ resultset
104
+ end
105
+
106
+ def filtering(which)
107
+ erb(:filtering, locals: {which: which})
108
+ end
109
+
110
+ def filter_link(jid, within = "retries")
111
+ if within.nil?
112
+ ::Rack::Utils.escape_html(jid)
113
+ else
114
+ "<a href='#{root_path}filter/#{within}?substr=#{jid}'>#{::Rack::Utils.escape_html(jid)}</a>"
115
+ end
116
+ end
117
+
118
+ def display_tags(job, within = "retries")
119
+ job.tags.map { |tag|
120
+ "<span class='label label-info jobtag'>#{filter_link(tag, within)}</span>"
121
+ }.join(" ")
41
122
  end
42
123
 
43
124
  # This view helper provide ability display you html code in
@@ -57,42 +138,61 @@ module Sidekiq
57
138
  @head_html.join if defined?(@head_html)
58
139
  end
59
140
 
60
- def poll_path
61
- if current_path != '' && params['poll']
62
- root_path + current_path
63
- else
64
- ""
65
- end
141
+ def text_direction
142
+ get_locale["TextDirection"] || "ltr"
66
143
  end
67
144
 
68
- # Given a browser request Accept-Language header like
69
- # "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2", this function
70
- # will return "fr" since that's the first code with a matching
71
- # locale in web/locales
145
+ def rtl?
146
+ text_direction == "rtl"
147
+ end
148
+
149
+ # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
150
+ def user_preferred_languages
151
+ languages = env["HTTP_ACCEPT_LANGUAGE"]
152
+ languages.to_s.downcase.gsub(/\s+/, "").split(",").map { |language|
153
+ locale, quality = language.split(";q=", 2)
154
+ locale = nil if locale == "*" # Ignore wildcards
155
+ quality = quality ? quality.to_f : 1.0
156
+ [locale, quality]
157
+ }.sort { |(_, left), (_, right)|
158
+ right <=> left
159
+ }.map(&:first).compact
160
+ end
161
+
162
+ # Given an Accept-Language header like "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2"
163
+ # this method will try to best match the available locales to the user's preferred languages.
164
+ #
165
+ # Inspiration taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb
72
166
  def locale
73
- @locale ||= begin
74
- locale = 'en'.freeze
75
- languages = env['HTTP_ACCEPT_LANGUAGE'.freeze] || 'en'.freeze
76
- languages.downcase.split(','.freeze).each do |lang|
77
- next if lang == '*'.freeze
78
- lang = lang.split(';'.freeze)[0]
79
- break locale = lang if find_locale_files(lang).any?
80
- end
81
- locale
167
+ # session[:locale] is set via the locale selector from the footer
168
+ @locale ||= if (l = session&.fetch(:locale, nil)) && available_locales.include?(l)
169
+ l
170
+ else
171
+ matched_locale = user_preferred_languages.map { |preferred|
172
+ preferred_language = preferred.split("-", 2).first
173
+
174
+ lang_group = available_locales.select { |available|
175
+ preferred_language == available.split("-", 2).first
176
+ }
177
+
178
+ lang_group.find { |lang| lang == preferred } || lang_group.min_by(&:length)
179
+ }.compact.first
180
+
181
+ matched_locale || "en"
82
182
  end
83
183
  end
84
184
 
85
- # mperham/sidekiq#3243
185
+ # sidekiq/sidekiq#3243
86
186
  def unfiltered?
87
- yield unless env['PATH_INFO'].start_with?("/filter/")
187
+ yield unless env["PATH_INFO"].start_with?("/filter/")
88
188
  end
89
189
 
90
190
  def get_locale
91
191
  strings(locale)
92
192
  end
93
193
 
94
- def t(msg, options={})
95
- string = get_locale[msg] || msg
194
+ def t(msg, options = {})
195
+ string = get_locale[msg] || strings("en")[msg] || msg
96
196
  if options.empty?
97
197
  string
98
198
  else
@@ -100,104 +200,135 @@ module Sidekiq
100
200
  end
101
201
  end
102
202
 
103
- def workers
104
- @workers ||= Sidekiq::Workers.new
203
+ def sort_direction_label
204
+ (params[:direction] == "asc") ? "&uarr;" : "&darr;"
205
+ end
206
+
207
+ def workset
208
+ @work ||= Sidekiq::WorkSet.new
105
209
  end
106
210
 
107
211
  def processes
108
212
  @processes ||= Sidekiq::ProcessSet.new
109
213
  end
110
214
 
111
- def stats
112
- @stats ||= Sidekiq::Stats.new
215
+ # Sorts processes by hostname following the natural sort order
216
+ def sorted_processes
217
+ @sorted_processes ||= begin
218
+ return processes unless processes.all? { |p| p["hostname"] }
219
+
220
+ processes.to_a.sort_by do |process|
221
+ # Kudos to `shurikk` on StackOverflow
222
+ # https://stackoverflow.com/a/15170063/575547
223
+ process["hostname"].split(/(\d+)/).map { |a| /\d+/.match?(a) ? a.to_i : a }
224
+ end
225
+ end
113
226
  end
114
227
 
115
- def retries_with_score(score)
116
- Sidekiq.redis do |conn|
117
- conn.zrangebyscore('retry', score, score)
118
- end.map { |msg| Sidekiq.load_json(msg) }
228
+ def busy_weights(capsule_weights)
229
+ # backwards compat with 7.0.0, remove in 7.1
230
+ cw = [capsule_weights].flatten
231
+ cw.map { |hash|
232
+ hash.map { |name, weight| (weight > 0) ? +name << ": " << weight.to_s : name }.join(", ")
233
+ }.join("; ")
119
234
  end
120
235
 
121
- def redis_connection
122
- Sidekiq.redis { |conn| conn.client.id }
236
+ def stats
237
+ @stats ||= Sidekiq::Stats.new
123
238
  end
124
239
 
125
- def namespace
126
- @@ns ||= Sidekiq.redis { |conn| conn.respond_to?(:namespace) ? conn.namespace : nil }
240
+ def redis_url
241
+ Sidekiq.redis do |conn|
242
+ conn.config.server_url
243
+ end
127
244
  end
128
245
 
129
246
  def redis_info
130
- Sidekiq.redis_info
247
+ Sidekiq.default_configuration.redis_info
131
248
  end
132
249
 
133
250
  def root_path
134
- "#{env['SCRIPT_NAME']}/"
251
+ "#{env["SCRIPT_NAME"]}/"
135
252
  end
136
253
 
137
254
  def current_path
138
- @current_path ||= request.path_info.gsub(/^\//,'')
255
+ @current_path ||= request.path_info.gsub(/^\//, "")
139
256
  end
140
257
 
141
258
  def current_status
142
- workers.size == 0 ? 'idle' : 'active'
259
+ (workset.size == 0) ? "idle" : "active"
143
260
  end
144
261
 
145
262
  def relative_time(time)
146
263
  stamp = time.getutc.iso8601
147
- %{<time title="#{stamp}" datetime="#{stamp}">#{time}</time>}
264
+ %(<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>)
148
265
  end
149
266
 
150
267
  def job_params(job, score)
151
- "#{score}-#{job['jid']}"
268
+ "#{score}-#{job["jid"]}"
152
269
  end
153
270
 
154
271
  def parse_params(params)
155
- score, jid = params.split("-")
272
+ score, jid = params.split("-", 2)
156
273
  [score.to_f, jid]
157
274
  end
158
275
 
159
- SAFE_QPARAMS = %w(page poll)
276
+ SAFE_QPARAMS = %w[page direction]
160
277
 
161
278
  # Merge options with current params, filter safe params, and stringify to query string
162
279
  def qparams(options)
163
- options = options.stringify_keys
164
- params.merge(options).map do |key, value|
280
+ stringified_options = options.transform_keys(&:to_s)
281
+
282
+ to_query_string(params.merge(stringified_options))
283
+ end
284
+
285
+ def to_query_string(params)
286
+ params.map { |key, value|
165
287
  SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
166
- end.compact.join("&")
288
+ }.compact.join("&")
167
289
  end
168
290
 
169
291
  def truncate(text, truncate_after_chars = 2000)
170
- truncate_after_chars && text.size > truncate_after_chars ? "#{text[0..truncate_after_chars]}..." : text
292
+ (truncate_after_chars && text.size > truncate_after_chars) ? "#{text[0..truncate_after_chars]}..." : text
171
293
  end
172
294
 
173
295
  def display_args(args, truncate_after_chars = 2000)
174
- args.map do |arg|
175
- h(truncate(to_display(arg), truncate_after_chars))
176
- end.join(", ")
296
+ return "Invalid job payload, args is nil" if args.nil?
297
+ return "Invalid job payload, args must be an Array, not #{args.class.name}" unless args.is_a?(Array)
298
+
299
+ begin
300
+ args.map { |arg|
301
+ h(truncate(to_display(arg), truncate_after_chars))
302
+ }.join(", ")
303
+ rescue
304
+ "Illegal job arguments: #{h args.inspect}"
305
+ end
177
306
  end
178
307
 
179
308
  def csrf_tag
180
- "<input type='hidden' name='authenticity_token' value='#{session[:csrf]}'/>"
309
+ "<input type='hidden' name='authenticity_token' value='#{env[:csrf_token]}'/>"
310
+ end
311
+
312
+ def csp_nonce
313
+ env[:csp_nonce]
181
314
  end
182
315
 
183
316
  def to_display(arg)
317
+ arg.inspect
318
+ rescue
184
319
  begin
185
- arg.inspect
186
- rescue
187
- begin
188
- arg.to_s
189
- rescue => ex
190
- "Cannot display argument: [#{ex.class.name}] #{ex.message}"
191
- end
320
+ arg.to_s
321
+ rescue => ex
322
+ "Cannot display argument: [#{ex.class.name}] #{ex.message}"
192
323
  end
193
324
  end
194
325
 
195
- RETRY_JOB_KEYS = Set.new(%w(
326
+ RETRY_JOB_KEYS = Set.new(%w[
196
327
  queue class args retry_count retried_at failed_at
197
328
  jid error_message error_class backtrace
198
329
  error_backtrace enqueued_at retry wrapped
199
- created_at
200
- ))
330
+ created_at tags display_class
331
+ ])
201
332
 
202
333
  def retry_extra_items(retry_job)
203
334
  @retry_extra_items ||= {}.tap do |extra|
@@ -207,24 +338,28 @@ module Sidekiq
207
338
  end
208
339
  end
209
340
 
210
- def number_with_delimiter(number)
211
- begin
212
- Float(number)
213
- rescue ArgumentError, TypeError
214
- return number
341
+ def format_memory(rss_kb)
342
+ return "0" if rss_kb.nil? || rss_kb == 0
343
+
344
+ if rss_kb < 100_000
345
+ "#{number_with_delimiter(rss_kb)} KB"
346
+ elsif rss_kb < 10_000_000
347
+ "#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
348
+ else
349
+ "#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)), precision: 1)} GB"
215
350
  end
351
+ end
216
352
 
217
- options = {delimiter: ',', separator: '.'}
218
- parts = number.to_s.to_str.split('.')
219
- parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
220
- parts.join(options[:separator])
353
+ def number_with_delimiter(number, options = {})
354
+ precision = options[:precision] || 0
355
+ %(<span data-nwp="#{precision}">#{number.round(precision)}</span>)
221
356
  end
222
357
 
223
358
  def h(text)
224
- ::Rack::Utils.escape_html(text)
359
+ ::Rack::Utils.escape_html(text.to_s)
225
360
  rescue ArgumentError => e
226
- raise unless e.message.eql?('invalid byte sequence in UTF-8')
227
- text.encode!('UTF-16', 'UTF-8', invalid: :replace, replace: '').encode!('UTF-8', 'UTF-16')
361
+ raise unless e.message.eql?("invalid byte sequence in UTF-8")
362
+ text.encode!("UTF-16", "UTF-8", invalid: :replace, replace: "").encode!("UTF-8", "UTF-16")
228
363
  retry
229
364
  end
230
365
 
@@ -241,7 +376,7 @@ module Sidekiq
241
376
  end
242
377
 
243
378
  def environment_title_prefix
244
- environment = Sidekiq.options[:environment] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
379
+ environment = Sidekiq.default_configuration[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
245
380
 
246
381
  "[#{environment.upcase}] " unless environment == "production"
247
382
  end
@@ -250,27 +385,29 @@ module Sidekiq
250
385
  "Sidekiq v#{Sidekiq::VERSION}"
251
386
  end
252
387
 
253
- def redis_connection_and_namespace
254
- @redis_connection_and_namespace ||= begin
255
- namespace_suffix = namespace == nil ? '' : "##{namespace}"
256
- "#{redis_connection}#{namespace_suffix}"
257
- end
388
+ def server_utc_time
389
+ Time.now.utc.strftime("%H:%M:%S UTC")
390
+ end
391
+
392
+ def pollable?
393
+ # there's no point to refreshing the metrics pages every N seconds
394
+ !(current_path == "" || current_path.index("metrics"))
258
395
  end
259
396
 
260
397
  def retry_or_delete_or_kill(job, params)
261
- if params['retry']
398
+ if params["retry"]
262
399
  job.retry
263
- elsif params['delete']
400
+ elsif params["delete"]
264
401
  job.delete
265
- elsif params['kill']
402
+ elsif params["kill"]
266
403
  job.kill
267
404
  end
268
405
  end
269
406
 
270
407
  def delete_or_add_queue(job, params)
271
- if params['delete']
408
+ if params["delete"]
272
409
  job.delete
273
- elsif params['add_to_queue']
410
+ elsif params["add_to_queue"]
274
411
  job.add_to_queue
275
412
  end
276
413
  end
@@ -1,18 +1,23 @@
1
1
  # frozen_string_literal: true
2
- require 'rack'
2
+
3
+ require "rack"
3
4
 
4
5
  module Sidekiq
5
6
  module WebRouter
6
- GET = 'GET'.freeze
7
- DELETE = 'DELETE'.freeze
8
- POST = 'POST'.freeze
9
- PUT = 'PUT'.freeze
10
- PATCH = 'PATCH'.freeze
11
- HEAD = 'HEAD'.freeze
12
-
13
- ROUTE_PARAMS = 'rack.route_params'.freeze
14
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
15
- PATH_INFO = 'PATH_INFO'.freeze
7
+ GET = "GET"
8
+ DELETE = "DELETE"
9
+ POST = "POST"
10
+ PUT = "PUT"
11
+ PATCH = "PATCH"
12
+ HEAD = "HEAD"
13
+
14
+ ROUTE_PARAMS = "rack.route_params"
15
+ REQUEST_METHOD = "REQUEST_METHOD"
16
+ PATH_INFO = "PATH_INFO"
17
+
18
+ def head(path, &block)
19
+ route(HEAD, path, &block)
20
+ end
16
21
 
17
22
  def get(path, &block)
18
23
  route(GET, path, &block)
@@ -35,10 +40,9 @@ module Sidekiq
35
40
  end
36
41
 
37
42
  def route(method, path, &block)
38
- @routes ||= { GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => [] }
43
+ @routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []}
39
44
 
40
45
  @routes[method] << WebRoute.new(method, path, block)
41
- @routes[HEAD] << WebRoute.new(method, path, block) if method == GET
42
46
  end
43
47
 
44
48
  def match(env)
@@ -50,7 +54,8 @@ module Sidekiq
50
54
  path_info = "/" if path_info == ""
51
55
 
52
56
  @routes[request_method].each do |route|
53
- if params = route.match(request_method, path_info)
57
+ params = route.match(request_method, path_info)
58
+ if params
54
59
  env[ROUTE_PARAMS] = params
55
60
 
56
61
  return WebAction.new(env, route.block)
@@ -64,7 +69,7 @@ module Sidekiq
64
69
  class WebRoute
65
70
  attr_accessor :request_method, :pattern, :block, :name
66
71
 
67
- NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^\.:$\/]+)/.freeze
72
+ NAMED_SEGMENTS_PATTERN = /\/([^\/]*):([^.:$\/]+)/
68
73
 
69
74
  def initialize(request_method, pattern, block)
70
75
  @request_method = request_method
@@ -77,7 +82,7 @@ module Sidekiq
77
82
  end
78
83
 
79
84
  def compile
80
- if pattern.match(NAMED_SEGMENTS_PATTERN)
85
+ if pattern.match?(NAMED_SEGMENTS_PATTERN)
81
86
  p = pattern.gsub(NAMED_SEGMENTS_PATTERN, '/\1(?<\2>[^$/]+)')
82
87
 
83
88
  Regexp.new("\\A#{p}\\Z")
@@ -91,9 +96,8 @@ module Sidekiq
91
96
  when String
92
97
  {} if path == matcher
93
98
  else
94
- if path_match = path.match(matcher)
95
- Hash[path_match.names.map(&:to_sym).zip(path_match.captures)]
96
- end
99
+ path_match = path.match(matcher)
100
+ path_match&.named_captures&.transform_keys(&:to_sym)
97
101
  end
98
102
  end
99
103
  end