sidekiq 8.0.7 → 8.0.8
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 +19 -0
- data/lib/sidekiq/api.rb +2 -1
- data/lib/sidekiq/client.rb +15 -1
- data/lib/sidekiq/component.rb +2 -1
- data/lib/sidekiq/config.rb +3 -4
- data/lib/sidekiq/job/iterable.rb +21 -12
- data/lib/sidekiq/job.rb +2 -2
- data/lib/sidekiq/job_retry.rb +10 -2
- data/lib/sidekiq/loader.rb +57 -0
- data/lib/sidekiq/rails.rb +3 -1
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/action.rb +3 -3
- data/lib/sidekiq.rb +5 -0
- data/web/assets/javascripts/application.js +5 -5
- data/web/assets/stylesheets/style.css +0 -7
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a1e8c90888b4135f21e4dfef66fd9ad9ac8c72ab267599a8452f4bd172234a2
|
4
|
+
data.tar.gz: fa83e70c818fc3b25441e946c1a093a2d9a5f4515ee2bd88522cbafcb8097973
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ea8ac9585538723d941af6ce9150933669705cf1e3459dcea8989d073d234c4e233e74314a5a682e3111e1290a69671051e847b5ee97970c1edd08293e14bbc
|
7
|
+
data.tar.gz: 0b253cd035132f786613fbb97440f5d16f48717aedaa7f50865e0e261ce0fc17b64885bea3cdeb5aabe72258c06ed013891d83655bdcf842fc1a261d1b0aa985
|
data/Changes.md
CHANGED
@@ -2,6 +2,25 @@
|
|
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
|
+
8.0.8
|
6
|
+
----------
|
7
|
+
|
8
|
+
- Allow an optional global iteration max runtime. After executing for this length of time,
|
9
|
+
Sidekiq will re-queue the job to continue execution at a later time [#6819, fatkodima]
|
10
|
+
```ruby
|
11
|
+
Sidekiq.configure_server do |cfg|
|
12
|
+
cfg[:max_iteration_runtime] = 600 # ten minutes
|
13
|
+
end
|
14
|
+
```
|
15
|
+
- Add `discarded_at` attribute when discarding a job so death handlers can distinguish between
|
16
|
+
a job which was killed and one that was discarded. [#6820, gstokkink]
|
17
|
+
- `perform_bulk` now accepts an `:at` array of times to schedule each job at the corresponding time.
|
18
|
+
`perform_bulk(args: [[1], [2]], at: [Time.now, Time.now + 1])` [#6790, fatkodima]
|
19
|
+
- `perform_bulk` now accepts a `:spread_interval` value to schedule jobs over
|
20
|
+
the next N seconds. `perform_bulk(..., spread_interval: 60)` [#6792, fatkodima]
|
21
|
+
- Fix unintended display of flash messages in the Web UI due to session key collision
|
22
|
+
- Add support for lazy load hooks [#6825]
|
23
|
+
|
5
24
|
8.0.7
|
6
25
|
----------
|
7
26
|
|
data/lib/sidekiq/api.rb
CHANGED
@@ -1168,7 +1168,6 @@ module Sidekiq
|
|
1168
1168
|
# # thread_id is a unique identifier per thread
|
1169
1169
|
# # work is a `Sidekiq::Work` instance that has the following accessor methods.
|
1170
1170
|
# # [work.queue, work.run_at, work.payload]
|
1171
|
-
# # run_at is an epoch Integer.
|
1172
1171
|
# end
|
1173
1172
|
#
|
1174
1173
|
class WorkSet
|
@@ -1322,3 +1321,5 @@ module Sidekiq
|
|
1322
1321
|
end
|
1323
1322
|
end
|
1324
1323
|
end
|
1324
|
+
|
1325
|
+
Sidekiq.loader.run_load_hooks(:api)
|
data/lib/sidekiq/client.rb
CHANGED
@@ -117,6 +117,9 @@ module Sidekiq
|
|
117
117
|
# larger than 1000 but YMMV based on network quality, size of job args, etc.
|
118
118
|
# A large number of jobs can cause a bit of Redis command processing latency.
|
119
119
|
#
|
120
|
+
# Accepts an additional `:spread_interval` option (in seconds) to randomly spread
|
121
|
+
# the jobs schedule times over the specified interval.
|
122
|
+
#
|
120
123
|
# Takes the same arguments as #push except that args is expected to be
|
121
124
|
# an Array of Arrays. All other keys are duplicated for each job. Each job
|
122
125
|
# is run through the client middleware pipeline and each job gets its own Job ID
|
@@ -131,13 +134,24 @@ module Sidekiq
|
|
131
134
|
def push_bulk(items)
|
132
135
|
batch_size = items.delete(:batch_size) || items.delete("batch_size") || 1_000
|
133
136
|
args = items["args"]
|
134
|
-
at = items.delete("at")
|
137
|
+
at = items.delete("at") || items.delete(:at)
|
135
138
|
raise ArgumentError, "Job 'at' must be a Numeric or an Array of Numeric timestamps" if at && (Array(at).empty? || !Array(at).all? { |entry| entry.is_a?(Numeric) })
|
136
139
|
raise ArgumentError, "Job 'at' Array must have same size as 'args' Array" if at.is_a?(Array) && at.size != args.size
|
137
140
|
|
138
141
|
jid = items.delete("jid")
|
139
142
|
raise ArgumentError, "Explicitly passing 'jid' when pushing more than one job is not supported" if jid && args.size > 1
|
140
143
|
|
144
|
+
spread_interval = items.delete(:spread_interval) || items.delete("spread_interval")
|
145
|
+
raise ArgumentError, "Jobs 'spread_interval' must be a positive Numeric" if spread_interval && (!spread_interval.is_a?(Numeric) || spread_interval <= 0)
|
146
|
+
raise ArgumentError, "Only one of 'at' or 'spread_interval' can be provided" if at && spread_interval
|
147
|
+
|
148
|
+
if !at && spread_interval
|
149
|
+
# Do not use spread interval smaller than pooling interval.
|
150
|
+
spread_interval = [spread_interval, 5].max
|
151
|
+
now = Time.now.to_f
|
152
|
+
at = args.map { now + rand * spread_interval }
|
153
|
+
end
|
154
|
+
|
141
155
|
normed = normalize_item(items)
|
142
156
|
slice_index = 0
|
143
157
|
result = args.each_slice(batch_size).flat_map do |slice|
|
data/lib/sidekiq/component.rb
CHANGED
@@ -19,7 +19,8 @@ module Sidekiq
|
|
19
19
|
DEFAULT_THREAD_PRIORITY = -1
|
20
20
|
|
21
21
|
##
|
22
|
-
# Sidekiq::Component
|
22
|
+
# Sidekiq::Component provides a set of utility methods depending only
|
23
|
+
# on Sidekiq::Config. It assumes a config instance is available at @config.
|
23
24
|
module Component # :nodoc:
|
24
25
|
attr_reader :config
|
25
26
|
|
data/lib/sidekiq/config.rb
CHANGED
@@ -17,10 +17,9 @@ module Sidekiq
|
|
17
17
|
poll_interval_average: nil,
|
18
18
|
average_scheduled_poll_interval: 5,
|
19
19
|
on_complex_arguments: :raise,
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
},
|
20
|
+
# if the Iterable job runs longer than this value (in seconds), then the job
|
21
|
+
# will be interrupted after the current iteration and re-enqueued at the back of the queue
|
22
|
+
max_iteration_runtime: nil,
|
24
23
|
error_handlers: [],
|
25
24
|
death_handlers: [],
|
26
25
|
lifecycle_events: {
|
data/lib/sidekiq/job/iterable.rb
CHANGED
@@ -143,7 +143,7 @@ module Sidekiq
|
|
143
143
|
fetch_previous_iteration_state
|
144
144
|
|
145
145
|
@_executions += 1
|
146
|
-
@_start_time =
|
146
|
+
@_start_time = mono_now
|
147
147
|
|
148
148
|
enumerator = build_enumerator(*args, cursor: @_cursor)
|
149
149
|
unless enumerator
|
@@ -204,17 +204,17 @@ module Sidekiq
|
|
204
204
|
|
205
205
|
time_limit = Sidekiq.default_configuration[:timeout]
|
206
206
|
found_record = false
|
207
|
-
state_flushed_at =
|
207
|
+
state_flushed_at = mono_now
|
208
208
|
|
209
209
|
enumerator.each do |object, cursor|
|
210
210
|
found_record = true
|
211
211
|
@_cursor = cursor
|
212
212
|
@current_object = object
|
213
213
|
|
214
|
-
|
215
|
-
if
|
214
|
+
interrupt_job = interrupted? || should_interrupt?
|
215
|
+
if mono_now - state_flushed_at >= STATE_FLUSH_INTERVAL || interrupt_job
|
216
216
|
_, _, cancelled = flush_state
|
217
|
-
state_flushed_at =
|
217
|
+
state_flushed_at = mono_now
|
218
218
|
if cancelled
|
219
219
|
@_cancelled = true
|
220
220
|
on_cancel
|
@@ -223,9 +223,9 @@ module Sidekiq
|
|
223
223
|
end
|
224
224
|
end
|
225
225
|
|
226
|
-
return false if
|
226
|
+
return false if interrupt_job
|
227
227
|
|
228
|
-
verify_iteration_time(time_limit
|
228
|
+
verify_iteration_time(time_limit) do
|
229
229
|
around_iteration do
|
230
230
|
each_iteration(object, *arguments)
|
231
231
|
rescue Exception
|
@@ -238,16 +238,16 @@ module Sidekiq
|
|
238
238
|
logger.debug("Enumerator found nothing to iterate!") unless found_record
|
239
239
|
true
|
240
240
|
ensure
|
241
|
-
@_runtime += (
|
241
|
+
@_runtime += (mono_now - @_start_time)
|
242
242
|
end
|
243
243
|
|
244
|
-
def verify_iteration_time(time_limit
|
245
|
-
start =
|
244
|
+
def verify_iteration_time(time_limit)
|
245
|
+
start = mono_now
|
246
246
|
yield
|
247
|
-
finish =
|
247
|
+
finish = mono_now
|
248
248
|
total = finish - start
|
249
249
|
if total > time_limit
|
250
|
-
logger.warn { "Iteration took longer (%.2f) than Sidekiq's shutdown timeout (%d)
|
250
|
+
logger.warn { "Iteration took longer (%.2f) than Sidekiq's shutdown timeout (%d). This can lead to job processing problems during deploys" % [total, time_limit] }
|
251
251
|
end
|
252
252
|
end
|
253
253
|
|
@@ -273,6 +273,11 @@ module Sidekiq
|
|
273
273
|
end
|
274
274
|
end
|
275
275
|
|
276
|
+
def should_interrupt?
|
277
|
+
max_iteration_runtime = Sidekiq.default_configuration[:max_iteration_runtime]
|
278
|
+
max_iteration_runtime && (mono_now - @_start_time > max_iteration_runtime)
|
279
|
+
end
|
280
|
+
|
276
281
|
def flush_state
|
277
282
|
key = iteration_key
|
278
283
|
state = {
|
@@ -308,6 +313,10 @@ module Sidekiq
|
|
308
313
|
raise "Unexpected thrown value: #{completed.inspect}"
|
309
314
|
end
|
310
315
|
end
|
316
|
+
|
317
|
+
def mono_now
|
318
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
319
|
+
end
|
311
320
|
end
|
312
321
|
end
|
313
322
|
end
|
data/lib/sidekiq/job.rb
CHANGED
@@ -248,9 +248,9 @@ module Sidekiq
|
|
248
248
|
end
|
249
249
|
alias_method :perform_sync, :perform_inline
|
250
250
|
|
251
|
-
def perform_bulk(args,
|
251
|
+
def perform_bulk(args, **options)
|
252
252
|
client = @klass.build_client
|
253
|
-
client.push_bulk(@opts.merge("class" => @klass, "args" => args,
|
253
|
+
client.push_bulk(@opts.merge({"class" => @klass, "args" => args}, options))
|
254
254
|
end
|
255
255
|
|
256
256
|
# +interval+ must be a timestamp, numeric or something that acts
|
data/lib/sidekiq/job_retry.rb
CHANGED
@@ -186,6 +186,8 @@ module Sidekiq
|
|
186
186
|
strategy, delay = delay_for(jobinst, count, exception, msg)
|
187
187
|
case strategy
|
188
188
|
when :discard
|
189
|
+
msg["discarded_at"] = now_ms
|
190
|
+
|
189
191
|
return run_death_handlers(msg, exception)
|
190
192
|
when :kill
|
191
193
|
return retries_exhausted(jobinst, msg, exception)
|
@@ -255,8 +257,14 @@ module Sidekiq
|
|
255
257
|
handle_exception(e, {context: "Error calling retries_exhausted", job: msg})
|
256
258
|
end
|
257
259
|
|
258
|
-
|
259
|
-
|
260
|
+
discarded = msg["dead"] == false || rv == :discard
|
261
|
+
|
262
|
+
if discarded
|
263
|
+
msg["discarded_at"] = now_ms
|
264
|
+
else
|
265
|
+
send_to_morgue(msg)
|
266
|
+
end
|
267
|
+
|
260
268
|
run_death_handlers(msg, exception)
|
261
269
|
end
|
262
270
|
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Sidekiq
|
2
|
+
require "sidekiq/component"
|
3
|
+
|
4
|
+
class Loader
|
5
|
+
include Sidekiq::Component
|
6
|
+
|
7
|
+
def initialize(cfg = Sidekiq.default_configuration)
|
8
|
+
@config = cfg
|
9
|
+
@load_hooks = Hash.new { |h, k| h[k] = [] }
|
10
|
+
@loaded = Set.new
|
11
|
+
@lock = Mutex.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# Declares a block that will be executed when a Sidekiq component is fully
|
15
|
+
# loaded. If the component has already loaded, the block is executed
|
16
|
+
# immediately.
|
17
|
+
#
|
18
|
+
# Sidekiq.loader.on_load(:api) do
|
19
|
+
# # extend the sidekiq API
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
def on_load(name, &block)
|
23
|
+
# we don't want to hold the lock while calling the block
|
24
|
+
to_run = nil
|
25
|
+
|
26
|
+
@lock.synchronize do
|
27
|
+
if @loaded.include?(name)
|
28
|
+
to_run = block
|
29
|
+
else
|
30
|
+
@load_hooks[name] << block
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
to_run&.call
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# Executes all blocks registered to +name+ via on_load.
|
39
|
+
#
|
40
|
+
# Sidekiq.loader.run_load_hooks(:api)
|
41
|
+
#
|
42
|
+
# In the case of the above example, it will execute all hooks registered for +:api+.
|
43
|
+
#
|
44
|
+
def run_load_hooks(name)
|
45
|
+
hks = @lock.synchronize do
|
46
|
+
@loaded << name
|
47
|
+
@load_hooks.delete(name)
|
48
|
+
end
|
49
|
+
|
50
|
+
hks&.each do |blk|
|
51
|
+
blk.call
|
52
|
+
rescue => ex
|
53
|
+
handle_exception(ex, hook: name)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/sidekiq/rails.rb
CHANGED
@@ -48,8 +48,10 @@ module Sidekiq
|
|
48
48
|
unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
|
49
49
|
if ::Rails.logger.respond_to?(:broadcast_to)
|
50
50
|
::Rails.logger.broadcast_to(config.logger)
|
51
|
-
|
51
|
+
elsif ::ActiveSupport::Logger.respond_to?(:broadcast)
|
52
52
|
::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
|
53
|
+
else
|
54
|
+
::Rails.logger = ::ActiveSupport::BroadcastLogger.new(::Rails.logger, config.logger)
|
53
55
|
end
|
54
56
|
end
|
55
57
|
end
|
data/lib/sidekiq/version.rb
CHANGED
data/lib/sidekiq/web/action.rb
CHANGED
@@ -102,15 +102,15 @@ module Sidekiq
|
|
102
102
|
def flash
|
103
103
|
msg = yield
|
104
104
|
logger.info msg
|
105
|
-
session[:
|
105
|
+
session[:skq_flash] = msg
|
106
106
|
end
|
107
107
|
|
108
108
|
def flash?
|
109
|
-
session&.[](:
|
109
|
+
session&.[](:skq_flash)
|
110
110
|
end
|
111
111
|
|
112
112
|
def get_flash
|
113
|
-
@flash ||= session.delete(:
|
113
|
+
@flash ||= session.delete(:skq_flash)
|
114
114
|
end
|
115
115
|
|
116
116
|
def erb(content, options = {})
|
data/lib/sidekiq.rb
CHANGED
@@ -29,6 +29,7 @@ end
|
|
29
29
|
|
30
30
|
require "sidekiq/config"
|
31
31
|
require "sidekiq/logger"
|
32
|
+
require "sidekiq/loader"
|
32
33
|
require "sidekiq/client"
|
33
34
|
require "sidekiq/transaction_aware_client"
|
34
35
|
require "sidekiq/job"
|
@@ -94,6 +95,10 @@ module Sidekiq
|
|
94
95
|
default_configuration.logger
|
95
96
|
end
|
96
97
|
|
98
|
+
def self.loader
|
99
|
+
@loader ||= Loader.new
|
100
|
+
end
|
101
|
+
|
97
102
|
def self.configure_server(&block)
|
98
103
|
(@config_blocks ||= []) << block
|
99
104
|
yield default_configuration if server?
|
@@ -58,7 +58,7 @@ function addPollingListeners(_event) {
|
|
58
58
|
|
59
59
|
function addDataToggleListeners(event) {
|
60
60
|
var source = event.target || event.srcElement;
|
61
|
-
var targName = source.
|
61
|
+
var targName = source.dataset.toggle;
|
62
62
|
var full = document.getElementById(targName);
|
63
63
|
full.classList.toggle("is-open");
|
64
64
|
}
|
@@ -81,7 +81,7 @@ function addShiftClickListeners() {
|
|
81
81
|
}
|
82
82
|
|
83
83
|
function updateFuzzyTimes() {
|
84
|
-
var locale = document.body.
|
84
|
+
var locale = document.body.dataset.locale;
|
85
85
|
var parts = locale.split('-');
|
86
86
|
if (typeof parts[1] !== 'undefined') {
|
87
87
|
parts[1] = parts[1].toUpperCase();
|
@@ -96,7 +96,7 @@ function updateFuzzyTimes() {
|
|
96
96
|
function updateNumbers() {
|
97
97
|
document.querySelectorAll("[data-nwp]").forEach(node => {
|
98
98
|
let number = parseFloat(node.textContent);
|
99
|
-
let precision = parseInt(node.dataset
|
99
|
+
let precision = parseInt(node.dataset.nwp || 0);
|
100
100
|
if (typeof number === "number") {
|
101
101
|
let formatted = number.toLocaleString(undefined, {
|
102
102
|
minimumFractionDigits: precision,
|
@@ -175,9 +175,9 @@ function handleConfirmDialog (event) {
|
|
175
175
|
const target = event.target
|
176
176
|
|
177
177
|
if (target.localName !== "input") { return }
|
178
|
-
|
178
|
+
const confirmMessage = target.dataset.confirm
|
179
179
|
|
180
|
-
|
180
|
+
if (confirmMessage === undefined) { return }
|
181
181
|
|
182
182
|
if (!window.confirm(confirmMessage)) {
|
183
183
|
event.preventDefault()
|
@@ -29,8 +29,6 @@
|
|
29
29
|
|
30
30
|
*, *::before, *::after { box-sizing: border-box; }
|
31
31
|
|
32
|
-
::selection { background: var(--color-selected); }
|
33
|
-
|
34
32
|
:focus-visible {
|
35
33
|
outline: 1px solid oklch(from var(--color-primary) l c h / 50%);
|
36
34
|
}
|
@@ -567,7 +565,6 @@ body > footer .nav {
|
|
567
565
|
--color-border: oklch(25% 0.01 256);
|
568
566
|
--color-input-border: oklch(31% 0.01 256);
|
569
567
|
--color-selected: oklch(27% 0.01 256);
|
570
|
-
--color-selected-text: oklch(55% 0.11 45);
|
571
568
|
--color-table-bg-alt: oklch(24% 0.01 256);
|
572
569
|
--color-shadow: oklch(9% 0.01 256 / 10%);
|
573
570
|
--color-text: oklch(75% 0.01 256);
|
@@ -616,10 +613,6 @@ body > footer .nav {
|
|
616
613
|
.label-info { background: var(--color-info); }
|
617
614
|
.label-danger { background: var(--color-danger); }
|
618
615
|
.label-warning { background: var(--color-warning); }
|
619
|
-
|
620
|
-
td.box::selection {
|
621
|
-
background-color: var(--color-selected-text);
|
622
|
-
}
|
623
616
|
}
|
624
617
|
|
625
618
|
@media (max-width: 800px) { :root { --font-size: 14px; } }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 8.0.
|
4
|
+
version: 8.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Perham
|
@@ -122,6 +122,7 @@ files:
|
|
122
122
|
- lib/sidekiq/job_retry.rb
|
123
123
|
- lib/sidekiq/job_util.rb
|
124
124
|
- lib/sidekiq/launcher.rb
|
125
|
+
- lib/sidekiq/loader.rb
|
125
126
|
- lib/sidekiq/logger.rb
|
126
127
|
- lib/sidekiq/manager.rb
|
127
128
|
- lib/sidekiq/metrics/query.rb
|