sidekiq 7.1.1 → 7.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5a020fb72399ae30db922c2c39fe63932c6a0f0ca2094c39d809df18b448331
4
- data.tar.gz: 50e6dfc4ab7db79279463fc70cc52226e7fe1a58188b6a0f3f2a774b76428a76
3
+ metadata.gz: 0a6064918f9c33be1d21f890b9cff080969fb44a916fe67ccb36958fe0a3b8f3
4
+ data.tar.gz: baf268f21f27e0dac2fc287f247910afe493e4dcaee21aa769ccebbed03e7699
5
5
  SHA512:
6
- metadata.gz: 169a24acd3011faf7d449f7fbf08d2e62125f0802791afdac7db992950ffdf53e768bc6549ccdd46e1868b07c5b367cb574be613704b7da658927d21b75bb320
7
- data.tar.gz: 165d6d1fb808f53731ff460c992bc5103bb0f66e619a9aed22561aee9c21e09fc59d8f1f388e52acde68eefdf81413953bd1dda6bf32ded0dbd9980a247a12e4
6
+ metadata.gz: c8f4e3caaeab143f20fdd592dc7939f09a302642867bddb4fcd02cbbaa0e734d7685ee3ad02543d3bb3ba6cbedaf2cbab3577a849c03d2f351aa7918d6eab73f
7
+ data.tar.gz: 1cf71898600afb872ee717be6496da7a146aa8e2f528b6c73e3630a64f71869bc23bfa2d7130f021c7f5e12b473ed3b537cebb4a8fedcfbe517af48dde5bbea6
data/Changes.md CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  [Sidekiq Changes](https://github.com/sidekiq/sidekiq/blob/main/Changes.md) | [Sidekiq Pro Changes](https://github.com/sidekiq/sidekiq/blob/main/Pro-Changes.md) | [Sidekiq Enterprise Changes](https://github.com/sidekiq/sidekiq/blob/main/Ent-Changes.md)
4
4
 
5
+ 7.1.4
6
+ ----------
7
+
8
+ - Fix empty `retry_for` logic [#6035]
9
+
10
+ 7.1.3
11
+ ----------
12
+
13
+ - Add `sidekiq_options retry_for: 48.hours` to allow time-based retry windows [#6029]
14
+ - Support sidekiq_retry_in and sidekiq_retries_exhausted_block in ActiveJobs (#5994)
15
+ - Lowercase all Rack headers for Rack 3.0 [#5951]
16
+ - Validate Sidekiq::Web page refresh delay to avoid potential DoS,
17
+ CVE-2023-26141, thanks for reporting Keegan!
18
+
19
+ 7.1.2
20
+ ----------
21
+
22
+ - Mark Web UI assets as private so CDNs won't cache them [#5936]
23
+ - Fix stackoverflow when using Oj and the JSON log formatter [#5920]
24
+ - Remove spurious `enqueued_at` from scheduled ActiveJobs [#5937]
25
+
5
26
  7.1.1
6
27
  ----------
7
28
 
@@ -66,6 +66,7 @@ module Sidekiq
66
66
  # args - an array of simple arguments to the perform method, must be JSON-serializable
67
67
  # at - timestamp to schedule the job (optional), must be Numeric (e.g. Time.now.to_f)
68
68
  # retry - whether to retry this job if it fails, default true or an integer number of retries
69
+ # retry_for - relative amount of time to retry this job if it fails, default nil
69
70
  # backtrace - whether to save any error backtrace, default false
70
71
  #
71
72
  # If class is set to the class name, the jobs' options will be based on Sidekiq's default
@@ -246,6 +247,8 @@ module Sidekiq
246
247
  if payloads.first.key?("at")
247
248
  conn.zadd("schedule", payloads.flat_map { |hash|
248
249
  at = hash.delete("at").to_s
250
+ # ActiveJob sets this but the job has not been enqueued yet
251
+ hash.delete("enqueued_at")
249
252
  [at, Sidekiq.dump_json(hash)]
250
253
  })
251
254
  else
@@ -56,6 +56,10 @@ module Sidekiq
56
56
  def_delegators :@options, :[], :[]=, :fetch, :key?, :has_key?, :merge!
57
57
  attr_reader :capsules
58
58
 
59
+ def to_json(*)
60
+ Sidekiq.dump_json(@options)
61
+ end
62
+
59
63
  # LEGACY: edits the default capsule
60
64
  # config.concurrency = 5
61
65
  def concurrency=(val)
@@ -263,7 +267,7 @@ module Sidekiq
263
267
  ctx[:_config] = self
264
268
  @options[:error_handlers].each do |handler|
265
269
  handler.call(ex, ctx)
266
- rescue => e
270
+ rescue Exception => e
267
271
  l = logger
268
272
  l.error "!!! ERROR HANDLER THREW AN ERROR !!!"
269
273
  l.error e
@@ -170,9 +170,11 @@ module Sidekiq
170
170
  msg["error_backtrace"] = compress_backtrace(lines)
171
171
  end
172
172
 
173
- # Goodbye dear message, you (re)tried your best I'm sure.
174
173
  return retries_exhausted(jobinst, msg, exception) if count >= max_retry_attempts
175
174
 
175
+ rf = msg["retry_for"]
176
+ return retries_exhausted(jobinst, msg, exception) if rf && ((msg["failed_at"] + rf) < Time.now.to_f)
177
+
176
178
  strategy, delay = delay_for(jobinst, count, exception, msg)
177
179
  case strategy
178
180
  when :discard
@@ -197,7 +199,14 @@ module Sidekiq
197
199
  # sidekiq_retry_in can return two different things:
198
200
  # 1. When to retry next, as an integer of seconds
199
201
  # 2. A symbol which re-routes the job elsewhere, e.g. :discard, :kill, :default
200
- jobinst&.sidekiq_retry_in_block&.call(count, exception, msg)
202
+ block = jobinst&.sidekiq_retry_in_block
203
+
204
+ # the sidekiq_retry_in_block can be defined in a wrapped class (ActiveJob for instance)
205
+ unless msg["wrapped"].nil?
206
+ wrapped = Object.const_get(msg["wrapped"])
207
+ block = wrapped.respond_to?(:sidekiq_retry_in_block) ? wrapped.sidekiq_retry_in_block : nil
208
+ end
209
+ block&.call(count, exception, msg)
201
210
  rescue Exception => e
202
211
  handle_exception(e, {context: "Failure scheduling retry using the defined `sidekiq_retry_in` in #{jobinst.class.name}, falling back to default"})
203
212
  nil
@@ -219,6 +228,12 @@ module Sidekiq
219
228
  def retries_exhausted(jobinst, msg, exception)
220
229
  begin
221
230
  block = jobinst&.sidekiq_retries_exhausted_block
231
+
232
+ # the sidekiq_retries_exhausted_block can be defined in a wrapped class (ActiveJob for instance)
233
+ unless msg["wrapped"].nil?
234
+ wrapped = Object.const_get(msg["wrapped"])
235
+ block = wrapped.respond_to?(:sidekiq_retries_exhausted_block) ? wrapped.sidekiq_retries_exhausted_block : nil
236
+ end
222
237
  block&.call(msg, exception)
223
238
  rescue => e
224
239
  handle_exception(e, {context: "Error calling retries_exhausted", job: msg})
@@ -13,6 +13,7 @@ module Sidekiq
13
13
  raise(ArgumentError, "Job class must be either a Class or String representation of the class name: `#{item}`") unless item["class"].is_a?(Class) || item["class"].is_a?(String)
14
14
  raise(ArgumentError, "Job 'at' must be a Numeric timestamp: `#{item}`") if item.key?("at") && !item["at"].is_a?(Numeric)
15
15
  raise(ArgumentError, "Job tags must be an Array: `#{item}`") if item["tags"] && !item["tags"].is_a?(Array)
16
+ raise(ArgumentError, "retry_for must be a relative amount of time, e.g. 48.hours `#{item}`") if item["retry_for"] && item["retry_for"] > 1_000_000_000
16
17
  end
17
18
 
18
19
  def verify_json(item)
@@ -54,6 +55,7 @@ module Sidekiq
54
55
  item["jid"] ||= SecureRandom.hex(12)
55
56
  item["class"] = item["class"].to_s
56
57
  item["queue"] = item["queue"].to_s
58
+ item["retry_for"] = item["retry_for"].to_i if item["retry_for"]
57
59
  item["created_at"] ||= Time.now.to_f
58
60
  item
59
61
  end
@@ -73,7 +73,7 @@ module Sidekiq
73
73
  def fetch(conn, now = Time.now)
74
74
  window = now.utc.strftime("%d-%H:%-M")
75
75
  key = "#{@klass}-#{window}"
76
- conn.bitfield(key, *FETCH)
76
+ conn.bitfield_ro(key, *FETCH)
77
77
  end
78
78
 
79
79
  def persist(conn, now = Time.now)
@@ -148,6 +148,8 @@ module Sidekiq
148
148
 
149
149
  IGNORE_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :never}
150
150
  private_constant :IGNORE_SHUTDOWN_INTERRUPTS
151
+ ALLOW_SHUTDOWN_INTERRUPTS = {Sidekiq::Shutdown => :immediate}
152
+ private_constant :ALLOW_SHUTDOWN_INTERRUPTS
151
153
 
152
154
  def process(uow)
153
155
  jobstr = uow.job
@@ -171,36 +173,35 @@ module Sidekiq
171
173
  end
172
174
 
173
175
  ack = false
174
- begin
175
- dispatch(job_hash, queue, jobstr) do |inst|
176
- config.server_middleware.invoke(inst, job_hash, queue) do
177
- execute_job(inst, job_hash["args"])
176
+ Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
177
+ Thread.handle_interrupt(ALLOW_SHUTDOWN_INTERRUPTS) do
178
+ dispatch(job_hash, queue, jobstr) do |inst|
179
+ config.server_middleware.invoke(inst, job_hash, queue) do
180
+ execute_job(inst, job_hash["args"])
181
+ end
178
182
  end
183
+ ack = true
184
+ rescue Sidekiq::Shutdown
185
+ # Had to force kill this job because it didn't finish
186
+ # within the timeout. Don't acknowledge the work since
187
+ # we didn't properly finish it.
188
+ rescue Sidekiq::JobRetry::Handled => h
189
+ # this is the common case: job raised error and Sidekiq::JobRetry::Handled
190
+ # signals that we created a retry successfully. We can acknowlege the job.
191
+ ack = true
192
+ e = h.cause || h
193
+ handle_exception(e, {context: "Job raised exception", job: job_hash})
194
+ raise e
195
+ rescue Exception => ex
196
+ # Unexpected error! This is very bad and indicates an exception that got past
197
+ # the retry subsystem (e.g. network partition). We won't acknowledge the job
198
+ # so it can be rescued when using Sidekiq Pro.
199
+ handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
200
+ raise ex
179
201
  end
180
- ack = true
181
- rescue Sidekiq::Shutdown
182
- # Had to force kill this job because it didn't finish
183
- # within the timeout. Don't acknowledge the work since
184
- # we didn't properly finish it.
185
- rescue Sidekiq::JobRetry::Handled => h
186
- # this is the common case: job raised error and Sidekiq::JobRetry::Handled
187
- # signals that we created a retry successfully. We can acknowlege the job.
188
- ack = true
189
- e = h.cause || h
190
- handle_exception(e, {context: "Job raised exception", job: job_hash})
191
- raise e
192
- rescue Exception => ex
193
- # Unexpected error! This is very bad and indicates an exception that got past
194
- # the retry subsystem (e.g. network partition). We won't acknowledge the job
195
- # so it can be rescued when using Sidekiq Pro.
196
- handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
197
- raise ex
198
202
  ensure
199
203
  if ack
200
- # We don't want a shutdown signal to interrupt job acknowledgment.
201
- Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
202
- uow.acknowledge
203
- end
204
+ uow.acknowledge
204
205
  end
205
206
  end
206
207
  end
data/lib/sidekiq/rails.rb CHANGED
@@ -20,6 +20,10 @@ module Sidekiq
20
20
  def inspect
21
21
  "#<Sidekiq::Rails::Reloader @app=#{@app.class.name}>"
22
22
  end
23
+
24
+ def to_json(*)
25
+ Sidekiq.dump_json(inspect)
26
+ end
23
27
  end
24
28
 
25
29
  # By including the Options module, we allow AJs to directly control sidekiq features
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "7.1.1"
4
+ VERSION = "7.1.4"
5
5
  MAJOR = 7
6
6
  end
@@ -15,11 +15,11 @@ module Sidekiq
15
15
  end
16
16
 
17
17
  def halt(res)
18
- throw :halt, [res, {"content-type" => "text/plain"}, [res.to_s]]
18
+ throw :halt, [res, {Rack::CONTENT_TYPE => "text/plain"}, [res.to_s]]
19
19
  end
20
20
 
21
21
  def redirect(location)
22
- throw :halt, [302, {"location" => "#{request.base_url}#{location}"}, []]
22
+ throw :halt, [302, {Web::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, {"content-type" => "application/json", "cache-control" => "private, no-store"}, [Sidekiq.dump_json(payload)]]
71
+ [200, {Rack::CONTENT_TYPE => "application/json", Rack::CACHE_CONTROL => "private, no-store"}, [Sidekiq.dump_json(payload)]]
72
72
  end
73
73
 
74
74
  def initialize(env, block)
@@ -330,7 +330,7 @@ module Sidekiq
330
330
 
331
331
  def call(env)
332
332
  action = self.class.match(env)
333
- return [404, {"content-type" => "text/plain", "x-cascade" => "pass"}, ["Not Found"]] unless action
333
+ return [404, {Rack::CONTENT_TYPE => "text/plain", Web::X_CASCADE => "pass"}, ["Not Found"]] unless action
334
334
 
335
335
  app = @klass
336
336
  resp = catch(:halt) do
@@ -347,10 +347,10 @@ module Sidekiq
347
347
  else
348
348
  # rendered content goes here
349
349
  headers = {
350
- "content-type" => "text/html",
351
- "cache-control" => "private, no-store",
352
- "content-language" => action.locale,
353
- "content-security-policy" => CSP_HEADER
350
+ Rack::CONTENT_TYPE => "text/html",
351
+ Rack::CACHE_CONTROL => "private, no-store",
352
+ Web::CONTENT_LANGUAGE => action.locale,
353
+ Web::CONTENT_SECURITY_POLICY => CSP_HEADER
354
354
  }
355
355
  # we'll let Rack calculate Content-Length for us.
356
356
  [200, headers, [resp]]
@@ -62,7 +62,7 @@ module Sidekiq
62
62
 
63
63
  def deny(env)
64
64
  logger(env).warn "attack prevented by #{self.class}"
65
- [403, {"Content-Type" => "text/plain"}, ["Forbidden"]]
65
+ [403, {Rack::CONTENT_TYPE => "text/plain"}, ["Forbidden"]]
66
66
  end
67
67
 
68
68
  def session(env)
data/lib/sidekiq/web.rb CHANGED
@@ -34,6 +34,18 @@ module Sidekiq
34
34
  "Metrics" => "metrics"
35
35
  }
36
36
 
37
+ if Gem::Version.new(Rack::RELEASE) < Gem::Version.new("3")
38
+ CONTENT_LANGUAGE = "Content-Language"
39
+ CONTENT_SECURITY_POLICY = "Content-Security-Policy"
40
+ LOCATION = "Location"
41
+ X_CASCADE = "X-Cascade"
42
+ else
43
+ CONTENT_LANGUAGE = "content-language"
44
+ CONTENT_SECURITY_POLICY = "content-security-policy"
45
+ LOCATION = "location"
46
+ X_CASCADE = "x-cascade"
47
+ end
48
+
37
49
  class << self
38
50
  def settings
39
51
  self
@@ -137,7 +149,7 @@ module Sidekiq
137
149
  m = middlewares
138
150
 
139
151
  rules = []
140
- rules = [[:all, {"cache-control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
152
+ rules = [[:all, {Rack::CACHE_CONTROL => "private, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
141
153
 
142
154
  ::Rack::Builder.new do
143
155
  use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
@@ -140,6 +140,7 @@ function checkResponse(resp) {
140
140
 
141
141
  function scheduleLivePoll() {
142
142
  let ti = parseInt(localStorage.sidekiqTimeInterval) || 5000;
143
+ if (ti < 2000) { ti = 2000 }
143
144
  livePollTimer = setTimeout(livePollCallback, ti);
144
145
  }
145
146
 
@@ -57,7 +57,9 @@ class DashboardChart extends BaseChart {
57
57
  class RealtimeChart extends DashboardChart {
58
58
  constructor(el, options) {
59
59
  super(el, options);
60
- this.delay = parseInt(localStorage.sidekiqTimeInterval) || 5000;
60
+ let d = parseInt(localStorage.sidekiqTimeInterval) || 5000;
61
+ if (d < 2000) { d = 2000; }
62
+ this.delay = d
61
63
  this.startPolling();
62
64
  document.addEventListener("interval:update", this.handleUpdate.bind(this));
63
65
  }
@@ -5,10 +5,13 @@
5
5
  AreYouSureDeleteJob: Deseja deletar esta tarefa?
6
6
  AreYouSureDeleteQueue: Deseja deletar a fila %{queue}? Isso irá deletar todas as tarefas desta fila.
7
7
  Arguments: Argumentos
8
+ AvgExecutionTime: Tempo médio de execução
8
9
  BackToApp: De volta ao aplicativo
10
+ Bucket: Bucket
9
11
  Busy: Ocupados
10
12
  Class: Classe
11
13
  Connections: Conexões
14
+ Context: Contexto
12
15
  CreatedAt: Criado em
13
16
  CurrentMessagesInQueue: Mensagens atualmente na <span class='title'>%{queue}</span>
14
17
  Dashboard: Painel
@@ -16,13 +19,16 @@
16
19
  DeadJobs: Tarefas mortas
17
20
  Delete: Apagar
18
21
  DeleteAll: Apagar tudo
22
+ Deploy: Deploy
19
23
  Enqueued: Na fila
20
24
  Error: Erro
21
25
  ErrorBacktrace: Rastreamento do erro
22
26
  ErrorClass: Classe de erro
23
27
  ErrorMessage: Mensagem de erro
28
+ ExecutionTime: Tempo de execução
24
29
  Extras: Extras
25
30
  Failed: Falhas
31
+ Failure: Falha
26
32
  Failures: Falhas
27
33
  GoBack: ← Voltar
28
34
  History: Histórico
@@ -34,10 +40,13 @@
34
40
  Latency: Latência
35
41
  LivePoll: Live Poll
36
42
  MemoryUsage: Uso de memória
43
+ Metrics: Métricas
37
44
  Name: Nome
38
45
  Namespace: Namespace
39
46
  NextRetry: Próxima Tentativa
47
+ NoDataFound: Nenhum dado encontrado
40
48
  NoDeadJobsFound: Nenhuma tarefa morta foi encontrada
49
+ NoJobMetricsFound: Nenhuma métrica de tarefa encontrada
41
50
  NoRetriesFound: Nenhuma tentativa encontrada
42
51
  NoScheduledFound: Nenhuma tarefa agendada foi encontrada
43
52
  NotYetEnqueued: Ainda não enfileirado
@@ -47,6 +56,7 @@
47
56
  Pause: Pausar
48
57
  Paused: Pausado
49
58
  PeakMemoryUsage: Pico de uso de memória
59
+ Uptime: Tempo de atividade
50
60
  Plugins: Plug-ins
51
61
  PollingInterval: Intervalo de Polling
52
62
  Process: Processo
@@ -63,14 +73,24 @@
63
73
  RetryNow: Tentar novamente agora
64
74
  Scheduled: Agendados
65
75
  ScheduledJobs: Tarefas agendadas
76
+ idle: Ocioso
77
+ active: Ativo
78
+ Seconds: Segundos
66
79
  ShowAll: Mostrar todos
67
80
  SixMonths: 6 meses
68
81
  Size: Tamanho
82
+ Started: Iniciado
69
83
  Stop: Parar
70
84
  StopAll: Parar Todos
71
85
  StopPolling: Parar Polling
86
+ Success: Sucesso
87
+ Summary: Resumo
72
88
  Thread: Thread
73
89
  Threads: Threads
90
+ ThreeMonths: 3 meses
91
+ TotalExecutionTime: Tempo total de execução
74
92
  Unpause: Despausar
75
93
  Utilization: Utilização
94
+ Version: Versão
95
+ When: Quando
76
96
  Worker: Trabalhador
@@ -37,7 +37,7 @@
37
37
  <tr>
38
38
  <th>BID</th>
39
39
  <td>
40
- <a href="<%= root_path %>batches/<%= job.bid %>"><%= job.bid %>
40
+ <a href="<%= root_path %>batches/<%= job.bid %>"><%= job.bid %></a>
41
41
  </td>
42
42
  </tr>
43
43
  <% end %>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.1
4
+ version: 7.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Perham
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-25 00:00:00.000000000 Z
11
+ date: 2023-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client