sidekiq 6.4.1 → 6.5.6

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.

Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +81 -1
  3. data/README.md +1 -1
  4. data/bin/sidekiqload +16 -10
  5. data/lib/sidekiq/api.rb +189 -58
  6. data/lib/sidekiq/cli.rb +39 -37
  7. data/lib/sidekiq/client.rb +26 -27
  8. data/lib/sidekiq/component.rb +65 -0
  9. data/lib/sidekiq/delay.rb +1 -1
  10. data/lib/sidekiq/extensions/generic_proxy.rb +1 -1
  11. data/lib/sidekiq/fetch.rb +18 -16
  12. data/lib/sidekiq/job_retry.rb +73 -52
  13. data/lib/sidekiq/job_util.rb +15 -9
  14. data/lib/sidekiq/launcher.rb +37 -33
  15. data/lib/sidekiq/logger.rb +5 -19
  16. data/lib/sidekiq/manager.rb +28 -25
  17. data/lib/sidekiq/metrics/deploy.rb +47 -0
  18. data/lib/sidekiq/metrics/query.rb +153 -0
  19. data/lib/sidekiq/metrics/shared.rb +94 -0
  20. data/lib/sidekiq/metrics/tracking.rb +134 -0
  21. data/lib/sidekiq/middleware/chain.rb +82 -38
  22. data/lib/sidekiq/middleware/current_attributes.rb +10 -4
  23. data/lib/sidekiq/middleware/i18n.rb +6 -4
  24. data/lib/sidekiq/middleware/modules.rb +21 -0
  25. data/lib/sidekiq/monitor.rb +1 -1
  26. data/lib/sidekiq/paginator.rb +2 -2
  27. data/lib/sidekiq/processor.rb +47 -41
  28. data/lib/sidekiq/rails.rb +15 -8
  29. data/lib/sidekiq/redis_client_adapter.rb +154 -0
  30. data/lib/sidekiq/redis_connection.rb +80 -49
  31. data/lib/sidekiq/ring_buffer.rb +29 -0
  32. data/lib/sidekiq/scheduled.rb +12 -17
  33. data/lib/sidekiq/testing/inline.rb +4 -4
  34. data/lib/sidekiq/testing.rb +37 -36
  35. data/lib/sidekiq/transaction_aware_client.rb +45 -0
  36. data/lib/sidekiq/version.rb +1 -1
  37. data/lib/sidekiq/web/action.rb +3 -3
  38. data/lib/sidekiq/web/application.rb +18 -5
  39. data/lib/sidekiq/web/csrf_protection.rb +2 -2
  40. data/lib/sidekiq/web/helpers.rb +28 -5
  41. data/lib/sidekiq/web.rb +5 -1
  42. data/lib/sidekiq/worker.rb +18 -13
  43. data/lib/sidekiq.rb +106 -31
  44. data/sidekiq.gemspec +2 -2
  45. data/web/assets/javascripts/application.js +58 -26
  46. data/web/assets/javascripts/chart.min.js +13 -0
  47. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  48. data/web/assets/javascripts/dashboard.js +0 -17
  49. data/web/assets/javascripts/graph.js +16 -0
  50. data/web/assets/javascripts/metrics.js +262 -0
  51. data/web/assets/stylesheets/application.css +45 -3
  52. data/web/locales/el.yml +43 -19
  53. data/web/locales/en.yml +7 -0
  54. data/web/locales/pt-br.yml +27 -9
  55. data/web/views/_nav.erb +1 -1
  56. data/web/views/_summary.erb +1 -1
  57. data/web/views/busy.erb +4 -4
  58. data/web/views/dashboard.erb +1 -0
  59. data/web/views/metrics.erb +69 -0
  60. data/web/views/metrics_for_job.erb +87 -0
  61. data/web/views/queue.erb +5 -1
  62. metadata +27 -8
  63. data/lib/sidekiq/exception_handler.rb +0 -27
  64. data/lib/sidekiq/util.rb +0 -108
data/lib/sidekiq/api.rb CHANGED
@@ -3,9 +3,20 @@
3
3
  require "sidekiq"
4
4
 
5
5
  require "zlib"
6
+ require "set"
6
7
  require "base64"
7
8
 
9
+ if ENV["SIDEKIQ_METRICS_BETA"]
10
+ require "sidekiq/metrics/deploy"
11
+ require "sidekiq/metrics/query"
12
+ end
13
+
8
14
  module Sidekiq
