sidekiq 7.1.2 → 7.1.3

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: 587dc2a304b24daba86943907fe6b0285ecd302119e900e68403fcf0dedff163
4
- data.tar.gz: 713489d61003baf22b07879e959048bede62a5b661cf37a6c43540cd3cd59eac
3
+ metadata.gz: 03e85917d2ea03bf226aa6922510b627931c326eb31badf50cc23f39182fc409
4
+ data.tar.gz: '00180ea8ecb3013a8a405eecd0c6051c3611de72e668a9ce1da695fdbfd34a59'
5
5
  SHA512:
6
- metadata.gz: 026b2dd393d8e9774f1afa6fe4df9cfca70520e84a0d566dde668e535809492476004f205668d16bbe2d5f0111fb4d51a4ddddd60bfd6a1c90d906bcb50b5c92
7
- data.tar.gz: 5b6769ef293543a4d7003e870c5ed5b1d9b7d44a941c4fc446afd16531fc1eaae2f684359fb7c093eb1761c517ff31c106f247c17a8e2808bb629adf47061a62
6
+ metadata.gz: 607e2599f0500af84ced9b8e0b4ed34d1cf947d1e0c7d144ab1a03f5f0652cdda97d097422b6beba8b76f5c78381d29cdde784b1c7463aa006d009304739acaa
7
+ data.tar.gz: 9a7758f5bd266711d56bd2df5275d9d942d1ca3e586f5e5eb945dd2a62a7bac5f4f54f058d41a2c747da301d691c7cceb47ef641f2d0a2cc2716051914d9646a
data/Changes.md CHANGED
@@ -2,6 +2,15 @@
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.3
6
+ ----------
7
+
8
+ - Add `sidekiq_options retry_for: 48.hours` to allow time-based retry windows [#6029]
9
+ - Support sidekiq_retry_in and sidekiq_retries_exhausted_block in ActiveJobs (#5994)
10
+ - Lowercase all Rack headers for Rack 3.0 [#5951]
11
+ - Validate Sidekiq::Web page refresh delay to avoid potential DoS,
12
+ CVE-2023-26141, thanks for reporting Keegan!
13
+
5
14
  7.1.2
6
15
  ----------
7
16
 
@@ -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
@@ -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
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "7.1.2"
4
+ VERSION = "7.1.3"
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" => "private, 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.2
4
+ version: 7.1.3
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-06-14 00:00:00.000000000 Z
11
+ date: 2023-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis-client