sidekiq 4.2.10 → 7.3.10

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