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.
- checksums.yaml +5 -5
- data/Changes.md +859 -7
- data/LICENSE.txt +9 -0
- data/README.md +49 -50
- data/bin/multi_queue_bench +271 -0
- data/bin/sidekiq +22 -3
- data/bin/sidekiqload +212 -119
- data/bin/sidekiqmon +11 -0
- data/lib/generators/sidekiq/job_generator.rb +59 -0
- data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
- data/lib/generators/sidekiq/templates/job_spec.rb.erb +6 -0
- data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
- data/lib/sidekiq/api.rb +680 -315
- data/lib/sidekiq/capsule.rb +132 -0
- data/lib/sidekiq/cli.rb +268 -248
- data/lib/sidekiq/client.rb +136 -101
- data/lib/sidekiq/component.rb +68 -0
- data/lib/sidekiq/config.rb +293 -0
- data/lib/sidekiq/deploy.rb +64 -0
- data/lib/sidekiq/embedded.rb +63 -0
- data/lib/sidekiq/fetch.rb +49 -42
- data/lib/sidekiq/iterable_job.rb +55 -0
- data/lib/sidekiq/job/interrupt_handler.rb +24 -0
- data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
- data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
- data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
- data/lib/sidekiq/job/iterable.rb +231 -0
- data/lib/sidekiq/job.rb +385 -0
- data/lib/sidekiq/job_logger.rb +62 -0
- data/lib/sidekiq/job_retry.rb +305 -0
- data/lib/sidekiq/job_util.rb +109 -0
- data/lib/sidekiq/launcher.rb +208 -108
- data/lib/sidekiq/logger.rb +131 -0
- data/lib/sidekiq/manager.rb +43 -47
- data/lib/sidekiq/metrics/query.rb +158 -0
- data/lib/sidekiq/metrics/shared.rb +97 -0
- data/lib/sidekiq/metrics/tracking.rb +148 -0
- data/lib/sidekiq/middleware/chain.rb +113 -56
- data/lib/sidekiq/middleware/current_attributes.rb +113 -0
- data/lib/sidekiq/middleware/i18n.rb +7 -7
- data/lib/sidekiq/middleware/modules.rb +23 -0
- data/lib/sidekiq/monitor.rb +147 -0
- data/lib/sidekiq/paginator.rb +28 -16
- data/lib/sidekiq/processor.rb +188 -98
- data/lib/sidekiq/rails.rb +46 -97
- data/lib/sidekiq/redis_client_adapter.rb +114 -0
- data/lib/sidekiq/redis_connection.rb +71 -73
- data/lib/sidekiq/ring_buffer.rb +31 -0
- data/lib/sidekiq/scheduled.rb +140 -51
- data/lib/sidekiq/sd_notify.rb +149 -0
- data/lib/sidekiq/systemd.rb +26 -0
- data/lib/sidekiq/testing/inline.rb +6 -5
- data/lib/sidekiq/testing.rb +95 -85
- data/lib/sidekiq/transaction_aware_client.rb +51 -0
- data/lib/sidekiq/version.rb +3 -1
- data/lib/sidekiq/web/action.rb +22 -16
- data/lib/sidekiq/web/application.rb +230 -86
- data/lib/sidekiq/web/csrf_protection.rb +183 -0
- data/lib/sidekiq/web/helpers.rb +241 -104
- data/lib/sidekiq/web/router.rb +23 -19
- data/lib/sidekiq/web.rb +118 -110
- data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/lib/sidekiq.rb +96 -185
- data/sidekiq.gemspec +26 -27
- data/web/assets/images/apple-touch-icon.png +0 -0
- data/web/assets/javascripts/application.js +157 -61
- data/web/assets/javascripts/base-charts.js +106 -0
- data/web/assets/javascripts/chart.min.js +13 -0
- data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
- data/web/assets/javascripts/dashboard-charts.js +192 -0
- data/web/assets/javascripts/dashboard.js +37 -280
- data/web/assets/javascripts/metrics.js +298 -0
- data/web/assets/stylesheets/application-dark.css +147 -0
- data/web/assets/stylesheets/application-rtl.css +163 -0
- data/web/assets/stylesheets/application.css +173 -198
- data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
- data/web/assets/stylesheets/bootstrap.css +2 -2
- data/web/locales/ar.yml +87 -0
- data/web/locales/cs.yml +62 -62
- data/web/locales/da.yml +60 -53
- data/web/locales/de.yml +65 -53
- data/web/locales/el.yml +43 -24
- data/web/locales/en.yml +86 -64
- data/web/locales/es.yml +70 -53
- data/web/locales/fa.yml +65 -64
- data/web/locales/fr.yml +83 -62
- data/web/locales/gd.yml +99 -0
- data/web/locales/he.yml +80 -0
- data/web/locales/hi.yml +59 -59
- data/web/locales/it.yml +53 -53
- data/web/locales/ja.yml +75 -62
- data/web/locales/ko.yml +52 -52
- data/web/locales/lt.yml +83 -0
- data/web/locales/nb.yml +61 -61
- data/web/locales/nl.yml +52 -52
- data/web/locales/pl.yml +45 -45
- data/web/locales/pt-br.yml +83 -55
- data/web/locales/pt.yml +51 -51
- data/web/locales/ru.yml +68 -63
- data/web/locales/sv.yml +53 -53
- data/web/locales/ta.yml +60 -60
- data/web/locales/tr.yml +101 -0
- data/web/locales/uk.yml +62 -61
- data/web/locales/ur.yml +80 -0
- data/web/locales/vi.yml +83 -0
- data/web/locales/zh-cn.yml +43 -16
- data/web/locales/zh-tw.yml +42 -8
- data/web/views/_footer.erb +21 -3
- data/web/views/_job_info.erb +21 -4
- data/web/views/_metrics_period_select.erb +12 -0
- data/web/views/_nav.erb +5 -19
- data/web/views/_paging.erb +3 -1
- data/web/views/_poll_link.erb +3 -6
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +85 -31
- data/web/views/dashboard.erb +50 -20
- data/web/views/dead.erb +3 -3
- data/web/views/filtering.erb +7 -0
- data/web/views/layout.erb +17 -6
- data/web/views/metrics.erb +91 -0
- data/web/views/metrics_for_job.erb +59 -0
- data/web/views/morgue.erb +14 -15
- data/web/views/queue.erb +34 -24
- data/web/views/queues.erb +20 -4
- data/web/views/retries.erb +19 -16
- data/web/views/retry.erb +3 -3
- data/web/views/scheduled.erb +19 -17
- metadata +91 -198
- data/.github/contributing.md +0 -32
- data/.github/issue_template.md +0 -9
- data/.gitignore +0 -12
- data/.travis.yml +0 -18
- data/3.0-Upgrade.md +0 -70
- data/4.0-Upgrade.md +0 -53
- data/COMM-LICENSE +0 -95
- data/Ent-Changes.md +0 -173
- data/Gemfile +0 -29
- data/LICENSE +0 -9
- data/Pro-2.0-Upgrade.md +0 -138
- data/Pro-3.0-Upgrade.md +0 -44
- data/Pro-Changes.md +0 -628
- data/Rakefile +0 -12
- data/bin/sidekiqctl +0 -99
- data/code_of_conduct.md +0 -50
- data/lib/generators/sidekiq/templates/worker_spec.rb.erb +0 -6
- data/lib/generators/sidekiq/worker_generator.rb +0 -49
- data/lib/sidekiq/core_ext.rb +0 -119
- data/lib/sidekiq/exception_handler.rb +0 -31
- data/lib/sidekiq/extensions/action_mailer.rb +0 -57
- data/lib/sidekiq/extensions/active_record.rb +0 -40
- data/lib/sidekiq/extensions/class_methods.rb +0 -40
- data/lib/sidekiq/extensions/generic_proxy.rb +0 -25
- data/lib/sidekiq/logging.rb +0 -106
- data/lib/sidekiq/middleware/server/active_record.rb +0 -13
- data/lib/sidekiq/middleware/server/logging.rb +0 -31
- data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
- data/lib/sidekiq/util.rb +0 -63
- data/lib/sidekiq/worker.rb +0 -121
data/lib/sidekiq/web/helpers.rb
CHANGED
|
@@ -1,43 +1,124 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
require "set"
|
|
5
|
+
require "yaml"
|
|
6
|
+
require "cgi"
|
|
6
7
|
|
|
7
8
|
module Sidekiq
|
|
8
|
-
#
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
26
|
-
|
|
80
|
+
@strings = nil
|
|
81
|
+
@locale_files = nil
|
|
82
|
+
@available_locales = nil
|
|
27
83
|
end
|
|
28
84
|
|
|
29
85
|
def locale_files
|
|
30
|
-
|
|
86
|
+
@locale_files ||= settings.locales.flat_map { |path|
|
|
31
87
|
Dir["#{path}/*.yml"]
|
|
32
|
-
|
|
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
|
-
|
|
40
|
-
|
|
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
|
|
61
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
#
|
|
185
|
+
# sidekiq/sidekiq#3243
|
|
86
186
|
def unfiltered?
|
|
87
|
-
yield unless env[
|
|
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
|
|
104
|
-
|
|
203
|
+
def sort_direction_label
|
|
204
|
+
(params[:direction] == "asc") ? "↑" : "↓"
|
|
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
|
-
|
|
112
|
-
|
|
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
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
|
122
|
-
|
|
236
|
+
def stats
|
|
237
|
+
@stats ||= Sidekiq::Stats.new
|
|
123
238
|
end
|
|
124
239
|
|
|
125
|
-
def
|
|
126
|
-
|
|
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[
|
|
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
|
-
|
|
259
|
+
(workset.size == 0) ? "idle" : "active"
|
|
143
260
|
end
|
|
144
261
|
|
|
145
262
|
def relative_time(time)
|
|
146
263
|
stamp = time.getutc.iso8601
|
|
147
|
-
%
|
|
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[
|
|
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
|
|
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
|
-
|
|
164
|
-
|
|
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
|
-
|
|
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
|
|
175
|
-
|
|
176
|
-
|
|
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='#{
|
|
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.
|
|
186
|
-
rescue
|
|
187
|
-
|
|
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
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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?(
|
|
227
|
-
text.encode!(
|
|
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.
|
|
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
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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[
|
|
398
|
+
if params["retry"]
|
|
262
399
|
job.retry
|
|
263
|
-
elsif params[
|
|
400
|
+
elsif params["delete"]
|
|
264
401
|
job.delete
|
|
265
|
-
elsif params[
|
|
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[
|
|
408
|
+
if params["delete"]
|
|
272
409
|
job.delete
|
|
273
|
-
elsif params[
|
|
410
|
+
elsif params["add_to_queue"]
|
|
274
411
|
job.add_to_queue
|
|
275
412
|
end
|
|
276
413
|
end
|
data/lib/sidekiq/web/router.rb
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
require "rack"
|
|
3
4
|
|
|
4
5
|
module Sidekiq
|
|
5
6
|
module WebRouter
|
|
6
|
-
GET =
|
|
7
|
-
DELETE =
|
|
8
|
-
POST =
|
|
9
|
-
PUT =
|
|
10
|
-
PATCH =
|
|
11
|
-
HEAD =
|
|
12
|
-
|
|
13
|
-
ROUTE_PARAMS =
|
|
14
|
-
REQUEST_METHOD =
|
|
15
|
-
PATH_INFO =
|
|
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 ||= {
|
|
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
|
-
|
|
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 = /\/([^\/]*):([
|
|
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
|
-
|
|
95
|
-
|
|
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
|