sidekiq 6.1.1 → 6.5.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changes.md +250 -3
- data/LICENSE +3 -3
- data/README.md +10 -6
- data/bin/sidekiq +3 -3
- data/bin/sidekiqload +70 -66
- data/bin/sidekiqmon +1 -1
- data/lib/generators/sidekiq/job_generator.rb +57 -0
- data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
- data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
- data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
- data/lib/sidekiq/api.rb +352 -156
- data/lib/sidekiq/cli.rb +86 -41
- data/lib/sidekiq/client.rb +49 -73
- data/lib/sidekiq/{util.rb → component.rb} +12 -14
- data/lib/sidekiq/delay.rb +3 -1
- data/lib/sidekiq/extensions/action_mailer.rb +3 -2
- data/lib/sidekiq/extensions/active_record.rb +1 -1
- data/lib/sidekiq/extensions/generic_proxy.rb +4 -2
- data/lib/sidekiq/fetch.rb +31 -20
- data/lib/sidekiq/job.rb +13 -0
- data/lib/sidekiq/job_logger.rb +16 -28
- data/lib/sidekiq/job_retry.rb +79 -59
- data/lib/sidekiq/job_util.rb +71 -0
- data/lib/sidekiq/launcher.rb +126 -65
- data/lib/sidekiq/logger.rb +11 -20
- data/lib/sidekiq/manager.rb +35 -34
- data/lib/sidekiq/metrics/deploy.rb +47 -0
- data/lib/sidekiq/metrics/query.rb +153 -0
- data/lib/sidekiq/metrics/shared.rb +94 -0
- data/lib/sidekiq/metrics/tracking.rb +134 -0
- data/lib/sidekiq/middleware/chain.rb +88 -42
- data/lib/sidekiq/middleware/current_attributes.rb +63 -0
- data/lib/sidekiq/middleware/i18n.rb +6 -4
- data/lib/sidekiq/middleware/modules.rb +21 -0
- data/lib/sidekiq/monitor.rb +2 -2
- data/lib/sidekiq/paginator.rb +17 -9
- data/lib/sidekiq/processor.rb +47 -41
- data/lib/sidekiq/rails.rb +32 -4
- data/lib/sidekiq/redis_client_adapter.rb +154 -0
- data/lib/sidekiq/redis_connection.rb +84 -55
- data/lib/sidekiq/ring_buffer.rb +29 -0
- data/lib/sidekiq/scheduled.rb +96 -32
- data/lib/sidekiq/testing/inline.rb +4 -4
- data/lib/sidekiq/testing.rb +38 -39
- data/lib/sidekiq/transaction_aware_client.rb +45 -0
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/action.rb +3 -3
- data/lib/sidekiq/web/application.rb +41 -16
- data/lib/sidekiq/web/csrf_protection.rb +32 -5
- data/lib/sidekiq/web/helpers.rb +52 -30
- data/lib/sidekiq/web/router.rb +4 -1
- data/lib/sidekiq/web.rb +38 -78
- data/lib/sidekiq/worker.rb +142 -16
- data/lib/sidekiq.rb +114 -31
- data/sidekiq.gemspec +12 -4
- data/web/assets/images/apple-touch-icon.png +0 -0
- data/web/assets/javascripts/application.js +114 -60
- 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.js +50 -67
- data/web/assets/javascripts/graph.js +16 -0
- data/web/assets/javascripts/metrics.js +262 -0
- data/web/assets/stylesheets/application-dark.css +61 -51
- data/web/assets/stylesheets/application-rtl.css +0 -4
- data/web/assets/stylesheets/application.css +84 -243
- data/web/locales/ar.yml +8 -2
- data/web/locales/el.yml +43 -19
- data/web/locales/en.yml +11 -1
- data/web/locales/es.yml +18 -2
- data/web/locales/fr.yml +8 -1
- data/web/locales/ja.yml +10 -0
- data/web/locales/lt.yml +1 -1
- data/web/locales/pt-br.yml +27 -9
- data/web/locales/ru.yml +4 -0
- data/web/locales/zh-cn.yml +36 -11
- data/web/locales/zh-tw.yml +32 -7
- data/web/views/_footer.erb +1 -1
- data/web/views/_job_info.erb +1 -1
- data/web/views/_nav.erb +1 -1
- data/web/views/_poll_link.erb +2 -5
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +57 -21
- data/web/views/dashboard.erb +23 -14
- data/web/views/dead.erb +1 -1
- data/web/views/layout.erb +2 -1
- data/web/views/metrics.erb +69 -0
- data/web/views/metrics_for_job.erb +87 -0
- data/web/views/morgue.erb +6 -6
- data/web/views/queue.erb +15 -11
- data/web/views/queues.erb +4 -4
- data/web/views/retries.erb +7 -7
- data/web/views/retry.erb +1 -1
- data/web/views/scheduled.erb +1 -1
- metadata +52 -39
- data/.circleci/config.yml +0 -71
- data/.github/contributing.md +0 -32
- data/.github/issue_template.md +0 -11
- data/.gitignore +0 -13
- data/.standard.yml +0 -20
- data/3.0-Upgrade.md +0 -70
- data/4.0-Upgrade.md +0 -53
- data/5.0-Upgrade.md +0 -56
- data/6.0-Upgrade.md +0 -72
- data/COMM-LICENSE +0 -97
- data/Ent-2.0-Upgrade.md +0 -37
- data/Ent-Changes.md +0 -275
- data/Gemfile +0 -24
- data/Gemfile.lock +0 -208
- data/Pro-2.0-Upgrade.md +0 -138
- data/Pro-3.0-Upgrade.md +0 -44
- data/Pro-4.0-Upgrade.md +0 -35
- data/Pro-5.0-Upgrade.md +0 -25
- data/Pro-Changes.md +0 -795
- data/Rakefile +0 -10
- data/code_of_conduct.md +0 -50
- data/lib/generators/sidekiq/worker_generator.rb +0 -57
- data/lib/sidekiq/exception_handler.rb +0 -27
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "securerandom"
|
4
|
+
require "sidekiq/client"
|
5
|
+
|
6
|
+
module Sidekiq
|
7
|
+
class TransactionAwareClient
|
8
|
+
def initialize(redis_pool)
|
9
|
+
@redis_client = Client.new(redis_pool)
|
10
|
+
end
|
11
|
+
|
12
|
+
def push(item)
|
13
|
+
# pre-allocate the JID so we can return it immediately and
|
14
|
+
# save it to the database as part of the transaction.
|
15
|
+
item["jid"] ||= SecureRandom.hex(12)
|
16
|
+
AfterCommitEverywhere.after_commit { @redis_client.push(item) }
|
17
|
+
item["jid"]
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# We don't provide transactionality for push_bulk because we don't want
|
22
|
+
# to hold potentially hundreds of thousands of job records in memory due to
|
23
|
+
# a long running enqueue process.
|
24
|
+
def push_bulk(items)
|
25
|
+
@redis_client.push_bulk(items)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Use `Sidekiq.transactional_push!` in your sidekiq.rb initializer
|
32
|
+
module Sidekiq
|
33
|
+
def self.transactional_push!
|
34
|
+
begin
|
35
|
+
require "after_commit_everywhere"
|
36
|
+
rescue LoadError
|
37
|
+
Sidekiq.logger.error("You need to add after_commit_everywhere to your Gemfile to use Sidekiq's transactional client")
|
38
|
+
raise
|
39
|
+
end
|
40
|
+
|
41
|
+
default_job_options["client_class"] = Sidekiq::TransactionAwareClient
|
42
|
+
Sidekiq::JobUtil::TRANSIENT_ATTRIBUTES << "client_class"
|
43
|
+
true
|
44
|
+
end
|
45
|
+
end
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web/action.rb
CHANGED
@@ -15,11 +15,11 @@ module Sidekiq
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def halt(res)
|
18
|
-
throw :halt, res
|
18
|
+
throw :halt, [res, {"content-type" => "text/plain"}, [res.to_s]]
|
19
19
|
end
|
20
20
|
|
21
21
|
def redirect(location)
|
22
|
-
throw :halt, [302, {"
|
22
|
+
throw :halt, [302, {"location" => "#{request.base_url}#{location}"}, []]
|
23
23
|
end
|
24
24
|
|
25
25
|
def params
|
@@ -68,7 +68,7 @@ module Sidekiq
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def json(payload)
|
71
|
-
[200, {"
|
71
|
+
[200, {"content-type" => "application/json", "cache-control" => "private, no-store"}, [Sidekiq.dump_json(payload)]]
|
72
72
|
end
|
73
73
|
|
74
74
|
def initialize(env, block)
|
@@ -4,7 +4,6 @@ module Sidekiq
|
|
4
4
|
class WebApplication
|
5
5
|
extend WebRouter
|
6
6
|
|
7
|
-
CONTENT_LENGTH = "Content-Length"
|
8
7
|
REDIS_KEYS = %w[redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human]
|
9
8
|
CSP_HEADER = [
|
10
9
|
"default-src 'self' https: http:",
|
@@ -42,16 +41,42 @@ module Sidekiq
|
|
42
41
|
# nothing, backwards compatibility
|
43
42
|
end
|
44
43
|
|
44
|
+
head "/" do
|
45
|
+
# HEAD / is the cheapest heartbeat possible,
|
46
|
+
# it hits Redis to ensure connectivity
|
47
|
+
Sidekiq.redis { |c| c.llen("queue:default") }
|
48
|
+
""
|
49
|
+
end
|
50
|
+
|
45
51
|
get "/" do
|
46
52
|
@redis_info = redis_info.select { |k, v| REDIS_KEYS.include? k }
|
47
|
-
|
53
|
+
days = (params["days"] || 30).to_i
|
54
|
+
return halt(401) if days < 1 || days > 180
|
55
|
+
|
56
|
+
stats_history = Sidekiq::Stats::History.new(days)
|
48
57
|
@processed_history = stats_history.processed
|
49
58
|
@failed_history = stats_history.failed
|
50
59
|
|
51
60
|
erb(:dashboard)
|
52
61
|
end
|
53
62
|
|
63
|
+
get "/metrics" do
|
64
|
+
q = Sidekiq::Metrics::Query.new
|
65
|
+
@query_result = q.top_jobs
|
66
|
+
erb(:metrics)
|
67
|
+
end
|
68
|
+
|
69
|
+
get "/metrics/:name" do
|
70
|
+
@name = route_params[:name]
|
71
|
+
q = Sidekiq::Metrics::Query.new
|
72
|
+
@query_result = q.for_job(@name)
|
73
|
+
erb(:metrics_for_job)
|
74
|
+
end
|
75
|
+
|
54
76
|
get "/busy" do
|
77
|
+
@count = (params["count"] || 100).to_i
|
78
|
+
(@current_page, @total_size, @workset) = page_items(workset, params["page"], @count)
|
79
|
+
|
55
80
|
erb(:busy)
|
56
81
|
end
|
57
82
|
|
@@ -76,15 +101,17 @@ module Sidekiq
|
|
76
101
|
erb(:queues)
|
77
102
|
end
|
78
103
|
|
104
|
+
QUEUE_NAME = /\A[a-z_:.\-0-9]+\z/i
|
105
|
+
|
79
106
|
get "/queues/:name" do
|
80
107
|
@name = route_params[:name]
|
81
108
|
|
82
|
-
halt(404)
|
109
|
+
halt(404) if !@name || @name !~ QUEUE_NAME
|
83
110
|
|
84
111
|
@count = (params["count"] || 25).to_i
|
85
112
|
@queue = Sidekiq::Queue.new(@name)
|
86
|
-
(@current_page, @total_size, @
|
87
|
-
@
|
113
|
+
(@current_page, @total_size, @jobs) = page("queue:#{@name}", params["page"], @count, reverse: params["direction"] == "asc")
|
114
|
+
@jobs = @jobs.map { |msg| Sidekiq::JobRecord.new(msg, @name) }
|
88
115
|
|
89
116
|
erb(:queue)
|
90
117
|
end
|
@@ -105,7 +132,7 @@ module Sidekiq
|
|
105
132
|
|
106
133
|
post "/queues/:name/delete" do
|
107
134
|
name = route_params[:name]
|
108
|
-
Sidekiq::
|
135
|
+
Sidekiq::JobRecord.new(params["key_val"], name).delete
|
109
136
|
|
110
137
|
redirect_with_query("#{root_path}queues/#{CGI.escape(name)}")
|
111
138
|
end
|
@@ -288,37 +315,35 @@ module Sidekiq
|
|
288
315
|
|
289
316
|
def call(env)
|
290
317
|
action = self.class.match(env)
|
291
|
-
return [404, {"
|
318
|
+
return [404, {"content-type" => "text/plain", "x-cascade" => "pass"}, ["Not Found"]] unless action
|
292
319
|
|
293
320
|
app = @klass
|
294
|
-
resp = catch(:halt) do
|
321
|
+
resp = catch(:halt) do
|
295
322
|
self.class.run_befores(app, action)
|
296
323
|
action.instance_exec env, &action.block
|
297
324
|
ensure
|
298
325
|
self.class.run_afters(app, action)
|
299
326
|
end
|
300
327
|
|
301
|
-
|
328
|
+
case resp
|
302
329
|
when Array
|
303
330
|
# redirects go here
|
304
331
|
resp
|
305
332
|
else
|
306
333
|
# rendered content goes here
|
307
334
|
headers = {
|
308
|
-
"
|
309
|
-
"
|
310
|
-
"
|
311
|
-
"
|
335
|
+
"content-type" => "text/html",
|
336
|
+
"cache-control" => "private, no-store",
|
337
|
+
"content-language" => action.locale,
|
338
|
+
"content-security-policy" => CSP_HEADER
|
312
339
|
}
|
313
340
|
# we'll let Rack calculate Content-Length for us.
|
314
341
|
[200, headers, [resp]]
|
315
342
|
end
|
316
|
-
|
317
|
-
resp
|
318
343
|
end
|
319
344
|
|
320
345
|
def self.helpers(mod = nil, &block)
|
321
|
-
if
|
346
|
+
if block
|
322
347
|
WebAction.class_eval(&block)
|
323
348
|
else
|
324
349
|
WebAction.send(:include, mod)
|
@@ -66,13 +66,37 @@ module Sidekiq
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def session(env)
|
69
|
-
env["rack.session"] || fail(
|
69
|
+
env["rack.session"] || fail(<<~EOM)
|
70
|
+
Sidekiq::Web needs a valid Rack session for CSRF protection. If this is a Rails app,
|
71
|
+
make sure you mount Sidekiq::Web *inside* your application routes:
|
72
|
+
|
73
|
+
|
74
|
+
Rails.application.routes.draw do
|
75
|
+
mount Sidekiq::Web => "/sidekiq"
|
76
|
+
....
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
If this is a Rails app in API mode, you need to enable sessions.
|
81
|
+
|
82
|
+
https://guides.rubyonrails.org/api_app.html#using-session-middlewares
|
83
|
+
|
84
|
+
If this is a bare Rack app, use a session middleware before Sidekiq::Web:
|
85
|
+
|
86
|
+
# first, use IRB to create a shared secret key for sessions and commit it
|
87
|
+
require 'securerandom'; File.open(".session.key", "w") {|f| f.write(SecureRandom.hex(32)) }
|
88
|
+
|
89
|
+
# now use the secret with a session cookie middleware
|
90
|
+
use Rack::Session::Cookie, secret: File.read(".session.key"), same_site: true, max_age: 86400
|
91
|
+
run Sidekiq::Web
|
92
|
+
|
93
|
+
EOM
|
70
94
|
end
|
71
95
|
|
72
96
|
def accept?(env)
|
73
97
|
return true if safe?(env)
|
74
98
|
|
75
|
-
giventoken = Rack::Request.new(env).params["authenticity_token"]
|
99
|
+
giventoken = ::Rack::Request.new(env).params["authenticity_token"]
|
76
100
|
valid_token?(env, giventoken)
|
77
101
|
end
|
78
102
|
|
@@ -92,6 +116,9 @@ module Sidekiq
|
|
92
116
|
sess = session(env)
|
93
117
|
localtoken = sess[:csrf]
|
94
118
|
|
119
|
+
# Checks that Rack::Session::Cookie actualy contains the csrf toekn
|
120
|
+
return false if localtoken.nil?
|
121
|
+
|
95
122
|
# Rotate the session token after every use
|
96
123
|
sess[:csrf] = SecureRandom.base64(TOKEN_LENGTH)
|
97
124
|
|
@@ -116,7 +143,7 @@ module Sidekiq
|
|
116
143
|
one_time_pad = SecureRandom.random_bytes(token.length)
|
117
144
|
encrypted_token = xor_byte_strings(one_time_pad, token)
|
118
145
|
masked_token = one_time_pad + encrypted_token
|
119
|
-
Base64.
|
146
|
+
Base64.urlsafe_encode64(masked_token)
|
120
147
|
end
|
121
148
|
|
122
149
|
# Essentially the inverse of +mask_token+.
|
@@ -138,11 +165,11 @@ module Sidekiq
|
|
138
165
|
end
|
139
166
|
|
140
167
|
def compare_with_real_token(token, local)
|
141
|
-
Rack::Utils.secure_compare(token.to_s, decode_token(local).to_s)
|
168
|
+
::Rack::Utils.secure_compare(token.to_s, decode_token(local).to_s)
|
142
169
|
end
|
143
170
|
|
144
171
|
def decode_token(token)
|
145
|
-
Base64.
|
172
|
+
Base64.urlsafe_decode64(token)
|
146
173
|
end
|
147
174
|
|
148
175
|
def xor_byte_strings(s1, s2)
|
data/lib/sidekiq/web/helpers.rb
CHANGED
@@ -10,18 +10,25 @@ module Sidekiq
|
|
10
10
|
module WebHelpers
|
11
11
|
def strings(lang)
|
12
12
|
@strings ||= {}
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
13
|
+
|
14
|
+
# Allow sidekiq-web extensions to add locale paths
|
15
|
+
# so extensions can be localized
|
16
|
+
@strings[lang] ||= settings.locales.each_with_object({}) do |path, global|
|
17
|
+
find_locale_files(lang).each do |file|
|
18
|
+
strs = YAML.safe_load(File.open(file))
|
19
|
+
global.merge!(strs[lang])
|
21
20
|
end
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
24
|
+
def singularize(str, count)
|
25
|
+
if count == 1 && str.respond_to?(:singularize) # rails
|
26
|
+
str.singularize
|
27
|
+
else
|
28
|
+
str
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
25
32
|
def clear_caches
|
26
33
|
@strings = nil
|
27
34
|
@locale_files = nil
|
@@ -63,17 +70,6 @@ module Sidekiq
|
|
63
70
|
@head_html.join if defined?(@head_html)
|
64
71
|
end
|
65
72
|
|
66
|
-
def poll_path
|
67
|
-
if current_path != "" && params["poll"]
|
68
|
-
path = root_path + current_path
|
69
|
-
query_string = to_query_string(params.slice(*params.keys - %w[page poll]))
|
70
|
-
path += "?#{query_string}" unless query_string.empty?
|
71
|
-
path
|
72
|
-
else
|
73
|
-
""
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
73
|
def text_direction
|
78
74
|
get_locale["TextDirection"] || "ltr"
|
79
75
|
end
|
@@ -118,7 +114,7 @@ module Sidekiq
|
|
118
114
|
# within is used by Sidekiq Pro
|
119
115
|
def display_tags(job, within = nil)
|
120
116
|
job.tags.map { |tag|
|
121
|
-
"<span class='
|
117
|
+
"<span class='label label-info jobtag'>#{::Rack::Utils.escape_html(tag)}</span>"
|
122
118
|
}.join(" ")
|
123
119
|
end
|
124
120
|
|
@@ -141,25 +137,37 @@ module Sidekiq
|
|
141
137
|
end
|
142
138
|
|
143
139
|
def sort_direction_label
|
144
|
-
params[:direction] == "asc" ? "↑" : "↓"
|
140
|
+
(params[:direction] == "asc") ? "↑" : "↓"
|
145
141
|
end
|
146
142
|
|
147
|
-
def
|
148
|
-
@
|
143
|
+
def workset
|
144
|
+
@work ||= Sidekiq::WorkSet.new
|
149
145
|
end
|
150
146
|
|
151
147
|
def processes
|
152
148
|
@processes ||= Sidekiq::ProcessSet.new
|
153
149
|
end
|
154
150
|
|
151
|
+
# Sorts processes by hostname following the natural sort order
|
152
|
+
def sorted_processes
|
153
|
+
@sorted_processes ||= begin
|
154
|
+
return processes unless processes.all? { |p| p["hostname"] }
|
155
|
+
|
156
|
+
processes.to_a.sort_by do |process|
|
157
|
+
# Kudos to `shurikk` on StackOverflow
|
158
|
+
# https://stackoverflow.com/a/15170063/575547
|
159
|
+
process["hostname"].split(/(\d+)/).map { |a| /\d+/.match?(a) ? a.to_i : a }
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
155
164
|
def stats
|
156
165
|
@stats ||= Sidekiq::Stats.new
|
157
166
|
end
|
158
167
|
|
159
168
|
def redis_connection
|
160
169
|
Sidekiq.redis do |conn|
|
161
|
-
|
162
|
-
"redis://#{c[:location]}/#{c[:db]}"
|
170
|
+
conn.connection[:id]
|
163
171
|
end
|
164
172
|
end
|
165
173
|
|
@@ -180,7 +188,7 @@ module Sidekiq
|
|
180
188
|
end
|
181
189
|
|
182
190
|
def current_status
|
183
|
-
|
191
|
+
(workset.size == 0) ? "idle" : "active"
|
184
192
|
end
|
185
193
|
|
186
194
|
def relative_time(time)
|
@@ -197,7 +205,7 @@ module Sidekiq
|
|
197
205
|
[score.to_f, jid]
|
198
206
|
end
|
199
207
|
|
200
|
-
SAFE_QPARAMS = %w[page
|
208
|
+
SAFE_QPARAMS = %w[page direction]
|
201
209
|
|
202
210
|
# Merge options with current params, filter safe params, and stringify to query string
|
203
211
|
def qparams(options)
|
@@ -213,7 +221,7 @@ module Sidekiq
|
|
213
221
|
end
|
214
222
|
|
215
223
|
def truncate(text, truncate_after_chars = 2000)
|
216
|
-
truncate_after_chars && text.size > truncate_after_chars ? "#{text[0..truncate_after_chars]}..." : text
|
224
|
+
(truncate_after_chars && text.size > truncate_after_chars) ? "#{text[0..truncate_after_chars]}..." : text
|
217
225
|
end
|
218
226
|
|
219
227
|
def display_args(args, truncate_after_chars = 2000)
|
@@ -247,7 +255,7 @@ module Sidekiq
|
|
247
255
|
queue class args retry_count retried_at failed_at
|
248
256
|
jid error_message error_class backtrace
|
249
257
|
error_backtrace enqueued_at retry wrapped
|
250
|
-
created_at tags
|
258
|
+
created_at tags display_class
|
251
259
|
])
|
252
260
|
|
253
261
|
def retry_extra_items(retry_job)
|
@@ -258,7 +266,21 @@ module Sidekiq
|
|
258
266
|
end
|
259
267
|
end
|
260
268
|
|
269
|
+
def format_memory(rss_kb)
|
270
|
+
return "0" if rss_kb.nil? || rss_kb == 0
|
271
|
+
|
272
|
+
if rss_kb < 100_000
|
273
|
+
"#{number_with_delimiter(rss_kb)} KB"
|
274
|
+
elsif rss_kb < 10_000_000
|
275
|
+
"#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
|
276
|
+
else
|
277
|
+
"#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)).round(1))} GB"
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
261
281
|
def number_with_delimiter(number)
|
282
|
+
return "" if number.nil?
|
283
|
+
|
262
284
|
begin
|
263
285
|
Float(number)
|
264
286
|
rescue ArgumentError, TypeError
|
@@ -292,7 +314,7 @@ module Sidekiq
|
|
292
314
|
end
|
293
315
|
|
294
316
|
def environment_title_prefix
|
295
|
-
environment = Sidekiq
|
317
|
+
environment = Sidekiq[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
296
318
|
|
297
319
|
"[#{environment.upcase}] " unless environment == "production"
|
298
320
|
end
|
data/lib/sidekiq/web/router.rb
CHANGED
@@ -15,6 +15,10 @@ module Sidekiq
|
|
15
15
|
REQUEST_METHOD = "REQUEST_METHOD"
|
16
16
|
PATH_INFO = "PATH_INFO"
|
17
17
|
|
18
|
+
def head(path, &block)
|
19
|
+
route(HEAD, path, &block)
|
20
|
+
end
|
21
|
+
|
18
22
|
def get(path, &block)
|
19
23
|
route(GET, path, &block)
|
20
24
|
end
|
@@ -39,7 +43,6 @@ module Sidekiq
|
|
39
43
|
@routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []}
|
40
44
|
|
41
45
|
@routes[method] << WebRoute.new(method, path, block)
|
42
|
-
@routes[HEAD] << WebRoute.new(method, path, block) if method == GET
|
43
46
|
end
|
44
47
|
|
45
48
|
def match(env)
|
data/lib/sidekiq/web.rb
CHANGED
@@ -13,10 +13,8 @@ require "sidekiq/web/application"
|
|
13
13
|
require "sidekiq/web/csrf_protection"
|
14
14
|
|
15
15
|
require "rack/content_length"
|
16
|
-
|
17
16
|
require "rack/builder"
|
18
|
-
require "rack/
|
19
|
-
require "rack/session/cookie"
|
17
|
+
require "rack/static"
|
20
18
|
|
21
19
|
module Sidekiq
|
22
20
|
class Web
|
@@ -35,19 +33,15 @@ module Sidekiq
|
|
35
33
|
"Dead" => "morgue"
|
36
34
|
}
|
37
35
|
|
36
|
+
if ENV["SIDEKIQ_METRICS_BETA"] == "1"
|
37
|
+
DEFAULT_TABS["Metrics"] = "metrics"
|
38
|
+
end
|
39
|
+
|
38
40
|
class << self
|
39
41
|
def settings
|
40
42
|
self
|
41
43
|
end
|
42
44
|
|
43
|
-
def middlewares
|
44
|
-
@middlewares ||= []
|
45
|
-
end
|
46
|
-
|
47
|
-
def use(*middleware_args, &block)
|
48
|
-
middlewares << [middleware_args, block]
|
49
|
-
end
|
50
|
-
|
51
45
|
def default_tabs
|
52
46
|
DEFAULT_TABS
|
53
47
|
end
|
@@ -73,32 +67,45 @@ module Sidekiq
|
|
73
67
|
opts.each { |key| set(key, false) }
|
74
68
|
end
|
75
69
|
|
76
|
-
|
70
|
+
def middlewares
|
71
|
+
@middlewares ||= []
|
72
|
+
end
|
73
|
+
|
74
|
+
def use(*args, &block)
|
75
|
+
middlewares << [args, block]
|
76
|
+
end
|
77
|
+
|
77
78
|
def set(attribute, value)
|
78
79
|
send(:"#{attribute}=", value)
|
79
80
|
end
|
80
81
|
|
81
|
-
|
82
|
+
def sessions=(val)
|
83
|
+
puts "WARNING: Sidekiq::Web.sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def session_secret=(val)
|
87
|
+
puts "WARNING: Sidekiq::Web.session_secret= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
|
88
|
+
end
|
89
|
+
|
90
|
+
attr_accessor :app_url, :redis_pool
|
82
91
|
attr_writer :locales, :views
|
83
92
|
end
|
84
93
|
|
85
94
|
def self.inherited(child)
|
86
95
|
child.app_url = app_url
|
87
|
-
child.session_secret = session_secret
|
88
96
|
child.redis_pool = redis_pool
|
89
|
-
child.sessions = sessions
|
90
97
|
end
|
91
98
|
|
92
99
|
def settings
|
93
100
|
self.class.settings
|
94
101
|
end
|
95
102
|
|
96
|
-
def
|
97
|
-
middlewares
|
103
|
+
def middlewares
|
104
|
+
@middlewares ||= self.class.middlewares
|
98
105
|
end
|
99
106
|
|
100
|
-
def
|
101
|
-
|
107
|
+
def use(*args, &block)
|
108
|
+
middlewares << [args, block]
|
102
109
|
end
|
103
110
|
|
104
111
|
def call(env)
|
@@ -126,18 +133,8 @@ module Sidekiq
|
|
126
133
|
send(:"#{attribute}=", value)
|
127
134
|
end
|
128
135
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
attr_writer :sessions
|
133
|
-
|
134
|
-
def sessions
|
135
|
-
unless instance_variable_defined?("@sessions")
|
136
|
-
@sessions = self.class.sessions
|
137
|
-
@sessions = @sessions.to_hash.dup if @sessions.respond_to?(:to_hash)
|
138
|
-
end
|
139
|
-
|
140
|
-
@sessions
|
136
|
+
def sessions=(val)
|
137
|
+
puts "Sidekiq::Web#sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller[2..2].first}"
|
141
138
|
end
|
142
139
|
|
143
140
|
def self.register(extension)
|
@@ -146,57 +143,20 @@ module Sidekiq
|
|
146
143
|
|
147
144
|
private
|
148
145
|
|
149
|
-
def using?(middleware)
|
150
|
-
middlewares.any? do |(m, _)|
|
151
|
-
m.is_a?(Array) && (m[0] == middleware || m[0].is_a?(middleware))
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def build_sessions
|
156
|
-
middlewares = self.middlewares
|
157
|
-
|
158
|
-
s = sessions
|
159
|
-
|
160
|
-
# turn on CSRF protection if sessions are enabled and this is not the test env
|
161
|
-
if s && !using?(CsrfProtection) && ENV["RACK_ENV"] != "test"
|
162
|
-
middlewares.unshift [[CsrfProtection], nil]
|
163
|
-
end
|
164
|
-
|
165
|
-
if s && !using?(::Rack::Session::Cookie)
|
166
|
-
unless (secret = Web.session_secret)
|
167
|
-
require "securerandom"
|
168
|
-
secret = SecureRandom.hex(64)
|
169
|
-
end
|
170
|
-
|
171
|
-
options = {secret: secret}
|
172
|
-
options = options.merge(s.to_hash) if s.respond_to? :to_hash
|
173
|
-
|
174
|
-
middlewares.unshift [[::Rack::Session::Cookie, options], nil]
|
175
|
-
end
|
176
|
-
|
177
|
-
# Since Sidekiq::WebApplication no longer calculates its own
|
178
|
-
# Content-Length response header, we must ensure that the Rack middleware
|
179
|
-
# that does this is loaded
|
180
|
-
unless using? ::Rack::ContentLength
|
181
|
-
middlewares.unshift [[::Rack::ContentLength], nil]
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
146
|
def build
|
186
|
-
build_sessions
|
187
|
-
|
188
|
-
middlewares = self.middlewares
|
189
147
|
klass = self.class
|
148
|
+
m = middlewares
|
190
149
|
|
191
|
-
|
192
|
-
|
193
|
-
map "/#{asset_dir}" do
|
194
|
-
run ::Rack::File.new("#{ASSETS}/#{asset_dir}", {"Cache-Control" => "public, max-age=86400"})
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
middlewares.each { |middleware, block| use(*middleware, &block) }
|
150
|
+
rules = []
|
151
|
+
rules = [[:all, {"cache-control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
|
199
152
|
|
153
|
+
::Rack::Builder.new do
|
154
|
+
use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
|
155
|
+
root: ASSETS,
|
156
|
+
cascade: true,
|
157
|
+
header_rules: rules
|
158
|
+
m.each { |middleware, block| use(*middleware, &block) }
|
159
|
+
use Sidekiq::Web::CsrfProtection unless $TESTING
|
200
160
|
run WebApplication.new(klass)
|
201
161
|
end
|
202
162
|
end
|