sidekiq 6.0.0 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

@@ -12,10 +12,10 @@ module Sidekiq
12
12
 
13
13
  Sidekiq.redis do |conn|
14
14
  type = conn.type(key)
15
+ rev = opts && opts[:reverse]
15
16
 
16
17
  case type
17
18
  when "zset"
18
- rev = opts && opts[:reverse]
19
19
  total_size, items = conn.multi {
20
20
  conn.zcard(key)
21
21
  if rev
@@ -28,8 +28,13 @@ module Sidekiq
28
28
  when "list"
29
29
  total_size, items = conn.multi {
30
30
  conn.llen(key)
31
- conn.lrange(key, starting, ending)
31
+ if rev
32
+ conn.lrange(key, -ending - 1, -starting - 1)
33
+ else
34
+ conn.lrange(key, starting, ending)
35
+ end
32
36
  }
37
+ items.reverse! if rev
33
38
  [current_page, total_size, items]
34
39
  when "none"
35
40
  [1, 0, []]
@@ -115,9 +115,9 @@ module Sidekiq
115
115
  # since middleware can mutate the job hash
116
116
  # we clone here so we report the original
117
117
  # job structure to the Web UI
118
- pristine = cloned(job_hash)
118
+ pristine = json_clone(job_hash)
119
119
 
120
- @job_logger.with_job_hash_context(job_hash) do
120
+ @job_logger.prepare(job_hash) do
121
121
  @retrier.global(pristine, queue) do
122
122
  @job_logger.call(job_hash, queue) do
123
123
  stats(pristine, queue) do
@@ -158,7 +158,7 @@ module Sidekiq
158
158
  begin
159
159
  dispatch(job_hash, queue) do |worker|
160
160
  Sidekiq.server_middleware.invoke(worker, job_hash, queue) do
161
- execute_job(worker, cloned(job_hash["args"]))
161
+ execute_job(worker, job_hash["args"])
162
162
  end
163
163
  end
164
164
  ack = true
@@ -261,22 +261,42 @@ module Sidekiq
261
261
  end
262
262
  end
263
263
 
264
- # Deep clone the arguments passed to the worker so that if
265
- # the job fails, what is pushed back onto Redis hasn't
266
- # been mutated by the worker.
267
- def cloned(thing)
268
- Marshal.load(Marshal.dump(thing))
269
- end
270
-
271
264
  def constantize(str)
265
+ return Object.const_get(str) unless str.include?("::")
266
+
272
267
  names = str.split("::")
273
268
  names.shift if names.empty? || names.first.empty?
274
269
 
275
270
  names.inject(Object) do |constant, name|
276
271
  # the false flag limits search for name to under the constant namespace
277
272
  # which mimics Rails' behaviour
278
- constant.const_defined?(name, false) ? constant.const_get(name, false) : constant.const_missing(name)
273
+ constant.const_get(name, false)
274
+ end
275
+ end
276
+
277
+ # Deep clone the arguments passed to the worker so that if
278
+ # the job fails, what is pushed back onto Redis hasn't
279
+ # been mutated by the worker.
280
+ def json_clone(obj)
281
+ if Integer === obj || Float === obj || TrueClass === obj || FalseClass === obj || NilClass === obj
282
+ return obj
283
+ elsif String === obj
284
+ return obj.dup
285
+ elsif Array === obj
286
+ duped = Array.new(obj.size)
287
+ obj.each_with_index do |value, index|
288
+ duped[index] = json_clone(value)
289
+ end
290
+ elsif Hash === obj
291
+ duped = obj.dup
292
+ duped.each_pair do |key, value|
293
+ duped[key] = json_clone(value)
294
+ end
295
+ else
296
+ duped = obj.dup
279
297
  end
298
+
299
+ duped
280
300
  end
281
301
  end
282
302
  end
@@ -14,18 +14,19 @@ module Sidekiq
14
14
  # Just check Redis for the set of jobs with a timestamp before now.
15
15
  Sidekiq.redis do |conn|
16
16
  sorted_sets.each do |sorted_set|
17
- # Get the next item in the queue if it's score (time to execute) is <= now.
18
- # We need to go through the list one at a time to reduce the risk of something
19
- # going wrong between the time jobs are popped from the scheduled queue and when
20
- # they are pushed onto a work queue and losing the jobs.
21
- while (job = conn.zrangebyscore(sorted_set, "-inf", now, limit: [0, 1]).first)
22
-
23
- # Pop item off the queue and add it to the work queue. If the job can't be popped from
24
- # the queue, it's because another process already popped it so we can move on to the
25
- # next one.
26
- if conn.zrem(sorted_set, job)
27
- Sidekiq::Client.push(Sidekiq.load_json(job))
28
- Sidekiq.logger.debug { "enqueued #{sorted_set}: #{job}" }
17
+ # Get next items in the queue with scores (time to execute) <= now.
18
+ until (jobs = conn.zrangebyscore(sorted_set, "-inf", now, limit: [0, 100])).empty?
19
+ # We need to go through the list one at a time to reduce the risk of something
20
+ # going wrong between the time jobs are popped from the scheduled queue and when
21
+ # they are pushed onto a work queue and losing the jobs.
22
+ jobs.each do |job|
23
+ # Pop item off the queue and add it to the work queue. If the job can't be popped from
24
+ # the queue, it's because another process already popped it so we can move on to the
25
+ # next one.
26
+ if conn.zrem(sorted_set, job)
27
+ Sidekiq::Client.push(Sidekiq.load_json(job))
28
+ Sidekiq.logger.debug { "enqueued #{sorted_set}: #{job}" }
29
+ end
29
30
  end
30
31
  end
31
32
  end
@@ -323,6 +323,18 @@ module Sidekiq
323
323
  end
324
324
  end
325
325
  end
326
+
327
+ module TestingExtensions
328
+ def jobs_for(klass)
329
+ jobs.select do |job|
330
+ marshalled = job["args"][0]
331
+ marshalled.index(klass.to_s) && YAML.load(marshalled)[0] == klass
332
+ end
333
+ end
334
+ end
335
+
336
+ Sidekiq::Extensions::DelayedMailer.extend(TestingExtensions) if defined?(Sidekiq::Extensions::DelayedMailer)
337
+ Sidekiq::Extensions::DelayedModel.extend(TestingExtensions) if defined?(Sidekiq::Extensions::DelayedModel)
326
338
  end
327
339
 
328
340
  if defined?(::Rails) && Rails.respond_to?(:env) && !Rails.env.test?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "6.0.0"
4
+ VERSION = "6.0.1"
5
5
  end
@@ -84,7 +84,7 @@ module Sidekiq
84
84
 
85
85
  @count = (params["count"] || 25).to_i
86
86
  @queue = Sidekiq::Queue.new(@name)
87
- (@current_page, @total_size, @messages) = page("queue:#{@name}", params["page"], @count)
87
+ (@current_page, @total_size, @messages) = page("queue:#{@name}", params["page"], @count, reverse: params["direction"] == "asc")
88
88
  @messages = @messages.map { |msg| Sidekiq::Job.new(msg, @name) }
89
89
 
90
90
  erb(:queue)
@@ -283,17 +283,13 @@ module Sidekiq
283
283
  action = self.class.match(env)
284
284
  return [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found"]] unless action
285
285
 
286
- resp = catch(:halt) {
287
- app = @klass
286
+ app = @klass
287
+ resp = catch(:halt) do # rubocop:disable Standard/SemanticBlocks
288
288
  self.class.run_befores(app, action)
289
- begin
290
- resp = action.instance_exec env, &action.block
291
- ensure
292
- self.class.run_afters(app, action)
293
- end
294
-
295
- resp
296
- }
289
+ action.instance_exec env, &action.block
290
+ ensure
291
+ self.class.run_afters(app, action)
292
+ end
297
293
 
298
294
  resp = case resp
299
295
  when Array
@@ -65,7 +65,10 @@ module Sidekiq
65
65
 
66
66
  def poll_path
67
67
  if current_path != "" && params["poll"]
68
- root_path + current_path
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
69
72
  else
70
73
  ""
71
74
  end
@@ -112,6 +115,13 @@ module Sidekiq
112
115
  end
113
116
  end
114
117
 
118
+ # within is used by Sidekiq Pro
119
+ def display_tags(job, within = nil)
120
+ job.tags.map { |tag|
121
+ "<span class='jobtag label label-info'>#{::Rack::Utils.escape_html(tag)}</span>"
122
+ }.join(" ")
123
+ end
124
+
115
125
  # mperham/sidekiq#3243
116
126
  def unfiltered?
117
127
  yield unless env["PATH_INFO"].start_with?("/filter/")
@@ -130,6 +140,10 @@ module Sidekiq
130
140
  end
131
141
  end
132
142
 
143
+ def sort_direction_label
144
+ params[:direction] == "asc" ? "&uarr;" : "&darr;"
145
+ end
146
+
133
147
  def workers
134
148
  @workers ||= Sidekiq::Workers.new
135
149
  end
@@ -189,7 +203,7 @@ module Sidekiq
189
203
  [score.to_f, jid]
190
204
  end
191
205
 
192
- SAFE_QPARAMS = %w[page poll]
206
+ SAFE_QPARAMS = %w[page poll direction]
193
207
 
194
208
  # Merge options with current params, filter safe params, and stringify to query string
195
209
  def qparams(options)
@@ -198,7 +212,11 @@ module Sidekiq
198
212
  options[key.to_s] = options.delete(key)
199
213
  end
200
214
 
201
- params.merge(options).map { |key, value|
215
+ to_query_string(params.merge(options))
216
+ end
217
+
218
+ def to_query_string(params)
219
+ params.map { |key, value|
202
220
  SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
203
221
  }.compact.join("&")
204
222
  end
@@ -238,7 +256,7 @@ module Sidekiq
238
256
  queue class args retry_count retried_at failed_at
239
257
  jid error_message error_class backtrace
240
258
  error_backtrace enqueued_at retry wrapped
241
- created_at
259
+ created_at tags
242
260
  ])
243
261
 
244
262
  def retry_extra_items(retry_job)
@@ -171,9 +171,9 @@ module Sidekiq
171
171
  now = Time.now.to_f
172
172
  ts = (int < 1_000_000_000 ? now + int : int)
173
173
 
174
- payload = @opts.merge("class" => @klass, "args" => args, "at" => ts)
174
+ payload = @opts.merge("class" => @klass, "args" => args)
175
175
  # Optimization to enqueue something now that is scheduled to go out now or in the past
176
- payload.delete("at") if ts <= now
176
+ payload["at"] = ts if ts > now
177
177
  @klass.client_push(payload)
178
178
  end
179
179
  alias_method :perform_at, :perform_in
@@ -207,10 +207,10 @@ module Sidekiq
207
207
  now = Time.now.to_f
208
208
  ts = (int < 1_000_000_000 ? now + int : int)
209
209
 
210
- item = {"class" => self, "args" => args, "at" => ts}
210
+ item = {"class" => self, "args" => args}
211
211
 
212
212
  # Optimization to enqueue something now that is scheduled to go out now or in the past
213
- item.delete("at") if ts <= now
213
+ item["at"] = ts if ts > now
214
214
 
215
215
  client_push(item)
216
216
  end
@@ -8,7 +8,7 @@ Gem::Specification.new do |gem|
8
8
  gem.homepage = "http://sidekiq.org"
9
9
  gem.license = "LGPL-3.0"
10
10
 
11
- gem.executables = ["sidekiq"]
11
+ gem.executables = ["sidekiq", "sidekiqmon"]
12
12
  gem.files = `git ls-files | grep -Ev '^(test|myapp|examples)'`.split("\n")
13
13
  gem.name = "sidekiq"
14
14
  gem.version = Sidekiq::VERSION
@@ -27,7 +27,7 @@ var realtimeGraph = function(updatePath) {
27
27
  renderer: 'line',
28
28
  interpolation: 'linear',
29
29
 
30
- series: new Rickshaw.Series.FixedDuration([{ name: graphElement.dataset.failedLabel, color: '#B1003E' }, { name: graphElement.dataset.processedLabel, color: '#006f68' }], undefined, {
30
+ series: new Rickshaw.Series.FixedDuration([{ name: graphElement.dataset.failedLabel, color: '#af0014' }, { name: graphElement.dataset.processedLabel, color: '#006f68' }], undefined, {
31
31
  timeInterval: timeInterval,
32
32
  maxDataPoints: 100,
33
33
  })
@@ -125,7 +125,7 @@ var historyGraph = function() {
125
125
  interpolation: 'linear',
126
126
  series: [
127
127
  {
128
- color: "#B1003E",
128
+ color: "#af0014",
129
129
  data: failed,
130
130
  name: graphElement.dataset.failedLabel
131
131
  }, {
@@ -0,0 +1,125 @@
1
+ @media (prefers-color-scheme: dark) {
2
+
3
+ body {
4
+ background-color: #000;
5
+ color: #ccc;
6
+ }
7
+
8
+ a,
9
+ .title,
10
+ .summary_bar ul .count,
11
+ .navbar .navbar-brand {
12
+ color: #af0014;
13
+ }
14
+
15
+ .navbar .navbar-brand:hover {
16
+ color: #ccc;
17
+ }
18
+
19
+ .navbar .navbar-brand .status {
20
+ color: #ccc;
21
+ }
22
+
23
+ .navbar-inverse {
24
+ background-color: #000;
25
+ border-color: #333;
26
+ }
27
+
28
+ table.table-white {
29
+ background-color: #111;
30
+ }
31
+
32
+ .table-striped > tbody > tr:nth-of-type(odd) {
33
+ background-color: #222;
34
+ }
35
+
36
+ .table-bordered,
37
+ .table-bordered > tbody > tr > td,
38
+ .table-bordered > tbody > tr > th,
39
+ .table-bordered > tfoot > tr > td,
40
+ .table-bordered > tfoot > tr > th,
41
+ .table-bordered > thead > tr > td,
42
+ .table-bordered > thead > tr > th {
43
+ border: 1px solid #333;
44
+ }
45
+
46
+ .table-hover > tbody > tr:hover {
47
+ background-color: #333;
48
+ }
49
+
50
+ .alert {
51
+ border: none;
52
+ color: #ccc;
53
+ }
54
+
55
+ .alert-success {
56
+ background-color: #000;
57
+ }
58
+
59
+ a:link,
60
+ a:active,
61
+ a:hover,
62
+ a:visited {
63
+ color: #63798c;
64
+ }
65
+
66
+ a.btn {
67
+ color: #000;
68
+ }
69
+
70
+ .summary_bar .summary {
71
+ background-color: #000;
72
+ border: 1px solid #333;
73
+ }
74
+
75
+ .navbar-default {
76
+ background-color: #000;
77
+ border-color: #3d3d3d;
78
+ }
79
+
80
+ .navbar-default .navbar-nav > .active > a,
81
+ .navbar-default .navbar-nav > .active > a:focus,
82
+ .navbar-default .navbar-nav > .active > a:hover {
83
+ color: #ccc;
84
+ background-color: #282828;
85
+ }
86
+
87
+ .navbar-default .navbar-nav > li > a:hover {
88
+ color: #ccc;
89
+ }
90
+
91
+ .pagination > li > a,
92
+ .pagination > li > a:hover,
93
+ .pagination > li > span {
94
+ color: #ccc;
95
+ background-color: #282828;
96
+ border-color: #353535;
97
+ }
98
+ .pagination > .disabled > a,
99
+ .pagination > .disabled > a:focus,
100
+ .pagination > .disabled > a:hover,
101
+ .pagination > .disabled > span,
102
+ .pagination > .disabled > span:focus,
103
+ .pagination > .disabled > span:hover {
104
+ color: #a5a5a5;
105
+ background-color: #282828;
106
+ border-color: #353535;
107
+ }
108
+
109
+ .stat {
110
+ border: 1px solid rgba(255, 255, 255, 0.1);
111
+ }
112
+
113
+ #live-poll {
114
+ color: #ccc;
115
+ }
116
+
117
+ .btn-warn {
118
+ color: #333;
119
+ }
120
+
121
+ .rickshaw_graph .y_ticks.glow text {
122
+ fill: #ccc;
123
+ color: #ccc;
124
+ }
125
+ }
@@ -201,6 +201,15 @@ td form {
201
201
  padding: 0;
202
202
  }
203
203
 
204
+ .jobtag a {
205
+ color: white;
206
+ }
207
+
208
+ .jobtag a:hover {
209
+ color: white;
210
+ text-decoration: underline;
211
+ }
212
+
204
213
  table .table-checkbox label {
205
214
  height: 100%;
206
215
  width: 100%;