15
+ # Retrieve runtime statistics from Redis regarding
16
+ # this Sidekiq cluster.
17
+ #
18
+ # stat = Sidekiq::Stats.new
19
+ # stat.processed
9
20
  class Stats
10
21
  def initialize
11
22
  fetch_stats_fast!
@@ -52,6 +63,7 @@ module Sidekiq
52
63
  end
53
64
 
54
65
  # O(1) redis calls
66
+ # @api private
55
67
  def fetch_stats_fast!
56
68
  pipe1_res = Sidekiq.redis { |conn|
57
69
  conn.pipelined do |pipeline|
@@ -91,6 +103,7 @@ module Sidekiq
91
103
  end
92
104
 
93
105
  # O(number of processes + number of queues) redis calls
106
+ # @api private
94
107
  def fetch_stats_slow!
95
108
  processes = Sidekiq.redis { |conn|
96
109
  conn.sscan_each("processes").to_a
@@ -116,11 +129,13 @@ module Sidekiq
116
129
  @stats
117
130
  end
118
131
 
132
+ # @api private
119
133
  def fetch_stats!
120
134
  fetch_stats_fast!
121
135
  fetch_stats_slow!
122
136
  end
123
137
 
138
+ # @api private
124
139
  def reset(*stats)
125
140
  all = %w[failed processed]
126
141
  stats = stats.empty? ? all : all & stats.flatten.compact.map(&:to_s)
@@ -191,7 +206,7 @@ module Sidekiq
191
206
  stat_hash[dates[idx]] = value ? value.to_i : 0
192
207
  end
193
208
  end
194
- rescue Redis::CommandError
209
+ rescue RedisConnection.adapter::CommandError
195
210
  # mget will trigger a CROSSSLOT error when run against a Cluster
196
211
  # TODO Someone want to add Cluster support?
197
212
  end
@@ -202,9 +217,10 @@ module Sidekiq
202
217
  end
203
218
 
204
219
  ##
205
- # Encapsulates a queue within Sidekiq.
220
+ # Represents a queue within Sidekiq.
206
221
  # Allows enumeration of all jobs within the queue
207
- # and deletion of jobs.
222
+ # and deletion of jobs. NB: this queue data is real-time
223
+ # and is changing within Redis moment by moment.
208
224
  #
209
225
  # queue = Sidekiq::Queue.new("mailer")
210
226
  # queue.each do |job|
@@ -212,29 +228,34 @@ module Sidekiq
212
228
  # job.args # => [1, 2, 3]
213
229
  # job.delete if job.jid == 'abcdef1234567890'
214
230
  # end
215
- #
216
231
  class Queue
217
232
  include Enumerable
218
233
 
219
234
  ##
220
- # Return all known queues within Redis.
235
+ # Fetch all known queues within Redis.
221
236
  #
237
+ # @return [Array<Sidekiq::Queue>]
222
238
  def self.all
223
239
  Sidekiq.redis { |c| c.sscan_each("queues").to_a }.sort.map { |q| Sidekiq::Queue.new(q) }
224
240
  end
225
241
 
226
242
  attr_reader :name
227
243
 
244
+ # @param name [String] the name of the queue
228
245
  def initialize(name = "default")
229
246
  @name = name.to_s
230
247
  @rname = "queue:#{name}"
231
248
  end
232
249
 
250
+ # The current size of the queue within Redis.
251
+ # This value is real-time and can change between calls.
252
+ #
253
+ # @return [Integer] the size
233
254
  def size
234
255
  Sidekiq.redis { |con| con.llen(@rname) }
235
256
  end
236
257
 
237
- # Sidekiq Pro overrides this
258
+ # @return [Boolean] if the queue is currently paused
238
259
  def paused?
239
260
  false
240
261
  end
@@ -243,7 +264,7 @@ module Sidekiq
243
264
  # Calculates this queue's latency, the difference in seconds since the oldest
244
265
  # job in the queue was enqueued.
245
266
  #
246
- # @return Float
267
+ # @return [Float] in seconds
247
268
  def latency
248
269
  entry = Sidekiq.redis { |conn|
249
270
  conn.lrange(@rname, -1, -1)
@@ -279,34 +300,54 @@ module Sidekiq
279
300
  ##
280
301
  # Find the job with the given JID within this queue.
281
302
  #
282
- # This is a slow, inefficient operation. Do not use under
303
+ # This is a *slow, inefficient* operation. Do not use under
283
304
  # normal conditions.
305
+ #
306
+ # @param jid [String] the job_id to look for
307
+ # @return [Sidekiq::JobRecord]
308
+ # @return [nil] if not found
284
309
  def find_job(jid)
285
310
  detect { |j| j.jid == jid }
286
311
  end
287
312
 
313
+ # delete all jobs within this queue
314
+ # @return [Boolean] true
288
315
  def clear
289
316
  Sidekiq.redis do |conn|
290
317
  conn.multi do |transaction|
291
318
  transaction.unlink(@rname)
292
- transaction.srem("queues", name)
319
+ transaction.srem("queues", [name])
293
320
  end
294
321
  end
322
+ true
295
323
  end
296
324
  alias_method :💣, :clear
325
+
326
+ # :nodoc:
327
+ # @api private
328
+ def as_json(options = nil)
329
+ {name: name} # 5336
330
+ end
297
331
  end
298
332
 
299
333
  ##
300
- # Encapsulates a pending job within a Sidekiq queue or
301
- # sorted set.
334
+ # Represents a pending job within a Sidekiq queue.
302
335
  #
303
336
  # The job should be considered immutable but may be
304
337
  # removed from the queue via JobRecord#delete.
305
- #
306
338
  class JobRecord
339
+ # the parsed Hash of job data
340
+ # @!attribute [r] Item
307
341
  attr_reader :item
342
+ # the underlying String in Redis
343
+ # @!attribute [r] Value
308
344
  attr_reader :value
345
+ # the queue associated with this job
346
+ # @!attribute [r] Queue
347
+ attr_reader :queue
309
348
 
349
+ # :nodoc:
350
+ # @api private
310
351
  def initialize(item, queue_name = nil)
311
352
  @args = nil
312
353
  @value = item
@@ -314,6 +355,8 @@ module Sidekiq
314
355
  @queue = queue_name || @item["queue"]
315
356
  end
316
357
 
358
+ # :nodoc:
359
+ # @api private
317
360
  def parse(item)
318
361
  Sidekiq.load_json(item)
319
362
  rescue JSON::ParserError
@@ -325,6 +368,8 @@ module Sidekiq
325
368
  {}
326
369
  end
327
370
 
371
+ # This is the job class which Sidekiq will execute. If using ActiveJob,
372
+ # this class will be the ActiveJob adapter class rather than a specific job.
328
373
  def klass
329
374
  self["class"]
330
375
  end
@@ -354,31 +399,31 @@ module Sidekiq
354
399
  def display_args
355
400
  # Unwrap known wrappers so they show up in a human-friendly manner in the Web UI
356
401
  @display_args ||= case klass
357
- when /\ASidekiq::Extensions::Delayed/
358
- safe_load(args[0], args) do |_, _, arg, kwarg|
359
- if !kwarg || kwarg.empty?
360
- arg
361
- else
362
- [arg, kwarg]
363
- end
364
- end
365
- when "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
366
- job_args = self["wrapped"] ? args[0]["arguments"] : []
367
- if (self["wrapped"] || args[0]) == "ActionMailer::DeliveryJob"
368
- # remove MailerClass, mailer_method and 'deliver_now'
369
- job_args.drop(3)
370
- elsif (self["wrapped"] || args[0]) == "ActionMailer::MailDeliveryJob"
371
- # remove MailerClass, mailer_method and 'deliver_now'
372
- job_args.drop(3).first["args"]
373
- else
374
- job_args
375
- end
376
- else
377
- if self["encrypt"]
378
- # no point in showing 150+ bytes of random garbage
379
- args[-1] = "[encrypted data]"
380
- end
381
- args
402
+ when /\ASidekiq::Extensions::Delayed/
403
+ safe_load(args[0], args) do |_, _, arg, kwarg|
404
+ if !kwarg || kwarg.empty?
405
+ arg
406
+ else
407
+ [arg, kwarg]
408
+ end
409
+ end
410
+ when "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
411
+ job_args = self["wrapped"] ? args[0]["arguments"] : []
412
+ if (self["wrapped"] || args[0]) == "ActionMailer::DeliveryJob"
413
+ # remove MailerClass, mailer_method and 'deliver_now'
414
+ job_args.drop(3)
415
+ elsif (self["wrapped"] || args[0]) == "ActionMailer::MailDeliveryJob"
416
+ # remove MailerClass, mailer_method and 'deliver_now'
417
+ job_args.drop(3).first["args"]
418
+ else
419
+ job_args
420
+ end
421
+ else
422
+ if self["encrypt"]
423
+ # no point in showing 150+ bytes of random garbage
424
+ args[-1] = "[encrypted data]"
425
+ end
426
+ args
382
427
  end
383
428
  end
384
429
 
@@ -412,15 +457,12 @@ module Sidekiq
412
457
  end
413
458
  end
414
459
 
415
- attr_reader :queue
416
-
417
460
  def latency
418
461
  now = Time.now.to_f
419
462
  now - (@item["enqueued_at"] || @item["created_at"] || now)
420
463
  end
421
464
 
422
- ##
423
- # Remove this job from the queue.
465
+ # Remove this job from the queue
424
466
  def delete
425
467
  count = Sidekiq.redis { |conn|
426
468
  conn.lrem("queue:#{@queue}", 1, @value)
@@ -428,6 +470,7 @@ module Sidekiq
428
470
  count != 0
429
471
  end
430
472
 
473
+ # Access arbitrary attributes within the job hash
431
474
  def [](name)
432
475
  # nil will happen if the JSON fails to parse.
433
476
  # We don't guarantee Sidekiq will work with bad job JSON but we should
@@ -442,6 +485,7 @@ module Sidekiq
442
485
  rescue => ex
443
486
  # #1761 in dev mode, it's possible to have jobs enqueued which haven't been loaded into
444
487
  # memory yet so the YAML can't be loaded.
488
+ # TODO is this still necessary? Zeitwerk reloader should handle?
445
489
  Sidekiq.logger.warn "Unable to load YAML: #{ex.message}" unless Sidekiq.options[:environment] == "development"
446
490
  default
447
491
  end
@@ -464,20 +508,28 @@ module Sidekiq
464
508
  end
465
509
  end
466
510
 
511
+ # Represents a job within a Redis sorted set where the score
512
+ # represents a timestamp associated with the job. This timestamp
513
+ # could be the scheduled time for it to run (e.g. scheduled set),
514
+ # or the expiration date after which the entry should be deleted (e.g. dead set).
467
515
  class SortedEntry < JobRecord
468
516
  attr_reader :score
469
517
  attr_reader :parent
470
518
 
519
+ # :nodoc:
520
+ # @api private
471
521
  def initialize(parent, score, item)
472
522
  super(item)
473
- @score = score
523
+ @score = Float(score)
474
524
  @parent = parent
475
525
  end
476
526
 
527
+ # The timestamp associated with this entry
477
528
  def at
478
529
  Time.at(score).utc
479
530
  end
480
531
 
532
+ # remove this entry from the sorted set
481
533
  def delete
482
534
  if @value
483
535
  @parent.delete_by_value(@parent.name, @value)
@@ -486,12 +538,17 @@ module Sidekiq
486
538
  end
487
539
  end
488
540
 
541
+ # Change the scheduled time for this job.
542
+ #
543
+ # @param at [Time] the new timestamp for this job
489
544
  def reschedule(at)
490
545
  Sidekiq.redis do |conn|
491
546
  conn.zincrby(@parent.name, at.to_f - @score, Sidekiq.dump_json(@item))
492
547
  end
493
548
  end
494
549
 
550
+ # Enqueue this job from the scheduled or dead set so it will
551
+ # be executed at some point in the near future.
495
552
  def add_to_queue
496
553
  remove_job do |message|
497
554
  msg = Sidekiq.load_json(message)
@@ -499,6 +556,8 @@ module Sidekiq
499
556
  end
500
557
  end
501
558
 
559
+ # enqueue this job from the retry set so it will be executed
560
+ # at some point in the near future.
502
561
  def retry
503
562
  remove_job do |message|
504
563
  msg = Sidekiq.load_json(message)
@@ -507,8 +566,7 @@ module Sidekiq
507
566
  end
508
567
  end
509
568
 
510
- ##
511
- # Place job in the dead set
569
+ # Move this job from its current set into the Dead set.
512
570
  def kill
513
571
  remove_job do |message|
514
572
  DeadSet.new.kill(message)
@@ -556,20 +614,32 @@ module Sidekiq
556
614
  end
557
615
  end
558
616
 
617
+ # Base class for all sorted sets within Sidekiq.
559
618
  class SortedSet
560
619
  include Enumerable
561
620
 
621
+ # Redis key of the set
622
+ # @!attribute [r] Name
562
623
  attr_reader :name
563
624
 
625
+ # :nodoc:
626
+ # @api private
564
627
  def initialize(name)
565
628
  @name = name
566
629
  @_size = size
567
630
  end
568
631
 
632
+ # real-time size of the set, will change
569
633
  def size
570
634
  Sidekiq.redis { |c| c.zcard(name) }
571
635
  end
572
636
 
637
+ # Scan through each element of the sorted set, yielding each to the supplied block.
638
+ # Please see Redis's <a href="https://redis.io/commands/scan/">SCAN documentation</a> for implementation details.
639
+ #
640
+ # @param match [String] a snippet or regexp to filter matches.
641
+ # @param count [Integer] number of elements to retrieve at a time, default 100
642
+ # @yieldparam [Sidekiq::SortedEntry] each entry
573
643
  def scan(match, count = 100)
574
644
  return to_enum(:scan, match, count) unless block_given?
575
645
 
@@ -581,18 +651,32 @@ module Sidekiq
581
651
  end
582
652
  end
583
653
 
654
+ # @return [Boolean] always true
584
655
  def clear
585
656
  Sidekiq.redis do |conn|
586
657
  conn.unlink(name)
587
658
  end
659
+ true
588
660
  end
589
661
  alias_method :💣, :clear
662
+
663
+ # :nodoc:
664
+ # @api private
665
+ def as_json(options = nil)
666
+ {name: name} # 5336
667
+ end
590
668
  end
591
669
 
670
+ # Base class for all sorted sets which contain jobs, e.g. scheduled, retry and dead.
671
+ # Sidekiq Pro and Enterprise add additional sorted sets which do not contain job data,
672
+ # e.g. Batches.
592
673
  class JobSet < SortedSet
593
- def schedule(timestamp, message)
674
+ # Add a job with the associated timestamp to this set.
675
+ # @param timestamp [Time] the score for the job
676
+ # @param job [Hash] the job data
677
+ def schedule(timestamp, job)
594
678
  Sidekiq.redis do |conn|
595
- conn.zadd(name, timestamp.to_f.to_s, Sidekiq.dump_json(message))
679
+ conn.zadd(name, timestamp.to_f.to_s, Sidekiq.dump_json(job))
596
680
  end
597
681
  end
598
682
 
@@ -606,7 +690,7 @@ module Sidekiq
606
690
  range_start = page * page_size + offset_size
607
691
  range_end = range_start + page_size - 1
608
692
  elements = Sidekiq.redis { |conn|
609
- conn.zrange name, range_start, range_end, with_scores: true
693
+ conn.zrange name, range_start, range_end, withscores: true
610
694
  }
611
695
  break if elements.empty?
612
696
  page -= 1
@@ -620,6 +704,10 @@ module Sidekiq
620
704
  ##
621
705
  # Fetch jobs that match a given time or Range. Job ID is an
622
706
  # optional second argument.
707
+ #
708
+ # @param score [Time,Range] a specific timestamp or range
709
+ # @param jid [String, optional] find a specific JID within the score
710
+ # @return [Array<SortedEntry>] any results found, can be empty
623
711
  def fetch(score, jid = nil)
624
712
  begin_score, end_score =
625
713
  if score.is_a?(Range)
@@ -629,7 +717,7 @@ module Sidekiq
629
717
  end
630
718
 
631
719
  elements = Sidekiq.redis { |conn|
632
- conn.zrangebyscore(name, begin_score, end_score, with_scores: true)
720
+ conn.zrangebyscore(name, begin_score, end_score, withscores: true)
633
721
  }
634
722
 
635
723
  elements.each_with_object([]) do |element, result|
@@ -641,7 +729,10 @@ module Sidekiq
641
729
 
642
730
  ##
643
731
  # Find the job with the given JID within this sorted set.
644
- # This is a slower O(n) operation. Do not use for app logic.
732
+ # *This is a slow O(n) operation*. Do not use for app logic.
733
+ #
734
+ # @param jid [String] the job identifier
735
+ # @return [SortedEntry] the record or nil
645
736
  def find_job(jid)
646
737
  Sidekiq.redis do |conn|
647
738
  conn.zscan_each(name, match: "*#{jid}*", count: 100) do |entry, score|
@@ -653,6 +744,8 @@ module Sidekiq
653
744
  nil
654
745
  end
655
746
 
747
+ # :nodoc:
748
+ # @api private
656
749
  def delete_by_value(name, value)
657
750
  Sidekiq.redis do |conn|
658
751
  ret = conn.zrem(name, value)
@@ -661,6 +754,8 @@ module Sidekiq
661
754
  end
662
755
  end
663
756
 
757
+ # :nodoc:
758
+ # @api private
664
759
  def delete_by_jid(score, jid)
665
760
  Sidekiq.redis do |conn|
666
761
  elements = conn.zrangebyscore(name, score, score)
@@ -681,10 +776,10 @@ module Sidekiq
681
776
  end
682
777
 
683
778
  ##
684
- # Allows enumeration of scheduled jobs within Sidekiq.
779
+ # The set of scheduled jobs within Sidekiq.
685
780
  # Based on this, you can search/filter for jobs. Here's an
686
- # example where I'm selecting all jobs of a certain type
687
- # and deleting them from the schedule queue.
781
+ # example where I'm selecting jobs based on some complex logic
782
+ # and deleting them from the scheduled set.
688
783
  #
689
784
  # r = Sidekiq::ScheduledSet.new
690
785
  # r.select do |scheduled|
@@ -699,7 +794,7 @@ module Sidekiq
699
794
  end
700
795
 
701
796
  ##
702
- # Allows enumeration of retries within Sidekiq.
797
+ # The set of retries within Sidekiq.
703
798
  # Based on this, you can search/filter for jobs. Here's an
704
799
  # example where I'm selecting all jobs of a certain type
705
800
  # and deleting them from the retry queue.
@@ -715,23 +810,29 @@ module Sidekiq
715
810
  super "retry"
716
811
  end
717
812
 
813
+ # Enqueues all jobs pending within the retry set.
718
814
  def retry_all
719
815
  each(&:retry) while size > 0
720
816
  end
721
817
 
818
+ # Kills all jobs pending within the retry set.
722
819
  def kill_all
723
820
  each(&:kill) while size > 0
724
821
  end
725
822
  end
726
823
 
727
824
  ##
728
- # Allows enumeration of dead jobs within Sidekiq.
825
+ # The set of dead jobs within Sidekiq. Dead jobs have failed all of
826
+ # their retries and are helding in this set pending some sort of manual
827
+ # fix. They will be removed after 6 months (dead_timeout) if not.
729
828
  #
730
829
  class DeadSet < JobSet
731
830
  def initialize
732
831
  super "dead"
733
832
  end
734
833
 
834
+ # Add the given job to the Dead set.
835
+ # @param message [String] the job data as JSON
735
836
  def kill(message, opts = {})
736
837
  now = Time.now.to_f
737
838
  Sidekiq.redis do |conn|
@@ -753,16 +854,21 @@ module Sidekiq
753
854
  true
754
855
  end
755
856
 
857
+ # Enqueue all dead jobs
756
858
  def retry_all
757
859
  each(&:retry) while size > 0
758
860
  end
759
861
 
862
+ # The maximum size of the Dead set. Older entries will be trimmed
863
+ # to stay within this limit. Default value is 10,000.
760
864
  def self.max_jobs
761
- Sidekiq.options[:dead_max_jobs]
865
+ Sidekiq[:dead_max_jobs]
762
866
  end
763
867
 
868
+ # The time limit for entries within the Dead set. Older entries will be thrown away.
869
+ # Default value is six months.
764
870
  def self.timeout
765
- Sidekiq.options[:dead_timeout_in_seconds]
871
+ Sidekiq[:dead_timeout_in_seconds]
766
872
  end
767
873
  end
768
874
 
@@ -771,18 +877,23 @@ module Sidekiq
771
877
  # right now. Each process sends a heartbeat to Redis every 5 seconds
772
878
  # so this set should be relatively accurate, barring network partitions.
773
879
  #
774
- # Yields a Sidekiq::Process.
880
+ # @yieldparam [Sidekiq::Process]
775
881
  #
776
882
  class ProcessSet
777
883
  include Enumerable
778
884
 
885
+ # :nodoc:
886
+ # @api private
779
887
  def initialize(clean_plz = true)
780
888
  cleanup if clean_plz
781
889
  end
782
890
 
783
891
  # Cleans up dead processes recorded in Redis.
784
892
  # Returns the number of processes cleaned.
893
+ # :nodoc:
894
+ # @api private
785
895
  def cleanup
896
+ return 0 unless Sidekiq.redis { |conn| conn.set("process_cleanup", "1", nx: true, ex: 60) }
786
897
  count = 0
787
898
  Sidekiq.redis do |conn|
788
899
  procs = conn.sscan_each("processes").to_a.sort
@@ -836,6 +947,7 @@ module Sidekiq
836
947
  # based on current heartbeat. #each does that and ensures the set only
837
948
  # contains Sidekiq processes which have sent a heartbeat within the last
838
949
  # 60 seconds.
950
+ # @return [Integer] current number of registered Sidekiq processes
839
951
  def size
840
952
  Sidekiq.redis { |conn| conn.scard("processes") }
841
953
  end
@@ -843,10 +955,12 @@ module Sidekiq
843
955
  # Total number of threads available to execute jobs.
844
956
  # For Sidekiq Enterprise customers this number (in production) must be
845
957
  # less than or equal to your licensed concurrency.
958
+ # @return [Integer] the sum of process concurrency
846
959
  def total_concurrency
847
960
  sum { |x| x["concurrency"].to_i }
848
961
  end
849
962
 
963
+ # @return [Integer] total amount of RSS memory consumed by Sidekiq processes
850
964
  def total_rss_in_kb
851
965
  sum { |x| x["rss"].to_i }
852
966
  end
@@ -855,6 +969,8 @@ module Sidekiq
855
969
  # Returns the identity of the current cluster leader or "" if no leader.
856
970
  # This is a Sidekiq Enterprise feature, will always return "" in Sidekiq
857
971
  # or Sidekiq Pro.
972
+ # @return [String] Identity of cluster leader
973
+ # @return [String] empty string if no leader
858
974
  def leader
859
975
  @leader ||= begin
860
976
  x = Sidekiq.redis { |c| c.get("dear-leader") }
@@ -881,6 +997,8 @@ module Sidekiq
881
997
  # 'identity' => <unique string identifying the process>,
882
998
  # }
883
999
  class Process
1000
+ # :nodoc:
1001
+ # @api private
884
1002
  def initialize(hash)
885
1003
  @attribs = hash
886
1004
  end
@@ -905,18 +1023,31 @@ module Sidekiq
905
1023
  self["queues"]
906
1024
  end
907
1025
 
1026
+ # Signal this process to stop processing new jobs.
1027
+ # It will continue to execute jobs it has already fetched.
1028
+ # This method is *asynchronous* and it can take 5-10
1029
+ # seconds for the process to quiet.
908
1030
  def quiet!
909
1031
  signal("TSTP")
910
1032
  end
911
1033
 
1034
+ # Signal this process to shutdown.
1035
+ # It will shutdown within its configured :timeout value, default 25 seconds.
1036
+ # This method is *asynchronous* and it can take 5-10
1037
+ # seconds for the process to start shutting down.
912
1038
  def stop!
913
1039
  signal("TERM")
914
1040
  end
915
1041
 
1042
+ # Signal this process to log backtraces for all threads.
1043
+ # Useful if you have a frozen or deadlocked process which is
1044
+ # still sending a heartbeat.
1045
+ # This method is *asynchronous* and it can take 5-10 seconds.
916
1046
  def dump_threads
917
1047
  signal("TTIN")
918
1048
  end
919
1049
 
1050
+ # @return [Boolean] true if this process is quiet or shutting down
920
1051
  def stopping?
921
1052
  self["quiet"] == "true"
922
1053
  end
@@ -964,7 +1095,7 @@ module Sidekiq
964
1095
  procs.sort.each do |key|
965
1096
  valid, workers = conn.pipelined { |pipeline|
966
1097
  pipeline.exists?(key)
967
- pipeline.hgetall("#{key}:workers")
1098
+ pipeline.hgetall("#{key}:work")
968
1099
  }
969
1100
  next unless valid
970
1101
  workers.each_pair do |tid, json|