sidekiq 7.3.7 → 8.0.0.beta1
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 +4 -4
- data/Changes.md +30 -0
- data/README.md +16 -13
- data/bin/sidekiqload +10 -10
- data/bin/webload +69 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +5 -18
- data/lib/sidekiq/api.rb +108 -36
- data/lib/sidekiq/capsule.rb +1 -1
- data/lib/sidekiq/cli.rb +15 -19
- data/lib/sidekiq/client.rb +13 -16
- data/lib/sidekiq/component.rb +30 -2
- data/lib/sidekiq/config.rb +18 -15
- data/lib/sidekiq/embedded.rb +1 -0
- data/lib/sidekiq/job/iterable.rb +3 -5
- data/lib/sidekiq/job_retry.rb +2 -2
- data/lib/sidekiq/job_util.rb +5 -1
- data/lib/sidekiq/launcher.rb +1 -1
- data/lib/sidekiq/logger.rb +6 -10
- data/lib/sidekiq/manager.rb +0 -1
- data/lib/sidekiq/metrics/query.rb +1 -3
- data/lib/sidekiq/middleware/current_attributes.rb +5 -17
- data/lib/sidekiq/paginator.rb +14 -1
- data/lib/sidekiq/processor.rb +21 -14
- data/lib/sidekiq/profiler.rb +59 -0
- data/lib/sidekiq/rails.rb +12 -2
- data/lib/sidekiq/redis_client_adapter.rb +0 -1
- data/lib/sidekiq/testing.rb +2 -2
- data/lib/sidekiq/version.rb +2 -2
- data/lib/sidekiq/web/action.rb +101 -85
- data/lib/sidekiq/web/application.rb +339 -333
- data/lib/sidekiq/web/config.rb +116 -0
- data/lib/sidekiq/web/helpers.rb +45 -20
- data/lib/sidekiq/web/router.rb +60 -76
- data/lib/sidekiq/web.rb +51 -156
- data/sidekiq.gemspec +6 -4
- data/web/assets/javascripts/application.js +6 -13
- data/web/assets/javascripts/base-charts.js +30 -18
- data/web/assets/javascripts/dashboard-charts.js +2 -0
- data/web/assets/javascripts/dashboard.js +6 -0
- data/web/assets/javascripts/metrics.js +1 -1
- data/web/assets/stylesheets/style.css +750 -0
- data/web/locales/ar.yml +1 -0
- data/web/locales/cs.yml +1 -0
- data/web/locales/da.yml +1 -0
- data/web/locales/de.yml +1 -0
- data/web/locales/el.yml +1 -0
- data/web/locales/en.yml +9 -0
- data/web/locales/es.yml +24 -2
- data/web/locales/fa.yml +1 -0
- data/web/locales/fr.yml +1 -0
- data/web/locales/gd.yml +1 -0
- data/web/locales/he.yml +1 -0
- data/web/locales/hi.yml +1 -0
- data/web/locales/it.yml +1 -0
- data/web/locales/ja.yml +1 -0
- data/web/locales/ko.yml +1 -0
- data/web/locales/lt.yml +1 -0
- data/web/locales/nb.yml +1 -0
- data/web/locales/nl.yml +1 -0
- data/web/locales/pl.yml +1 -0
- data/web/locales/{pt-br.yml → pt-BR.yml} +2 -1
- data/web/locales/pt.yml +1 -0
- data/web/locales/ru.yml +1 -0
- data/web/locales/sv.yml +1 -0
- data/web/locales/ta.yml +1 -0
- data/web/locales/tr.yml +1 -0
- data/web/locales/uk.yml +1 -0
- data/web/locales/ur.yml +1 -0
- data/web/locales/vi.yml +1 -0
- data/web/locales/{zh-cn.yml → zh-CN.yml} +85 -73
- data/web/locales/{zh-tw.yml → zh-TW.yml} +2 -1
- data/web/views/_footer.erb +31 -34
- data/web/views/_job_info.erb +91 -89
- data/web/views/_metrics_period_select.erb +1 -1
- data/web/views/_nav.erb +14 -21
- data/web/views/_paging.erb +23 -21
- data/web/views/_poll_link.erb +2 -2
- data/web/views/_summary.erb +16 -16
- data/web/views/busy.erb +124 -122
- data/web/views/dashboard.erb +62 -64
- data/web/views/dead.erb +31 -27
- data/web/views/filtering.erb +3 -3
- data/web/views/layout.erb +5 -21
- data/web/views/metrics.erb +83 -80
- data/web/views/metrics_for_job.erb +39 -42
- data/web/views/morgue.erb +61 -70
- data/web/views/profiles.erb +43 -0
- data/web/views/queue.erb +54 -52
- data/web/views/queues.erb +43 -41
- data/web/views/retries.erb +66 -75
- data/web/views/retry.erb +32 -27
- data/web/views/scheduled.erb +58 -54
- data/web/views/scheduled_job_info.erb +1 -1
- metadata +46 -22
- data/web/assets/stylesheets/application-dark.css +0 -147
- data/web/assets/stylesheets/application-rtl.css +0 -163
- data/web/assets/stylesheets/application.css +0 -759
- data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
- data/web/assets/stylesheets/bootstrap.css +0 -5
- data/web/views/_status.erb +0 -4
@@ -1,436 +1,442 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "base64"
|
4
|
+
require "sidekiq/paginator"
|
5
|
+
require "sidekiq/web/helpers"
|
6
|
+
|
3
7
|
module Sidekiq
|
4
|
-
class
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
"
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
8
|
+
class Web
|
9
|
+
class Application
|
10
|
+
extend Router
|
11
|
+
include Router
|
12
|
+
|
13
|
+
REDIS_KEYS = %w[redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human]
|
14
|
+
|
15
|
+
CSP_HEADER_TEMPLATE = [
|
16
|
+
"default-src 'self' https: http:",
|
17
|
+
"child-src 'self'",
|
18
|
+
"connect-src 'self' https: http: wss: ws:",
|
19
|
+
"font-src 'none'",
|
20
|
+
"frame-src 'self'",
|
21
|
+
"img-src 'self' https: http: data:",
|
22
|
+
"manifest-src 'self'",
|
23
|
+
"media-src 'self'",
|
24
|
+
"object-src 'none'",
|
25
|
+
"script-src 'self' 'nonce-!placeholder!'",
|
26
|
+
"style-src 'self' 'nonce-!placeholder!'",
|
27
|
+
"worker-src 'self'",
|
28
|
+
"base-uri 'self'"
|
29
|
+
].join("; ").freeze
|
30
|
+
|
31
|
+
METRICS_PERIODS = {
|
32
|
+
"1h" => 60,
|
33
|
+
"2h" => 120,
|
34
|
+
"4h" => 240,
|
35
|
+
"8h" => 480
|
36
|
+
}
|
33
37
|
|
34
|
-
|
35
|
-
|
36
|
-
|
38
|
+
def initialize(inst)
|
39
|
+
@app = inst
|
40
|
+
end
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
head "/" do
|
43
|
+
# HEAD / is the cheapest heartbeat possible,
|
44
|
+
# it hits Redis to ensure connectivity and returns
|
45
|
+
# the size of the default queue
|
46
|
+
Sidekiq.redis { |c| c.llen("queue:default") }.to_s
|
47
|
+
end
|
41
48
|
|
42
|
-
|
43
|
-
|
44
|
-
|
49
|
+
get "/" do
|
50
|
+
@redis_info = redis_info.select { |k, v| REDIS_KEYS.include? k }
|
51
|
+
days = (url_params("days") || 30).to_i
|
52
|
+
return halt(401) if days < 1 || days > 180
|
45
53
|
|
46
|
-
|
47
|
-
|
48
|
-
|
54
|
+
stats_history = Sidekiq::Stats::History.new(days)
|
55
|
+
@processed_history = stats_history.processed
|
56
|
+
@failed_history = stats_history.failed
|
49
57
|
|
50
|
-
|
51
|
-
|
52
|
-
# it hits Redis to ensure connectivity and returns
|
53
|
-
# the size of the default queue
|
54
|
-
Sidekiq.redis { |c| c.llen("queue:default") }.to_s
|
55
|
-
end
|
58
|
+
erb(:dashboard)
|
59
|
+
end
|
56
60
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
return halt(401) if days < 1 || days > 180
|
61
|
+
get "/metrics" do
|
62
|
+
x = url_params("substr")
|
63
|
+
class_filter = (x.nil? || x == "") ? nil : Regexp.new(Regexp.escape(x), Regexp::IGNORECASE)
|
61
64
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
+
q = Sidekiq::Metrics::Query.new
|
66
|
+
@period = h((url_params("period") || "")[0..1])
|
67
|
+
@periods = METRICS_PERIODS
|
68
|
+
minutes = @periods.fetch(@period, @periods.values.first)
|
69
|
+
@query_result = q.top_jobs(minutes: minutes, class_filter: class_filter)
|
65
70
|
|
66
|
-
|
67
|
-
|
71
|
+
erb(:metrics)
|
72
|
+
end
|
68
73
|
|
69
|
-
|
70
|
-
|
71
|
-
|
74
|
+
get "/metrics/:name" do
|
75
|
+
@name = route_params(:name)
|
76
|
+
@period = h((url_params("period") || "")[0..1])
|
77
|
+
q = Sidekiq::Metrics::Query.new
|
78
|
+
@periods = METRICS_PERIODS
|
79
|
+
minutes = @periods.fetch(@period, @periods.values.first)
|
80
|
+
@query_result = q.for_job(@name, minutes: minutes)
|
81
|
+
erb(:metrics_for_job)
|
82
|
+
end
|
72
83
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
minutes = @periods.fetch(@period, @periods.values.first)
|
77
|
-
@query_result = q.top_jobs(minutes: minutes, class_filter: class_filter)
|
84
|
+
get "/busy" do
|
85
|
+
@count = (url_params("count") || 100).to_i
|
86
|
+
(@current_page, @total_size, @workset) = page_items(workset, url_params("page"), @count)
|
78
87
|
|
79
|
-
|
80
|
-
|
88
|
+
erb(:busy)
|
89
|
+
end
|
81
90
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
q = Sidekiq::Metrics::Query.new
|
86
|
-
@periods = METRICS_PERIODS
|
87
|
-
minutes = @periods.fetch(@period, @periods.values.first)
|
88
|
-
@query_result = q.for_job(@name, minutes: minutes)
|
89
|
-
erb(:metrics_for_job)
|
90
|
-
end
|
91
|
+
post "/busy" do
|
92
|
+
if url_params("identity")
|
93
|
+
pro = Sidekiq::ProcessSet[url_params("identity")]
|
91
94
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
+
pro.quiet! if url_params("quiet")
|
96
|
+
pro.stop! if url_params("stop")
|
97
|
+
else
|
98
|
+
processes.each do |pro|
|
99
|
+
next if pro.embedded?
|
95
100
|
|
96
|
-
|
97
|
-
|
101
|
+
pro.quiet! if url_params("quiet")
|
102
|
+
pro.stop! if url_params("stop")
|
103
|
+
end
|
104
|
+
end
|
98
105
|
|
99
|
-
|
100
|
-
|
101
|
-
pro = Sidekiq::ProcessSet[params["identity"]]
|
106
|
+
redirect "#{root_path}busy"
|
107
|
+
end
|
102
108
|
|
103
|
-
|
104
|
-
|
105
|
-
else
|
106
|
-
processes.each do |pro|
|
107
|
-
next if pro.embedded?
|
109
|
+
get "/queues" do
|
110
|
+
@queues = Sidekiq::Queue.all
|
108
111
|
|
109
|
-
|
110
|
-
pro.stop! if params["stop"]
|
111
|
-
end
|
112
|
+
erb(:queues)
|
112
113
|
end
|
113
114
|
|
114
|
-
|
115
|
-
end
|
115
|
+
QUEUE_NAME = /\A[a-z_:.\-0-9]+\z/i
|
116
116
|
|
117
|
-
|
118
|
-
|
117
|
+
get "/queues/:name" do
|
118
|
+
@name = route_params(:name)
|
119
119
|
|
120
|
-
|
121
|
-
end
|
120
|
+
halt(404) if !@name || @name !~ QUEUE_NAME
|
122
121
|
|
123
|
-
|
122
|
+
@count = (url_params("count") || 25).to_i
|
123
|
+
@queue = Sidekiq::Queue.new(@name)
|
124
|
+
(@current_page, @total_size, @jobs) = page("queue:#{@name}", url_params("page"), @count, reverse: url_params("direction") == "asc")
|
125
|
+
@jobs = @jobs.map { |msg| Sidekiq::JobRecord.new(msg, @name) }
|
124
126
|
|
125
|
-
|
126
|
-
|
127
|
+
erb(:queue)
|
128
|
+
end
|
127
129
|
|
128
|
-
|
130
|
+
post "/queues/:name" do
|
131
|
+
queue = Sidekiq::Queue.new(route_params(:name))
|
129
132
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
133
|
+
if Sidekiq.pro? && url_params("pause")
|
134
|
+
queue.pause!
|
135
|
+
elsif Sidekiq.pro? && url_params("unpause")
|
136
|
+
queue.unpause!
|
137
|
+
else
|
138
|
+
queue.clear
|
139
|
+
end
|
134
140
|
|
135
|
-
|
136
|
-
|
141
|
+
redirect "#{root_path}queues"
|
142
|
+
end
|
137
143
|
|
138
|
-
|
139
|
-
|
144
|
+
post "/queues/:name/delete" do
|
145
|
+
name = route_params(:name)
|
146
|
+
Sidekiq::JobRecord.new(url_params("key_val"), name).delete
|
140
147
|
|
141
|
-
|
142
|
-
queue.pause!
|
143
|
-
elsif Sidekiq.pro? && params["unpause"]
|
144
|
-
queue.unpause!
|
145
|
-
else
|
146
|
-
queue.clear
|
148
|
+
redirect_with_query("#{root_path}queues/#{CGI.escape(name)}")
|
147
149
|
end
|
148
150
|
|
149
|
-
|
150
|
-
|
151
|
+
get "/morgue" do
|
152
|
+
x = url_params("substr")
|
151
153
|
|
152
|
-
|
153
|
-
|
154
|
-
|
154
|
+
if x && x != ""
|
155
|
+
@dead = search(Sidekiq::DeadSet.new, x)
|
156
|
+
else
|
157
|
+
@count = (url_params("count") || 25).to_i
|
158
|
+
(@current_page, @total_size, @dead) = page("dead", url_params("page"), @count, reverse: true)
|
159
|
+
@dead = @dead.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
|
160
|
+
end
|
155
161
|
|
156
|
-
|
157
|
-
|
162
|
+
erb(:morgue)
|
163
|
+
end
|
158
164
|
|
159
|
-
|
160
|
-
|
165
|
+
get "/morgue/:key" do
|
166
|
+
key = route_params(:key)
|
167
|
+
halt(404) unless key
|
161
168
|
|
162
|
-
|
163
|
-
@dead = search(Sidekiq::DeadSet.new, x)
|
164
|
-
else
|
165
|
-
@count = (params["count"] || 25).to_i
|
166
|
-
(@current_page, @total_size, @dead) = page("dead", params["page"], @count, reverse: true)
|
167
|
-
@dead = @dead.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
|
168
|
-
end
|
169
|
+
@dead = Sidekiq::DeadSet.new.fetch(*parse_key(key)).first
|
169
170
|
|
170
|
-
|
171
|
-
|
171
|
+
if @dead.nil?
|
172
|
+
redirect "#{root_path}morgue"
|
173
|
+
else
|
174
|
+
erb(:dead)
|
175
|
+
end
|
176
|
+
end
|
172
177
|
|
173
|
-
|
174
|
-
|
175
|
-
halt(404) unless key
|
178
|
+
post "/morgue" do
|
179
|
+
redirect(request.path) unless url_params("key")
|
176
180
|
|
177
|
-
|
181
|
+
url_params("key").each do |key|
|
182
|
+
job = Sidekiq::DeadSet.new.fetch(*parse_key(key)).first
|
183
|
+
retry_or_delete_or_kill job, request.params if job
|
184
|
+
end
|
178
185
|
|
179
|
-
|
180
|
-
redirect "#{root_path}morgue"
|
181
|
-
else
|
182
|
-
erb(:dead)
|
186
|
+
redirect_with_query("#{root_path}morgue")
|
183
187
|
end
|
184
|
-
end
|
185
188
|
|
186
|
-
|
187
|
-
|
189
|
+
post "/morgue/all/delete" do
|
190
|
+
Sidekiq::DeadSet.new.clear
|
188
191
|
|
189
|
-
|
190
|
-
job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
|
191
|
-
retry_or_delete_or_kill job, params if job
|
192
|
+
redirect "#{root_path}morgue"
|
192
193
|
end
|
193
194
|
|
194
|
-
|
195
|
-
|
195
|
+
post "/morgue/all/retry" do
|
196
|
+
Sidekiq::DeadSet.new.retry_all
|
196
197
|
|
197
|
-
|
198
|
-
|
198
|
+
redirect "#{root_path}morgue"
|
199
|
+
end
|
199
200
|
|
200
|
-
|
201
|
-
|
201
|
+
post "/morgue/:key" do
|
202
|
+
key = route_params(:key)
|
203
|
+
halt(404) unless key
|
202
204
|
|
203
|
-
|
204
|
-
|
205
|
+
job = Sidekiq::DeadSet.new.fetch(*parse_key(key)).first
|
206
|
+
retry_or_delete_or_kill job, request.params if job
|
205
207
|
|
206
|
-
|
207
|
-
|
208
|
+
redirect_with_query("#{root_path}morgue")
|
209
|
+
end
|
208
210
|
|
209
|
-
|
210
|
-
|
211
|
-
halt(404) unless key
|
211
|
+
get "/retries" do
|
212
|
+
x = url_params("substr")
|
212
213
|
|
213
|
-
|
214
|
-
|
214
|
+
if x && x != ""
|
215
|
+
@retries = search(Sidekiq::RetrySet.new, x)
|
216
|
+
else
|
217
|
+
@count = (url_params("count") || 25).to_i
|
218
|
+
(@current_page, @total_size, @retries) = page("retry", url_params("page"), @count)
|
219
|
+
@retries = @retries.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
|
220
|
+
end
|
215
221
|
|
216
|
-
|
217
|
-
|
222
|
+
erb(:retries)
|
223
|
+
end
|
218
224
|
|
219
|
-
|
220
|
-
|
225
|
+
get "/retries/:key" do
|
226
|
+
@retry = Sidekiq::RetrySet.new.fetch(*parse_key(route_params(:key))).first
|
221
227
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
@retries = @retries.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
|
228
|
+
if @retry.nil?
|
229
|
+
redirect "#{root_path}retries"
|
230
|
+
else
|
231
|
+
erb(:retry)
|
232
|
+
end
|
228
233
|
end
|
229
234
|
|
230
|
-
|
231
|
-
|
235
|
+
post "/retries" do
|
236
|
+
route_params("mike")
|
237
|
+
url_params(:mike)
|
238
|
+
redirect(request.path) unless url_params("key")
|
232
239
|
|
233
|
-
|
234
|
-
|
240
|
+
url_params("key").each do |key|
|
241
|
+
job = Sidekiq::RetrySet.new.fetch(*parse_key(key)).first
|
242
|
+
retry_or_delete_or_kill job, request.params if job
|
243
|
+
end
|
235
244
|
|
236
|
-
|
237
|
-
redirect "#{root_path}retries"
|
238
|
-
else
|
239
|
-
erb(:retry)
|
245
|
+
redirect_with_query("#{root_path}retries")
|
240
246
|
end
|
241
|
-
end
|
242
247
|
|
243
|
-
|
244
|
-
|
248
|
+
post "/retries/all/delete" do
|
249
|
+
Sidekiq::RetrySet.new.clear
|
250
|
+
redirect "#{root_path}retries"
|
251
|
+
end
|
245
252
|
|
246
|
-
|
247
|
-
|
248
|
-
|
253
|
+
post "/retries/all/retry" do
|
254
|
+
Sidekiq::RetrySet.new.retry_all
|
255
|
+
redirect "#{root_path}retries"
|
249
256
|
end
|
250
257
|
|
251
|
-
|
252
|
-
|
258
|
+
post "/retries/all/kill" do
|
259
|
+
Sidekiq::RetrySet.new.kill_all
|
260
|
+
redirect "#{root_path}retries"
|
261
|
+
end
|
253
262
|
|
254
|
-
|
255
|
-
|
263
|
+
post "/retries/:key" do
|
264
|
+
job = Sidekiq::RetrySet.new.fetch(*parse_key(route_params(:key))).first
|
256
265
|
|
257
|
-
|
258
|
-
end
|
266
|
+
retry_or_delete_or_kill job, request.params if job
|
259
267
|
|
260
|
-
|
261
|
-
|
268
|
+
redirect_with_query("#{root_path}retries")
|
269
|
+
end
|
262
270
|
|
263
|
-
|
264
|
-
|
271
|
+
get "/scheduled" do
|
272
|
+
x = url_params("substr")
|
265
273
|
|
266
|
-
|
267
|
-
|
274
|
+
if x && x != ""
|
275
|
+
@scheduled = search(Sidekiq::ScheduledSet.new, x)
|
276
|
+
else
|
277
|
+
@count = (url_params("count") || 25).to_i
|
278
|
+
(@current_page, @total_size, @scheduled) = page("schedule", url_params("page"), @count)
|
279
|
+
@scheduled = @scheduled.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
|
280
|
+
end
|
268
281
|
|
269
|
-
|
270
|
-
|
282
|
+
erb(:scheduled)
|
283
|
+
end
|
271
284
|
|
272
|
-
|
273
|
-
|
285
|
+
get "/scheduled/:key" do
|
286
|
+
@job = Sidekiq::ScheduledSet.new.fetch(*parse_key(route_params(:key))).first
|
274
287
|
|
275
|
-
|
288
|
+
if @job.nil?
|
289
|
+
redirect "#{root_path}scheduled"
|
290
|
+
else
|
291
|
+
erb(:scheduled_job_info)
|
292
|
+
end
|
293
|
+
end
|
276
294
|
|
277
|
-
|
278
|
-
|
295
|
+
post "/scheduled" do
|
296
|
+
redirect(request.path) unless url_params("key")
|
279
297
|
|
280
|
-
|
281
|
-
|
298
|
+
url_params("key").each do |key|
|
299
|
+
job = Sidekiq::ScheduledSet.new.fetch(*parse_key(key)).first
|
300
|
+
delete_or_add_queue job, request.params if job
|
301
|
+
end
|
282
302
|
|
283
|
-
|
284
|
-
@scheduled = search(Sidekiq::ScheduledSet.new, x)
|
285
|
-
else
|
286
|
-
@count = (params["count"] || 25).to_i
|
287
|
-
(@current_page, @total_size, @scheduled) = page("schedule", params["page"], @count)
|
288
|
-
@scheduled = @scheduled.map { |msg, score| Sidekiq::SortedEntry.new(nil, score, msg) }
|
303
|
+
redirect_with_query("#{root_path}scheduled")
|
289
304
|
end
|
290
305
|
|
291
|
-
|
292
|
-
|
306
|
+
post "/scheduled/:key" do
|
307
|
+
key = route_params(:key)
|
308
|
+
halt(404) unless key
|
293
309
|
|
294
|
-
|
295
|
-
|
310
|
+
job = Sidekiq::ScheduledSet.new.fetch(*parse_key(key)).first
|
311
|
+
delete_or_add_queue job, request.params if job
|
296
312
|
|
297
|
-
|
298
|
-
redirect "#{root_path}scheduled"
|
299
|
-
else
|
300
|
-
erb(:scheduled_job_info)
|
313
|
+
redirect_with_query("#{root_path}scheduled")
|
301
314
|
end
|
302
|
-
end
|
303
315
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
params["key"].each do |key|
|
308
|
-
job = Sidekiq::ScheduledSet.new.fetch(*parse_params(key)).first
|
309
|
-
delete_or_add_queue job, params if job
|
316
|
+
get "/dashboard/stats" do
|
317
|
+
redirect "#{root_path}stats"
|
310
318
|
end
|
311
319
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
320
|
+
get "/stats" do
|
321
|
+
sidekiq_stats = Sidekiq::Stats.new
|
322
|
+
redis_stats = redis_info.select { |k, v| REDIS_KEYS.include? k }
|
323
|
+
json(
|
324
|
+
sidekiq: {
|
325
|
+
processed: sidekiq_stats.processed,
|
326
|
+
failed: sidekiq_stats.failed,
|
327
|
+
busy: sidekiq_stats.workers_size,
|
328
|
+
processes: sidekiq_stats.processes_size,
|
329
|
+
enqueued: sidekiq_stats.enqueued,
|
330
|
+
scheduled: sidekiq_stats.scheduled_size,
|
331
|
+
retries: sidekiq_stats.retry_size,
|
332
|
+
dead: sidekiq_stats.dead_size,
|
333
|
+
default_latency: sidekiq_stats.default_queue_latency
|
334
|
+
},
|
335
|
+
redis: redis_stats,
|
336
|
+
server_utc_time: server_utc_time
|
337
|
+
)
|
338
|
+
end
|
328
339
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
json(
|
333
|
-
sidekiq: {
|
334
|
-
processed: sidekiq_stats.processed,
|
335
|
-
failed: sidekiq_stats.failed,
|
336
|
-
busy: sidekiq_stats.workers_size,
|
337
|
-
processes: sidekiq_stats.processes_size,
|
338
|
-
enqueued: sidekiq_stats.enqueued,
|
339
|
-
scheduled: sidekiq_stats.scheduled_size,
|
340
|
-
retries: sidekiq_stats.retry_size,
|
341
|
-
dead: sidekiq_stats.dead_size,
|
342
|
-
default_latency: sidekiq_stats.default_queue_latency
|
343
|
-
},
|
344
|
-
redis: redis_stats,
|
345
|
-
server_utc_time: server_utc_time
|
346
|
-
)
|
347
|
-
end
|
340
|
+
get "/stats/queues" do
|
341
|
+
json Sidekiq::Stats.new.queues
|
342
|
+
end
|
348
343
|
|
349
|
-
|
350
|
-
|
351
|
-
|
344
|
+
get "/profiles" do
|
345
|
+
erb(:profiles)
|
346
|
+
end
|
352
347
|
|
353
|
-
|
354
|
-
|
348
|
+
get "/profiles/:key" do
|
349
|
+
store = config[:profile_store_url]
|
350
|
+
return redirect_to "#{root_path}profiles" unless store
|
351
|
+
|
352
|
+
key = route_params(:key)
|
353
|
+
sid = Sidekiq.redis { |c| c.hget(key, "sid") }
|
354
|
+
|
355
|
+
unless sid
|
356
|
+
require "net/http"
|
357
|
+
data = Sidekiq.redis { |c| c.hget(key, "data") }
|
358
|
+
resp = Net::HTTP.post(URI(store),
|
359
|
+
data,
|
360
|
+
{"Accept" => "application/vnd.firefox-profiler+json;version=1.0",
|
361
|
+
"User-Agent" => "Sidekiq #{Sidekiq::VERSION} job profiler"})
|
362
|
+
# https://raw.githubusercontent.com/firefox-devtools/profiler-server/master/tools/decode_jwt_payload.py
|
363
|
+
sid = Sidekiq.load_json(Base64.decode64(resp.body.split(".")[1]))["profileToken"]
|
364
|
+
Sidekiq.redis { |c| c.hset(key, "sid", sid) }
|
365
|
+
end
|
366
|
+
url = config[:profile_view_url] % sid
|
367
|
+
redirect_to url
|
368
|
+
end
|
355
369
|
|
356
|
-
|
357
|
-
|
358
|
-
|
370
|
+
get "/profiles/:key/data" do
|
371
|
+
key = route_params(:key)
|
372
|
+
data = Sidekiq.redis { |c| c.hget(key, "data") }
|
359
373
|
|
360
|
-
|
374
|
+
[200, {
|
375
|
+
"content-type" => "application/json",
|
376
|
+
"content-encoding" => "gzip",
|
377
|
+
# allow Firefox Profiler's XHR to fetch this profile data
|
378
|
+
"access-control-allow-origin" => "*"
|
379
|
+
}, [data]]
|
380
|
+
end
|
361
381
|
|
362
|
-
|
363
|
-
|
382
|
+
post "/change_locale" do
|
383
|
+
locale = url_params("locale")
|
364
384
|
|
365
|
-
|
366
|
-
|
367
|
-
return [404, {Rack::CONTENT_TYPE => "text/plain", Web::X_CASCADE => "pass"}, ["Not Found"]] unless action
|
368
|
-
|
369
|
-
app = @klass
|
370
|
-
resp = catch(:halt) do
|
371
|
-
self.class.run_befores(app, action)
|
372
|
-
action.instance_exec env, &action.block
|
373
|
-
ensure
|
374
|
-
self.class.run_afters(app, action)
|
375
|
-
end
|
376
|
-
|
377
|
-
case resp
|
378
|
-
when Array
|
379
|
-
# redirects go here
|
380
|
-
resp
|
381
|
-
else
|
382
|
-
# rendered content goes here
|
383
|
-
headers = {
|
384
|
-
Rack::CONTENT_TYPE => "text/html",
|
385
|
-
Rack::CACHE_CONTROL => "private, no-store",
|
386
|
-
Web::CONTENT_LANGUAGE => action.locale,
|
387
|
-
Web::CONTENT_SECURITY_POLICY => process_csp(env, CSP_HEADER_TEMPLATE),
|
388
|
-
Web::X_CONTENT_TYPE_OPTIONS => "nosniff"
|
385
|
+
match = available_locales.find { |available|
|
386
|
+
locale == available
|
389
387
|
}
|
390
|
-
# we'll let Rack calculate Content-Length for us.
|
391
|
-
[200, headers, [resp]]
|
392
|
-
end
|
393
|
-
end
|
394
388
|
|
395
|
-
|
396
|
-
input.gsub("!placeholder!", env[:csp_nonce])
|
397
|
-
end
|
389
|
+
session[:locale] = match if match
|
398
390
|
|
399
|
-
|
400
|
-
if block
|
401
|
-
WebAction.class_eval(&block)
|
402
|
-
else
|
403
|
-
WebAction.send(:include, mod)
|
391
|
+
reload_page
|
404
392
|
end
|
405
|
-
end
|
406
393
|
|
407
|
-
|
408
|
-
|
409
|
-
|
394
|
+
def redis(&)
|
395
|
+
Thread.current[:sidekiq_redis_pool].with(&)
|
396
|
+
end
|
410
397
|
|
411
|
-
|
412
|
-
|
413
|
-
|
398
|
+
def call(env)
|
399
|
+
action = match(env)
|
400
|
+
return [404, {"content-type" => "text/plain", "x-cascade" => "pass"}, ["Not Found"]] unless action
|
414
401
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
end
|
402
|
+
resp = catch(:halt) do
|
403
|
+
Thread.current[:sidekiq_redis_pool] = env[:redis_pool]
|
404
|
+
action.instance_exec env, &action.block
|
405
|
+
ensure
|
406
|
+
Thread.current[:sidekiq_redis_pool] = nil
|
407
|
+
end
|
422
408
|
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
409
|
+
case resp
|
410
|
+
when Array
|
411
|
+
# redirects go here
|
412
|
+
resp
|
413
|
+
else
|
414
|
+
# rendered content goes here
|
415
|
+
headers = {
|
416
|
+
"content-type" => "text/html",
|
417
|
+
"cache-control" => "private, no-store",
|
418
|
+
"content-language" => action.locale,
|
419
|
+
"content-security-policy" => process_csp(env, CSP_HEADER_TEMPLATE),
|
420
|
+
"x-content-type-options" => "nosniff"
|
421
|
+
}
|
422
|
+
# we'll let Rack calculate Content-Length for us.
|
423
|
+
[200, headers, [resp]]
|
424
|
+
end
|
425
|
+
end
|
427
426
|
|
428
|
-
|
429
|
-
|
430
|
-
|
427
|
+
def process_csp(env, input)
|
428
|
+
input.gsub("!placeholder!", env[:csp_nonce])
|
429
|
+
end
|
431
430
|
|
432
|
-
|
433
|
-
|
431
|
+
# Used by extensions to add helper methods accessible to
|
432
|
+
# any defined endpoints in Application. Careful with generic
|
433
|
+
# method naming as there's no namespacing so collisions are
|
434
|
+
# possible.
|
435
|
+
def self.helpers(mod)
|
436
|
+
Sidekiq::Web::Action.send(:include, mod)
|
437
|
+
end
|
438
|
+
helpers WebHelpers
|
439
|
+
helpers Paginator
|
434
440
|
end
|
435
441
|
end
|
436
442
|
end
|