sidekiq 6.4.2 → 6.5.2
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.
- checksums.yaml +4 -4
- data/Changes.md +35 -0
- data/bin/sidekiqload +15 -3
- data/lib/sidekiq/api.rb +160 -32
- data/lib/sidekiq/cli.rb +34 -32
- data/lib/sidekiq/client.rb +4 -4
- data/lib/sidekiq/component.rb +65 -0
- data/lib/sidekiq/delay.rb +1 -1
- data/lib/sidekiq/fetch.rb +16 -14
- data/lib/sidekiq/job_retry.rb +50 -34
- data/lib/sidekiq/job_util.rb +7 -3
- data/lib/sidekiq/launcher.rb +22 -19
- data/lib/sidekiq/logger.rb +1 -1
- data/lib/sidekiq/manager.rb +23 -20
- data/lib/sidekiq/metrics/deploy.rb +47 -0
- data/lib/sidekiq/metrics/query.rb +124 -0
- data/lib/sidekiq/metrics/shared.rb +94 -0
- data/lib/sidekiq/metrics/tracking.rb +134 -0
- data/lib/sidekiq/middleware/chain.rb +82 -38
- data/lib/sidekiq/middleware/current_attributes.rb +10 -4
- data/lib/sidekiq/middleware/i18n.rb +2 -0
- data/lib/sidekiq/middleware/modules.rb +21 -0
- data/lib/sidekiq/paginator.rb +2 -2
- data/lib/sidekiq/processor.rb +13 -13
- data/lib/sidekiq/rails.rb +5 -5
- data/lib/sidekiq/redis_client_adapter.rb +154 -0
- data/lib/sidekiq/redis_connection.rb +80 -47
- data/lib/sidekiq/ring_buffer.rb +29 -0
- data/lib/sidekiq/scheduled.rb +11 -10
- data/lib/sidekiq/testing.rb +1 -1
- data/lib/sidekiq/transaction_aware_client.rb +45 -0
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/application.rb +13 -0
- data/lib/sidekiq/web/helpers.rb +25 -2
- data/lib/sidekiq/web.rb +4 -0
- data/lib/sidekiq/worker.rb +2 -1
- data/lib/sidekiq.rb +87 -18
- data/sidekiq.gemspec +1 -1
- data/web/assets/javascripts/application.js +1 -1
- data/web/assets/javascripts/dashboard.js +0 -17
- data/web/assets/javascripts/graph.js +16 -0
- data/web/locales/en.yml +4 -0
- data/web/locales/pt-br.yml +27 -9
- data/web/views/_nav.erb +1 -1
- data/web/views/busy.erb +1 -1
- data/web/views/dashboard.erb +1 -0
- data/web/views/metrics.erb +59 -0
- data/web/views/metrics_for_job.erb +92 -0
- data/web/views/queue.erb +5 -1
- metadata +16 -6
- data/lib/sidekiq/exception_handler.rb +0 -27
- data/lib/sidekiq/util.rb +0 -108
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2c97836bf831cdce9bbab92fbd7b0d01d3522c24f5cced0e0822a53b906d0af
|
4
|
+
data.tar.gz: 85c7f9abe6844a5471519ca235c65e84275bd2f8d33e395ec3f85bada091c699
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06e0cad7074f7288c4ec66ee0f8e38e85c556390f61a23dc9dc025459a0da57ed0641ae89c450862c2101045d4220e5e11ad8b7a23f5316fbfe7fb12fc04ec67
|
7
|
+
data.tar.gz: 7362db4b038735acda91a86017053cc5fec9b70f1bbc8b0650abb592b60598c49a07fbecd3ec14b18a9f6b1cf6e3b2e0810b64e70e833072b64248a945f45646
|
data/Changes.md
CHANGED
@@ -2,6 +2,41 @@
|
|
2
2
|
|
3
3
|
[Sidekiq Changes](https://github.com/mperham/sidekiq/blob/main/Changes.md) | [Sidekiq Pro Changes](https://github.com/mperham/sidekiq/blob/main/Pro-Changes.md) | [Sidekiq Enterprise Changes](https://github.com/mperham/sidekiq/blob/main/Ent-Changes.md)
|
4
4
|
|
5
|
+
6.5.2
|
6
|
+
----------
|
7
|
+
|
8
|
+
- [Job Metrics are under active development, help wanted!](https://github.com/mperham/sidekiq/wiki/Metrics#contributing) **BETA**
|
9
|
+
- Add `Context` column on queue page which shows any CurrentAttributes [#5450]
|
10
|
+
- `sidekiq_retry_in` may now return `:discard` or `:kill` to dynamically stop job retries [#5406]
|
11
|
+
- Smarter sorting of processes in /busy Web UI [#5398]
|
12
|
+
- Fix broken hamburger menu in mobile UI [#5428]
|
13
|
+
- Require redis-rb 4.5.0. Note that Sidekiq will break if you use the
|
14
|
+
[`Redis.exists_returns_integer = false`](https://github.com/redis/redis-rb/blob/master/CHANGELOG.md#450) flag. [#5394]
|
15
|
+
|
16
|
+
6.5.1
|
17
|
+
----------
|
18
|
+
|
19
|
+
- Fix `push_bulk` breakage [#5387]
|
20
|
+
|
21
|
+
6.5.0
|
22
|
+
---------
|
23
|
+
|
24
|
+
- Substantial refactoring of Sidekiq server internals, part of a larger effort
|
25
|
+
to reduce Sidekiq's internal usage of global methods and data, see [docs/global_to_local.md](docs/global_to_local.md) and [docs/middleware.md](docs/middleware.md).
|
26
|
+
- **Add beta support for the `redis-client` gem**. This will become the default Redis driver in Sidekiq 7.0. [#5298]
|
27
|
+
Read more: https://github.com/mperham/sidekiq/wiki/Using-redis-client
|
28
|
+
- **Add beta support for DB transaction-aware client** [#5291]
|
29
|
+
Add this line to your initializer and any jobs created during a transaction
|
30
|
+
will only be pushed to Redis **after the transaction commits**. You will need to add the
|
31
|
+
`after_commit_everywhere` gem to your Gemfile.
|
32
|
+
```ruby
|
33
|
+
Sidekiq.transactional_push!
|
34
|
+
```
|
35
|
+
This feature does not have a lot of production usage yet; please try it out and let us
|
36
|
+
know if you have any issues. It will be fully supported in Sidekiq 7.0 or removed if it
|
37
|
+
proves problematic.
|
38
|
+
- Fix regression with middleware arguments [#5312]
|
39
|
+
|
5
40
|
6.4.2
|
6
41
|
---------
|
7
42
|
|
data/bin/sidekiqload
CHANGED
@@ -11,6 +11,10 @@ Bundler.require(:default, :load_test)
|
|
11
11
|
require_relative "../lib/sidekiq/cli"
|
12
12
|
require_relative "../lib/sidekiq/launcher"
|
13
13
|
|
14
|
+
if ENV["SIDEKIQ_REDIS_CLIENT"]
|
15
|
+
Sidekiq::RedisConnection.adapter = :redis_client
|
16
|
+
end
|
17
|
+
|
14
18
|
Sidekiq.configure_server do |config|
|
15
19
|
config.options[:concurrency] = 10
|
16
20
|
config.redis = {db: 13, port: 6380}
|
@@ -118,15 +122,23 @@ Monitoring = Thread.new do
|
|
118
122
|
end
|
119
123
|
end
|
120
124
|
|
125
|
+
def with_latency(latency, &block)
|
126
|
+
Sidekiq.logger.error "Simulating #{latency}ms of latency between Sidekiq and redis"
|
127
|
+
if latency > 0
|
128
|
+
Toxiproxy[:redis].downstream(:latency, latency: latency).apply(&block)
|
129
|
+
else
|
130
|
+
yield
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
121
134
|
begin
|
122
135
|
# RubyProf::exclude_threads = [ Monitoring ]
|
123
136
|
# RubyProf.start
|
124
137
|
events = Sidekiq.options[:lifecycle_events][:startup]
|
125
138
|
events.each(&:call)
|
126
139
|
events.clear
|
127
|
-
|
128
|
-
|
129
|
-
Toxiproxy[:redis].downstream(:latency, latency: 1).apply do
|
140
|
+
|
141
|
+
with_latency(Integer(ENV.fetch("LATENCY", "1"))) do
|
130
142
|
launcher = Sidekiq::Launcher.new(Sidekiq.options)
|
131
143
|
launcher.run
|
132
144
|
|
data/lib/sidekiq/api.rb
CHANGED
@@ -3,9 +3,17 @@
|
|
3
3
|
require "sidekiq"
|
4
4
|
|
5
5
|
require "zlib"
|
6
|
+
require "set"
|
6
7
|
require "base64"
|
8
|
+
require "sidekiq/metrics/deploy"
|
9
|
+
require "sidekiq/metrics/query"
|
7
10
|
|
8
11
|
module Sidekiq
|
12
|
+
# Retrieve runtime statistics from Redis regarding
|
13
|
+
# this Sidekiq cluster.
|
14
|
+
#
|
15
|
+
# stat = Sidekiq::Stats.new
|
16
|
+
# stat.processed
|
9
17
|
class Stats
|
10
18
|
def initialize
|
11
19
|
fetch_stats_fast!
|
@@ -52,6 +60,7 @@ module Sidekiq
|
|
52
60
|
end
|
53
61
|
|
54
62
|
# O(1) redis calls
|
63
|
+
# @api private
|
55
64
|
def fetch_stats_fast!
|
56
65
|
pipe1_res = Sidekiq.redis { |conn|
|
57
66
|
conn.pipelined do |pipeline|
|
@@ -91,6 +100,7 @@ module Sidekiq
|
|
91
100
|
end
|
92
101
|
|
93
102
|
# O(number of processes + number of queues) redis calls
|
103
|
+
# @api private
|
94
104
|
def fetch_stats_slow!
|
95
105
|
processes = Sidekiq.redis { |conn|
|
96
106
|
conn.sscan_each("processes").to_a
|
@@ -116,11 +126,13 @@ module Sidekiq
|
|
116
126
|
@stats
|
117
127
|
end
|
118
128
|
|
129
|
+
# @api private
|
119
130
|
def fetch_stats!
|
120
131
|
fetch_stats_fast!
|
121
132
|
fetch_stats_slow!
|
122
133
|
end
|
123
134
|
|
135
|
+
# @api private
|
124
136
|
def reset(*stats)
|
125
137
|
all = %w[failed processed]
|
126
138
|
stats = stats.empty? ? all : all & stats.flatten.compact.map(&:to_s)
|
@@ -191,7 +203,7 @@ module Sidekiq
|
|
191
203
|
stat_hash[dates[idx]] = value ? value.to_i : 0
|
192
204
|
end
|
193
205
|
end
|
194
|
-
rescue
|
206
|
+
rescue RedisConnection.adapter::CommandError
|
195
207
|
# mget will trigger a CROSSSLOT error when run against a Cluster
|
196
208
|
# TODO Someone want to add Cluster support?
|
197
209
|
end
|
@@ -202,9 +214,10 @@ module Sidekiq
|
|
202
214
|
end
|
203
215
|
|
204
216
|
##
|
205
|
-
#
|
217
|
+
# Represents a queue within Sidekiq.
|
206
218
|
# Allows enumeration of all jobs within the queue
|
207
|
-
# and deletion of jobs.
|
219
|
+
# and deletion of jobs. NB: this queue data is real-time
|
220
|
+
# and is changing within Redis moment by moment.
|
208
221
|
#
|
209
222
|
# queue = Sidekiq::Queue.new("mailer")
|
210
223
|
# queue.each do |job|
|
@@ -212,29 +225,34 @@ module Sidekiq
|
|
212
225
|
# job.args # => [1, 2, 3]
|
213
226
|
# job.delete if job.jid == 'abcdef1234567890'
|
214
227
|
# end
|
215
|
-
#
|
216
228
|
class Queue
|
217
229
|
include Enumerable
|
218
230
|
|
219
231
|
##
|
220
|
-
#
|
232
|
+
# Fetch all known queues within Redis.
|
221
233
|
#
|
234
|
+
# @return [Array<Sidekiq::Queue>]
|
222
235
|
def self.all
|
223
236
|
Sidekiq.redis { |c| c.sscan_each("queues").to_a }.sort.map { |q| Sidekiq::Queue.new(q) }
|
224
237
|
end
|
225
238
|
|
226
239
|
attr_reader :name
|
227
240
|
|
241
|
+
# @param name [String] the name of the queue
|
228
242
|
def initialize(name = "default")
|
229
243
|
@name = name.to_s
|
230
244
|
@rname = "queue:#{name}"
|
231
245
|
end
|
232
246
|
|
247
|
+
# The current size of the queue within Redis.
|
248
|
+
# This value is real-time and can change between calls.
|
249
|
+
#
|
250
|
+
# @return [Integer] the size
|
233
251
|
def size
|
234
252
|
Sidekiq.redis { |con| con.llen(@rname) }
|
235
253
|
end
|
236
254
|
|
237
|
-
#
|
255
|
+
# @return [Boolean] if the queue is currently paused
|
238
256
|
def paused?
|
239
257
|
false
|
240
258
|
end
|
@@ -243,7 +261,7 @@ module Sidekiq
|
|
243
261
|
# Calculates this queue's latency, the difference in seconds since the oldest
|
244
262
|
# job in the queue was enqueued.
|
245
263
|
#
|
246
|
-
# @return Float
|
264
|
+
# @return [Float] in seconds
|
247
265
|
def latency
|
248
266
|
entry = Sidekiq.redis { |conn|
|
249
267
|
conn.lrange(@rname, -1, -1)
|
@@ -279,12 +297,18 @@ module Sidekiq
|
|
279
297
|
##
|
280
298
|
# Find the job with the given JID within this queue.
|
281
299
|
#
|
282
|
-
# This is a slow, inefficient operation. Do not use under
|
300
|
+
# This is a *slow, inefficient* operation. Do not use under
|
283
301
|
# normal conditions.
|
302
|
+
#
|
303
|
+
# @param jid [String] the job_id to look for
|
304
|
+
# @return [Sidekiq::JobRecord]
|
305
|
+
# @return [nil] if not found
|
284
306
|
def find_job(jid)
|
285
307
|
detect { |j| j.jid == jid }
|
286
308
|
end
|
287
309
|
|
310
|
+
# delete all jobs within this queue
|
311
|
+
# @return [Boolean] true
|
288
312
|
def clear
|
289
313
|
Sidekiq.redis do |conn|
|
290
314
|
conn.multi do |transaction|
|
@@ -292,21 +316,35 @@ module Sidekiq
|
|
292
316
|
transaction.srem("queues", name)
|
293
317
|
end
|
294
318
|
end
|
319
|
+
true
|
295
320
|
end
|
296
321
|
alias_method :💣, :clear
|
322
|
+
|
323
|
+
# :nodoc:
|
324
|
+
# @api private
|
325
|
+
def as_json(options = nil)
|
326
|
+
{name: name} # 5336
|
327
|
+
end
|
297
328
|
end
|
298
329
|
|
299
330
|
##
|
300
|
-
#
|
301
|
-
# sorted set.
|
331
|
+
# Represents a pending job within a Sidekiq queue.
|
302
332
|
#
|
303
333
|
# The job should be considered immutable but may be
|
304
334
|
# removed from the queue via JobRecord#delete.
|
305
|
-
#
|
306
335
|
class JobRecord
|
336
|
+
# the parsed Hash of job data
|
337
|
+
# @!attribute [r] Item
|
307
338
|
attr_reader :item
|
339
|
+
# the underlying String in Redis
|
340
|
+
# @!attribute [r] Value
|
308
341
|
attr_reader :value
|
342
|
+
# the queue associated with this job
|
343
|
+
# @!attribute [r] Queue
|
344
|
+
attr_reader :queue
|
309
345
|
|
346
|
+
# :nodoc:
|
347
|
+
# @api private
|
310
348
|
def initialize(item, queue_name = nil)
|
311
349
|
@args = nil
|
312
350
|
@value = item
|
@@ -314,6 +352,8 @@ module Sidekiq
|
|
314
352
|
@queue = queue_name || @item["queue"]
|
315
353
|
end
|
316
354
|
|
355
|
+
# :nodoc:
|
356
|
+
# @api private
|
317
357
|
def parse(item)
|
318
358
|
Sidekiq.load_json(item)
|
319
359
|
rescue JSON::ParserError
|
@@ -325,6 +365,8 @@ module Sidekiq
|
|
325
365
|
{}
|
326
366
|
end
|
327
367
|
|
368
|
+
# This is the job class which Sidekiq will execute. If using ActiveJob,
|
369
|
+
# this class will be the ActiveJob adapter class rather than a specific job.
|
328
370
|
def klass
|
329
371
|
self["class"]
|
330
372
|
end
|
@@ -412,15 +454,12 @@ module Sidekiq
|
|
412
454
|
end
|
413
455
|
end
|
414
456
|
|
415
|
-
attr_reader :queue
|
416
|
-
|
417
457
|
def latency
|
418
458
|
now = Time.now.to_f
|
419
459
|
now - (@item["enqueued_at"] || @item["created_at"] || now)
|
420
460
|
end
|
421
461
|
|
422
|
-
|
423
|
-
# Remove this job from the queue.
|
462
|
+
# Remove this job from the queue
|
424
463
|
def delete
|
425
464
|
count = Sidekiq.redis { |conn|
|
426
465
|
conn.lrem("queue:#{@queue}", 1, @value)
|
@@ -428,6 +467,7 @@ module Sidekiq
|
|
428
467
|
count != 0
|
429
468
|
end
|
430
469
|
|
470
|
+
# Access arbitrary attributes within the job hash
|
431
471
|
def [](name)
|
432
472
|
# nil will happen if the JSON fails to parse.
|
433
473
|
# We don't guarantee Sidekiq will work with bad job JSON but we should
|
@@ -442,7 +482,8 @@ module Sidekiq
|
|
442
482
|
rescue => ex
|
443
483
|
# #1761 in dev mode, it's possible to have jobs enqueued which haven't been loaded into
|
444
484
|
# memory yet so the YAML can't be loaded.
|
445
|
-
|
485
|
+
# TODO is this still necessary? Zeitwerk reloader should handle?
|
486
|
+
Sidekiq.logger.warn "Unable to load YAML: #{ex.message}" unless Sidekiq.config[:environment] == "development"
|
446
487
|
default
|
447
488
|
end
|
448
489
|
|
@@ -464,20 +505,28 @@ module Sidekiq
|
|
464
505
|
end
|
465
506
|
end
|
466
507
|
|
508
|
+
# Represents a job within a Redis sorted set where the score
|
509
|
+
# represents a timestamp associated with the job. This timestamp
|
510
|
+
# could be the scheduled time for it to run (e.g. scheduled set),
|
511
|
+
# or the expiration date after which the entry should be deleted (e.g. dead set).
|
467
512
|
class SortedEntry < JobRecord
|
468
513
|
attr_reader :score
|
469
514
|
attr_reader :parent
|
470
515
|
|
516
|
+
# :nodoc:
|
517
|
+
# @api private
|
471
518
|
def initialize(parent, score, item)
|
472
519
|
super(item)
|
473
|
-
@score = score
|
520
|
+
@score = Float(score)
|
474
521
|
@parent = parent
|
475
522
|
end
|
476
523
|
|
524
|
+
# The timestamp associated with this entry
|
477
525
|
def at
|
478
526
|
Time.at(score).utc
|
479
527
|
end
|
480
528
|
|
529
|
+
# remove this entry from the sorted set
|
481
530
|
def delete
|
482
531
|
if @value
|
483
532
|
@parent.delete_by_value(@parent.name, @value)
|
@@ -486,12 +535,17 @@ module Sidekiq
|
|
486
535
|
end
|
487
536
|
end
|
488
537
|
|
538
|
+
# Change the scheduled time for this job.
|
539
|
+
#
|
540
|
+
# @param at [Time] the new timestamp for this job
|
489
541
|
def reschedule(at)
|
490
542
|
Sidekiq.redis do |conn|
|
491
543
|
conn.zincrby(@parent.name, at.to_f - @score, Sidekiq.dump_json(@item))
|
492
544
|
end
|
493
545
|
end
|
494
546
|
|
547
|
+
# Enqueue this job from the scheduled or dead set so it will
|
548
|
+
# be executed at some point in the near future.
|
495
549
|
def add_to_queue
|
496
550
|
remove_job do |message|
|
497
551
|
msg = Sidekiq.load_json(message)
|
@@ -499,6 +553,8 @@ module Sidekiq
|
|
499
553
|
end
|
500
554
|
end
|
501
555
|
|
556
|
+
# enqueue this job from the retry set so it will be executed
|
557
|
+
# at some point in the near future.
|
502
558
|
def retry
|
503
559
|
remove_job do |message|
|
504
560
|
msg = Sidekiq.load_json(message)
|
@@ -507,8 +563,7 @@ module Sidekiq
|
|
507
563
|
end
|
508
564
|
end
|
509
565
|
|
510
|
-
|
511
|
-
# Place job in the dead set
|
566
|
+
# Move this job from its current set into the Dead set.
|
512
567
|
def kill
|
513
568
|
remove_job do |message|
|
514
569
|
DeadSet.new.kill(message)
|
@@ -556,20 +611,32 @@ module Sidekiq
|
|
556
611
|
end
|
557
612
|
end
|
558
613
|
|
614
|
+
# Base class for all sorted sets within Sidekiq.
|
559
615
|
class SortedSet
|
560
616
|
include Enumerable
|
561
617
|
|
618
|
+
# Redis key of the set
|
619
|
+
# @!attribute [r] Name
|
562
620
|
attr_reader :name
|
563
621
|
|
622
|
+
# :nodoc:
|
623
|
+
# @api private
|
564
624
|
def initialize(name)
|
565
625
|
@name = name
|
566
626
|
@_size = size
|
567
627
|
end
|
568
628
|
|
629
|
+
# real-time size of the set, will change
|
569
630
|
def size
|
570
631
|
Sidekiq.redis { |c| c.zcard(name) }
|
571
632
|
end
|
572
633
|
|
634
|
+
# Scan through each element of the sorted set, yielding each to the supplied block.
|
635
|
+
# Please see Redis's <a href="https://redis.io/commands/scan/">SCAN documentation</a> for implementation details.
|
636
|
+
#
|
637
|
+
# @param match [String] a snippet or regexp to filter matches.
|
638
|
+
# @param count [Integer] number of elements to retrieve at a time, default 100
|
639
|
+
# @yieldparam [Sidekiq::SortedEntry] each entry
|
573
640
|
def scan(match, count = 100)
|
574
641
|
return to_enum(:scan, match, count) unless block_given?
|
575
642
|
|
@@ -581,18 +648,32 @@ module Sidekiq
|
|
581
648
|
end
|
582
649
|
end
|
583
650
|
|
651
|
+
# @return [Boolean] always true
|
584
652
|
def clear
|
585
653
|
Sidekiq.redis do |conn|
|
586
654
|
conn.unlink(name)
|
587
655
|
end
|
656
|
+
true
|
588
657
|
end
|
589
658
|
alias_method :💣, :clear
|
659
|
+
|
660
|
+
# :nodoc:
|
661
|
+
# @api private
|
662
|
+
def as_json(options = nil)
|
663
|
+
{name: name} # 5336
|
664
|
+
end
|
590
665
|
end
|
591
666
|
|
667
|
+
# Base class for all sorted sets which contain jobs, e.g. scheduled, retry and dead.
|
668
|
+
# Sidekiq Pro and Enterprise add additional sorted sets which do not contain job data,
|
669
|
+
# e.g. Batches.
|
592
670
|
class JobSet < SortedSet
|
593
|
-
|
671
|
+
# Add a job with the associated timestamp to this set.
|
672
|
+
# @param timestamp [Time] the score for the job
|
673
|
+
# @param job [Hash] the job data
|
674
|
+
def schedule(timestamp, job)
|
594
675
|
Sidekiq.redis do |conn|
|
595
|
-
conn.zadd(name, timestamp.to_f.to_s, Sidekiq.dump_json(
|
676
|
+
conn.zadd(name, timestamp.to_f.to_s, Sidekiq.dump_json(job))
|
596
677
|
end
|
597
678
|
end
|
598
679
|
|
@@ -606,7 +687,7 @@ module Sidekiq
|
|
606
687
|
range_start = page * page_size + offset_size
|
607
688
|
range_end = range_start + page_size - 1
|
608
689
|
elements = Sidekiq.redis { |conn|
|
609
|
-
conn.zrange name, range_start, range_end,
|
690
|
+
conn.zrange name, range_start, range_end, withscores: true
|
610
691
|
}
|
611
692
|
break if elements.empty?
|
612
693
|
page -= 1
|
@@ -620,6 +701,10 @@ module Sidekiq
|
|
620
701
|
##
|
621
702
|
# Fetch jobs that match a given time or Range. Job ID is an
|
622
703
|
# optional second argument.
|
704
|
+
#
|
705
|
+
# @param score [Time,Range] a specific timestamp or range
|
706
|
+
# @param jid [String, optional] find a specific JID within the score
|
707
|
+
# @return [Array<SortedEntry>] any results found, can be empty
|
623
708
|
def fetch(score, jid = nil)
|
624
709
|
begin_score, end_score =
|
625
710
|
if score.is_a?(Range)
|
@@ -629,7 +714,7 @@ module Sidekiq
|
|
629
714
|
end
|
630
715
|
|
631
716
|
elements = Sidekiq.redis { |conn|
|
632
|
-
conn.zrangebyscore(name, begin_score, end_score,
|
717
|
+
conn.zrangebyscore(name, begin_score, end_score, withscores: true)
|
633
718
|
}
|
634
719
|
|
635
720
|
elements.each_with_object([]) do |element, result|
|
@@ -641,7 +726,10 @@ module Sidekiq
|
|
641
726
|
|
642
727
|
##
|
643
728
|
# Find the job with the given JID within this sorted set.
|
644
|
-
# This is a
|
729
|
+
# *This is a slow O(n) operation*. Do not use for app logic.
|
730
|
+
#
|
731
|
+
# @param jid [String] the job identifier
|
732
|
+
# @return [SortedEntry] the record or nil
|
645
733
|
def find_job(jid)
|
646
734
|
Sidekiq.redis do |conn|
|
647
735
|
conn.zscan_each(name, match: "*#{jid}*", count: 100) do |entry, score|
|
@@ -653,6 +741,8 @@ module Sidekiq
|
|
653
741
|
nil
|
654
742
|
end
|
655
743
|
|
744
|
+
# :nodoc:
|
745
|
+
# @api private
|
656
746
|
def delete_by_value(name, value)
|
657
747
|
Sidekiq.redis do |conn|
|
658
748
|
ret = conn.zrem(name, value)
|
@@ -661,6 +751,8 @@ module Sidekiq
|
|
661
751
|
end
|
662
752
|
end
|
663
753
|
|
754
|
+
# :nodoc:
|
755
|
+
# @api private
|
664
756
|
def delete_by_jid(score, jid)
|
665
757
|
Sidekiq.redis do |conn|
|
666
758
|
elements = conn.zrangebyscore(name, score, score)
|
@@ -681,10 +773,10 @@ module Sidekiq
|
|
681
773
|
end
|
682
774
|
|
683
775
|
##
|
684
|
-
#
|
776
|
+
# The set of scheduled jobs within Sidekiq.
|
685
777
|
# Based on this, you can search/filter for jobs. Here's an
|
686
|
-
# example where I'm selecting
|
687
|
-
# and deleting them from the
|
778
|
+
# example where I'm selecting jobs based on some complex logic
|
779
|
+
# and deleting them from the scheduled set.
|
688
780
|
#
|
689
781
|
# r = Sidekiq::ScheduledSet.new
|
690
782
|
# r.select do |scheduled|
|
@@ -699,7 +791,7 @@ module Sidekiq
|
|
699
791
|
end
|
700
792
|
|
701
793
|
##
|
702
|
-
#
|
794
|
+
# The set of retries within Sidekiq.
|
703
795
|
# Based on this, you can search/filter for jobs. Here's an
|
704
796
|
# example where I'm selecting all jobs of a certain type
|
705
797
|
# and deleting them from the retry queue.
|
@@ -715,23 +807,29 @@ module Sidekiq
|
|
715
807
|
super "retry"
|
716
808
|
end
|
717
809
|
|
810
|
+
# Enqueues all jobs pending within the retry set.
|
718
811
|
def retry_all
|
719
812
|
each(&:retry) while size > 0
|
720
813
|
end
|
721
814
|
|
815
|
+
# Kills all jobs pending within the retry set.
|
722
816
|
def kill_all
|
723
817
|
each(&:kill) while size > 0
|
724
818
|
end
|
725
819
|
end
|
726
820
|
|
727
821
|
##
|
728
|
-
#
|
822
|
+
# The set of dead jobs within Sidekiq. Dead jobs have failed all of
|
823
|
+
# their retries and are helding in this set pending some sort of manual
|
824
|
+
# fix. They will be removed after 6 months (dead_timeout) if not.
|
729
825
|
#
|
730
826
|
class DeadSet < JobSet
|
731
827
|
def initialize
|
732
828
|
super "dead"
|
733
829
|
end
|
734
830
|
|
831
|
+
# Add the given job to the Dead set.
|
832
|
+
# @param message [String] the job data as JSON
|
735
833
|
def kill(message, opts = {})
|
736
834
|
now = Time.now.to_f
|
737
835
|
Sidekiq.redis do |conn|
|
@@ -753,16 +851,21 @@ module Sidekiq
|
|
753
851
|
true
|
754
852
|
end
|
755
853
|
|
854
|
+
# Enqueue all dead jobs
|
756
855
|
def retry_all
|
757
856
|
each(&:retry) while size > 0
|
758
857
|
end
|
759
858
|
|
859
|
+
# The maximum size of the Dead set. Older entries will be trimmed
|
860
|
+
# to stay within this limit. Default value is 10,000.
|
760
861
|
def self.max_jobs
|
761
|
-
Sidekiq
|
862
|
+
Sidekiq[:dead_max_jobs]
|
762
863
|
end
|
763
864
|
|
865
|
+
# The time limit for entries within the Dead set. Older entries will be thrown away.
|
866
|
+
# Default value is six months.
|
764
867
|
def self.timeout
|
765
|
-
Sidekiq
|
868
|
+
Sidekiq[:dead_timeout_in_seconds]
|
766
869
|
end
|
767
870
|
end
|
768
871
|
|
@@ -771,18 +874,23 @@ module Sidekiq
|
|
771
874
|
# right now. Each process sends a heartbeat to Redis every 5 seconds
|
772
875
|
# so this set should be relatively accurate, barring network partitions.
|
773
876
|
#
|
774
|
-
#
|
877
|
+
# @yieldparam [Sidekiq::Process]
|
775
878
|
#
|
776
879
|
class ProcessSet
|
777
880
|
include Enumerable
|
778
881
|
|
882
|
+
# :nodoc:
|
883
|
+
# @api private
|
779
884
|
def initialize(clean_plz = true)
|
780
885
|
cleanup if clean_plz
|
781
886
|
end
|
782
887
|
|
783
888
|
# Cleans up dead processes recorded in Redis.
|
784
889
|
# Returns the number of processes cleaned.
|
890
|
+
# :nodoc:
|
891
|
+
# @api private
|
785
892
|
def cleanup
|
893
|
+
return 0 unless Sidekiq.redis { |conn| conn.set("process_cleanup", "1", nx: true, ex: 60) }
|
786
894
|
count = 0
|
787
895
|
Sidekiq.redis do |conn|
|
788
896
|
procs = conn.sscan_each("processes").to_a.sort
|
@@ -836,6 +944,7 @@ module Sidekiq
|
|
836
944
|
# based on current heartbeat. #each does that and ensures the set only
|
837
945
|
# contains Sidekiq processes which have sent a heartbeat within the last
|
838
946
|
# 60 seconds.
|
947
|
+
# @return [Integer] current number of registered Sidekiq processes
|
839
948
|
def size
|
840
949
|
Sidekiq.redis { |conn| conn.scard("processes") }
|
841
950
|
end
|
@@ -843,10 +952,12 @@ module Sidekiq
|
|
843
952
|
# Total number of threads available to execute jobs.
|
844
953
|
# For Sidekiq Enterprise customers this number (in production) must be
|
845
954
|
# less than or equal to your licensed concurrency.
|
955
|
+
# @return [Integer] the sum of process concurrency
|
846
956
|
def total_concurrency
|
847
957
|
sum { |x| x["concurrency"].to_i }
|
848
958
|
end
|
849
959
|
|
960
|
+
# @return [Integer] total amount of RSS memory consumed by Sidekiq processes
|
850
961
|
def total_rss_in_kb
|
851
962
|
sum { |x| x["rss"].to_i }
|
852
963
|
end
|
@@ -855,6 +966,8 @@ module Sidekiq
|
|
855
966
|
# Returns the identity of the current cluster leader or "" if no leader.
|
856
967
|
# This is a Sidekiq Enterprise feature, will always return "" in Sidekiq
|
857
968
|
# or Sidekiq Pro.
|
969
|
+
# @return [String] Identity of cluster leader
|
970
|
+
# @return [String] empty string if no leader
|
858
971
|
def leader
|
859
972
|
@leader ||= begin
|
860
973
|
x = Sidekiq.redis { |c| c.get("dear-leader") }
|
@@ -881,6 +994,8 @@ module Sidekiq
|
|
881
994
|
# 'identity' => <unique string identifying the process>,
|
882
995
|
# }
|
883
996
|
class Process
|
997
|
+
# :nodoc:
|
998
|
+
# @api private
|
884
999
|
def initialize(hash)
|
885
1000
|
@attribs = hash
|
886
1001
|
end
|
@@ -905,18 +1020,31 @@ module Sidekiq
|
|
905
1020
|
self["queues"]
|
906
1021
|
end
|
907
1022
|
|
1023
|
+
# Signal this process to stop processing new jobs.
|
1024
|
+
# It will continue to execute jobs it has already fetched.
|
1025
|
+
# This method is *asynchronous* and it can take 5-10
|
1026
|
+
# seconds for the process to quiet.
|
908
1027
|
def quiet!
|
909
1028
|
signal("TSTP")
|
910
1029
|
end
|
911
1030
|
|
1031
|
+
# Signal this process to shutdown.
|
1032
|
+
# It will shutdown within its configured :timeout value, default 25 seconds.
|
1033
|
+
# This method is *asynchronous* and it can take 5-10
|
1034
|
+
# seconds for the process to start shutting down.
|
912
1035
|
def stop!
|
913
1036
|
signal("TERM")
|
914
1037
|
end
|
915
1038
|
|
1039
|
+
# Signal this process to log backtraces for all threads.
|
1040
|
+
# Useful if you have a frozen or deadlocked process which is
|
1041
|
+
# still sending a heartbeat.
|
1042
|
+
# This method is *asynchronous* and it can take 5-10 seconds.
|
916
1043
|
def dump_threads
|
917
1044
|
signal("TTIN")
|
918
1045
|
end
|
919
1046
|
|
1047
|
+
# @return [Boolean] true if this process is quiet or shutting down
|
920
1048
|
def stopping?
|
921
1049
|
self["quiet"] == "true"
|
922
1050
|
end
|