sidekiq 7.1.2 → 7.1.3

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 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