sidekiq 8.0.2 → 8.1.0

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.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +108 -0
  3. data/README.md +15 -0
  4. data/bin/lint-herb +13 -0
  5. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +104 -58
  6. data/lib/generators/sidekiq/templates/job.rb.erb +1 -1
  7. data/lib/sidekiq/api.rb +30 -6
  8. data/lib/sidekiq/capsule.rb +4 -0
  9. data/lib/sidekiq/cli.rb +16 -4
  10. data/lib/sidekiq/client.rb +15 -1
  11. data/lib/sidekiq/component.rb +2 -1
  12. data/lib/sidekiq/config.rb +11 -6
  13. data/lib/sidekiq/fetch.rb +1 -0
  14. data/lib/sidekiq/job/iterable.rb +33 -14
  15. data/lib/sidekiq/job.rb +4 -2
  16. data/lib/sidekiq/job_logger.rb +5 -3
  17. data/lib/sidekiq/job_retry.rb +23 -8
  18. data/lib/sidekiq/launcher.rb +18 -9
  19. data/lib/sidekiq/loader.rb +57 -0
  20. data/lib/sidekiq/logger.rb +16 -9
  21. data/lib/sidekiq/metrics/tracking.rb +3 -0
  22. data/lib/sidekiq/middleware/current_attributes.rb +6 -2
  23. data/lib/sidekiq/middleware/i18n.rb +2 -0
  24. data/lib/sidekiq/monitor.rb +4 -8
  25. data/lib/sidekiq/profiler.rb +17 -3
  26. data/lib/sidekiq/rails.rb +46 -67
  27. data/lib/sidekiq/redis_connection.rb +2 -2
  28. data/lib/sidekiq/ring_buffer.rb +1 -0
  29. data/lib/sidekiq/scheduled.rb +7 -5
  30. data/lib/sidekiq/testing.rb +1 -1
  31. data/lib/sidekiq/transaction_aware_client.rb +13 -5
  32. data/lib/sidekiq/version.rb +1 -1
  33. data/lib/sidekiq/web/action.rb +45 -2
  34. data/lib/sidekiq/web/application.rb +22 -4
  35. data/lib/sidekiq/web/config.rb +3 -3
  36. data/lib/sidekiq/web/helpers.rb +26 -29
  37. data/lib/sidekiq/web.rb +23 -3
  38. data/lib/sidekiq.rb +5 -0
  39. data/sidekiq.gemspec +5 -5
  40. data/web/assets/images/logo.png +0 -0
  41. data/web/assets/images/status.png +0 -0
  42. data/web/assets/javascripts/application.js +36 -13
  43. data/web/assets/javascripts/dashboard.js +1 -1
  44. data/web/assets/stylesheets/style.css +30 -6
  45. data/web/locales/ar.yml +1 -0
  46. data/web/locales/cs.yml +1 -0
  47. data/web/locales/da.yml +1 -0
  48. data/web/locales/de.yml +1 -0
  49. data/web/locales/el.yml +1 -0
  50. data/web/locales/en.yml +1 -0
  51. data/web/locales/es.yml +1 -0
  52. data/web/locales/fa.yml +1 -0
  53. data/web/locales/fr.yml +2 -1
  54. data/web/locales/gd.yml +1 -0
  55. data/web/locales/he.yml +1 -0
  56. data/web/locales/hi.yml +1 -0
  57. data/web/locales/it.yml +8 -0
  58. data/web/locales/ja.yml +1 -0
  59. data/web/locales/ko.yml +1 -0
  60. data/web/locales/lt.yml +1 -0
  61. data/web/locales/nb.yml +1 -0
  62. data/web/locales/nl.yml +1 -0
  63. data/web/locales/pl.yml +1 -0
  64. data/web/locales/pt-BR.yml +1 -0
  65. data/web/locales/pt.yml +1 -0
  66. data/web/locales/ru.yml +1 -0
  67. data/web/locales/sv.yml +1 -0
  68. data/web/locales/ta.yml +1 -0
  69. data/web/locales/tr.yml +1 -0
  70. data/web/locales/uk.yml +6 -5
  71. data/web/locales/ur.yml +1 -0
  72. data/web/locales/vi.yml +1 -0
  73. data/web/locales/zh-CN.yml +1 -0
  74. data/web/locales/zh-TW.yml +1 -0
  75. data/web/views/{_footer.erb → _footer.html.erb} +1 -1
  76. data/web/views/{_metrics_period_select.erb → _metrics_period_select.html.erb} +1 -1
  77. data/web/views/{_paging.erb → _paging.html.erb} +0 -1
  78. data/web/views/_poll_link.html.erb +4 -0
  79. data/web/views/{busy.erb → busy.html.erb} +4 -8
  80. data/web/views/{dashboard.erb → dashboard.html.erb} +3 -3
  81. data/web/views/{dead.erb → dead.html.erb} +3 -3
  82. data/web/views/filtering.html.erb +6 -0
  83. data/web/views/{layout.erb → layout.html.erb} +8 -7
  84. data/web/views/{metrics.erb → metrics.html.erb} +9 -8
  85. data/web/views/{morgue.erb → morgue.html.erb} +8 -4
  86. data/web/views/{queue.erb → queue.html.erb} +2 -2
  87. data/web/views/{queues.erb → queues.html.erb} +4 -4
  88. data/web/views/{retries.erb → retries.html.erb} +9 -5
  89. data/web/views/{retry.erb → retry.html.erb} +2 -2
  90. data/web/views/{scheduled.erb → scheduled.html.erb} +9 -5
  91. data/web/views/{scheduled_job_info.erb → scheduled_job_info.html.erb} +2 -2
  92. metadata +37 -36
  93. data/lib/sidekiq/web/csrf_protection.rb +0 -183
  94. data/web/views/_poll_link.erb +0 -4
  95. data/web/views/filtering.erb +0 -6
  96. /data/web/views/{_job_info.erb → _job_info.html.erb} +0 -0
  97. /data/web/views/{_nav.erb → _nav.html.erb} +0 -0
  98. /data/web/views/{_summary.erb → _summary.html.erb} +0 -0
  99. /data/web/views/{metrics_for_job.erb → metrics_for_job.html.erb} +0 -0
  100. /data/web/views/{profiles.erb → profiles.html.erb} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2128fafa84e48bfd4cad6155e9dcddaab93bc68152b28d6c27156cf84490cf9
4
- data.tar.gz: 346e2bff46bcae590efbdab4bf4c0942994a70fd4360cb395efffaac02e9ee02
3
+ metadata.gz: 63658318932ac6b7045211590b07f84ed9e1e6398f908ace790a0df20d5ac1f0
4
+ data.tar.gz: 0c3f5e69ada7529bdac7392acb1c56ef2a19b8ed13210be8c36aac19858c6df4
5
5
  SHA512:
6
- metadata.gz: 318665d5dd278811f1ce98923fac64fbc79d78f38bde1e54c92f975f4bce24a24ef43a7c15eda03fc54caa39f1bddd1e932aa98f866a6983d4d92cf2ec587a6d
7
- data.tar.gz: 16b597d7ff91c948bd4868c85386387a4f306f6dfce260d9141db485e9252845c38c2195ee6ec7b4490e94c950d66de43a4a429feaacf37c9b06d3be4ed9ed14
6
+ metadata.gz: 9a5e95a2faccbbf42f8e651c4882c9495ad72b0500bb8fe77fa2a93fb4aa6f8cb96fc820621cd1910fb4b00accf35d75a99d548b3cc8ccdd41a18b5620a9ad73
7
+ data.tar.gz: 1bc66490ee07c548c71f680fb280e876efa7eeb8c6f234277df73211ce05c5ae29f5c9bc13064972da945568d1df6634252a6c22a7c5298bdd436b7266246c53
data/Changes.md CHANGED
@@ -2,6 +2,114 @@
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.1.0
6
+ ----------
7
+
8
+ - `retry_for` and `retry` are now mutually exclusive [#6878, Saidbek]
9
+ - `perform_inline` now enforces `strict_args!` [#6718, Saidbek]
10
+ - Integrate Herb linting for ERB templates [#6760, Saidbek]
11
+ - Remove CSRF code, use `Sec-Fetch-Site` header [#6874, deve1212]
12
+ - Allow custom Web UI `assets_path` for CDN purposes [#6865, stanhu]
13
+ - Upgrade to connection_pool 3.0
14
+ - Allow idle connection reaping after N seconds.
15
+ You can activate this **beta** feature like below.
16
+ Feedback requested: is this feature stable and useful for you in production?
17
+ This feature may or may not be enabled by default in Sidekiq 9.0.
18
+ ```ruby
19
+ Sidekiq.configure_server do |cfg|
20
+ cfg.reap_idle_redis_connections(60)
21
+ end
22
+ ```
23
+
24
+ 8.0.10
25
+ ----------
26
+
27
+ - Add confirm dialog for Delete All buttons in Web UI [#6853]
28
+ - Adjust scheduler to run closer to poll average [#6866]
29
+ - Forward compatibility changes for connection_pool 3.0.0
30
+ - Backwards compatibility fix for <8.0.9 process data in Redis [#6870]
31
+ - Backtrace dump can now be triggered with the INFO signal, since Puma uses the
32
+ same signal [#6857]
33
+
34
+ 8.0.9
35
+ ----------
36
+
37
+ - Implement idle Redis connection reaping, will be activated in 8.1 [#6663]
38
+ - Updated `Sidekiq::Process` API to provide capsule data. The `queues` and `weights`
39
+ data will be removed from Redis in Sidekiq 8.1, as this data can now be found in the
40
+ `capsules` element. [#6295]
41
+ - Restore bulk action buttons on Scheduled, Retry and Dead tabs [#6833, deve1212]
42
+ - Support logging additional job attributes [#6846, bschrag620]
43
+ - Fix display of long job args [#6836]
44
+ - Create development lifecycle (`docs/sdlc.md`) and security (`docs/SECURITY.md`) policy
45
+ documentation for Sidekiq's current workflows
46
+
47
+ 8.0.8
48
+ ----------
49
+
50
+ - Allow an optional global iteration max runtime. After executing for this length of time,
51
+ Sidekiq will re-queue the job to continue execution at a later time [#6819, fatkodima]
52
+ ```ruby
53
+ Sidekiq.configure_server do |cfg|
54
+ cfg[:max_iteration_runtime] = 600 # ten minutes
55
+ end
56
+ ```
57
+ - Add `discarded_at` attribute when discarding a job so death handlers can distinguish between
58
+ a job which was killed and one that was discarded. [#6820, gstokkink]
59
+ - `perform_bulk` now accepts an `:at` array of times to schedule each job at the corresponding time.
60
+ `perform_bulk(args: [[1], [2]], at: [Time.now, Time.now + 1])` [#6790, fatkodima]
61
+ - `perform_bulk` now accepts a `:spread_interval` value to schedule jobs over
62
+ the next N seconds. `perform_bulk(..., spread_interval: 60)` [#6792, fatkodima]
63
+ - Fix unintended display of flash messages in the Web UI due to session key collision
64
+ - Add support for lazy load hooks [#6825]
65
+
66
+ 8.0.7
67
+ ----------
68
+
69
+ - The `:discard` option for `sidekiq_retries_exhausted` and `sidekiq_retry_in`
70
+ now calls death handlers, otherwise it could break other Sidekiq
71
+ functionality. [#6741]
72
+ - Provide a Plain log formatter which does not colorize output [#6778]
73
+ - Job iteration now exposes `current_object` for easy access within the `around_iteration` callback [#6774]
74
+ - Fix JS race condition which could skip confirmation dialogs when Live Polling [#6768]
75
+ - Fix edge case which could lose CurrentAttributes [#6767]
76
+ - Update UK locale [#6776]
77
+
78
+ 8.0.6
79
+ ----------
80
+
81
+ - Adjust transactional client to use ActiveRecord 7.2's support for
82
+ `after_all_transactions_commit` when available. [#6765, rewritten]
83
+ - Fix Rails 7.0 and 7.1 compatibility [#6746, mlarraz]
84
+ - Flush metrics at `:exit` [#6764]
85
+
86
+ 8.0.5
87
+ ----------
88
+
89
+ - Add `stopping?` method to AJ adapter for compatibility with the new AJ::Continuations feature [#6732]
90
+ - Further improvements to Rails boot compatibility [#6710]
91
+ - Add ability to disable CSRF middleware. SameSite cookies prevent
92
+ CSRF in a cleaner manner and are default in most browsers now.
93
+ CSRF code will be removed in Sidekiq 9.0. [#6739]
94
+
95
+ 8.0.4
96
+ ----------
97
+
98
+ - Adjust Rails integration for various edge cases [6713]
99
+ - Flush job iteration state when an error is raised [#6704]
100
+ - Update Accept-Language parsing in Web UI [#6721]
101
+ - Remove fixed-width in Web UI [#6686]
102
+ - Adjust CSRF middleware ordering [#6688]
103
+ - Support proxies when POSTing profiles to profiler.firefox.com [#6687]
104
+ - Dont swallow NoMethodErrors in CurrentAttributes [#6685]
105
+
106
+ 8.0.3
107
+ ----------
108
+
109
+ - Configure Vernier output directory [#6674]
110
+ - Rework Rails integration [#6669]
111
+ - Implement flash messages for the Web UI [#6675]
112
+
5
113
  8.0.2
6
114
  ----------
7
115
 
data/README.md CHANGED
@@ -97,6 +97,21 @@ Contributing
97
97
 
98
98
  See [the contributing guidelines](https://github.com/sidekiq/sidekiq/blob/main/.github/contributing.md).
99
99
 
100
+ ### ERB Linting with HERB
101
+
102
+ This project uses [HERB](https://herb-tools.dev/) for ERB file linting and formatting. All ERB files have been renamed to use the `.html.erb` extension for better tooling support.
103
+
104
+ **Local Development:**
105
+ ```bash
106
+ # Run HERB linting
107
+ bundle exec rake lint:herb
108
+ # or
109
+ bin/lint-herb
110
+ ```
111
+
112
+ **CI Integration:**
113
+ HERB linting is automatically run in CI to ensure all ERB files are properly formatted and free of parse errors.
114
+
100
115
  License
101
116
  -----------------
102
117
 
data/bin/lint-herb ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # HERB Linting Script
5
+ # Run this script to lint all ERB files in the project
6
+ # Usage: bin/lint-herb
7
+
8
+ require "bundler/setup"
9
+
10
+ puts "🔍 Running HERB linting on ERB files..."
11
+ puts
12
+
13
+ exec("bundle exec herb analyze web/views -n --no-log-file")
@@ -1,75 +1,121 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module ActiveJob
4
- module QueueAdapters
5
- # Explicitly remove the implementation existing in older Rails.
6
- remove_const(:SidekiqAdapter) if const_defined?(:SidekiqAdapter)
3
+ begin
4
+ gem "activejob", ">= 7.0"
5
+ require "active_job"
7
6
 
8
- # Sidekiq adapter for Active Job
9
- #
10
- # To use Sidekiq set the queue_adapter config to +:sidekiq+.
11
- #
12
- # Rails.application.config.active_job.queue_adapter = :sidekiq
13
- class SidekiqAdapter
14
- # Defines whether enqueuing should happen implicitly to after commit when called
15
- # from inside a transaction.
7
+ module Sidekiq
8
+ module ActiveJob
16
9
  # @api private
17
- def enqueue_after_transaction_commit?
18
- true
19
- end
10
+ class Wrapper
11
+ include Sidekiq::Job
20
12
 
21
- # @api private
22
- def enqueue(job)
23
- job.provider_job_id = Sidekiq::ActiveJob::Wrapper.set(
24
- wrapped: job.class,
25
- queue: job.queue_name
26
- ).perform_async(job.serialize)
13
+ def perform(job_data)
14
+ ::ActiveJob::Base.execute(job_data.merge("provider_job_id" => jid))
15
+ end
27
16
  end
17
+ end
18
+ end
28
19
 
29
- # @api private
30
- def enqueue_at(job, timestamp)
31
- job.provider_job_id = Sidekiq::ActiveJob::Wrapper.set(
32
- wrapped: job.class,
33
- queue: job.queue_name
34
- ).perform_at(timestamp, job.serialize)
35
- end
20
+ ActiveSupport.on_load(:active_job) do
21
+ # By including the Options module, we allow AJs to directly control sidekiq features
22
+ # via the *sidekiq_options* class method and, for instance, not use AJ's retry system.
23
+ # AJ retries don't show up in the Sidekiq UI Retries tab, don't save any error data, can't be
24
+ # manually retried, don't automatically die, etc.
25
+ #
26
+ # class SomeJob < ActiveJob::Base
27
+ # queue_as :default
28
+ # sidekiq_options retry: 3, backtrace: 10
29
+ # def perform
30
+ # end
31
+ # end
32
+ include Sidekiq::Job::Options unless respond_to?(:sidekiq_options)
33
+ end
36
34
 
37
- # @api private
38
- def enqueue_all(jobs)
39
- enqueued_count = 0
40
- jobs.group_by(&:class).each do |job_class, same_class_jobs|
41
- same_class_jobs.group_by(&:queue_name).each do |queue, same_class_and_queue_jobs|
42
- immediate_jobs, scheduled_jobs = same_class_and_queue_jobs.partition { |job| job.scheduled_at.nil? }
43
-
44
- if immediate_jobs.any?
45
- jids = Sidekiq::Client.push_bulk(
46
- "class" => Sidekiq::ActiveJob::Wrapper,
47
- "wrapped" => job_class,
48
- "queue" => queue,
49
- "args" => immediate_jobs.map { |job| [job.serialize] }
50
- )
51
- enqueued_count += jids.compact.size
52
- end
35
+ # Patch the ActiveJob module
36
+ module ActiveJob
37
+ module QueueAdapters
38
+ # Explicitly remove the implementation existing in older Rails.
39
+ remove_const(:SidekiqAdapter) if const_defined?(:SidekiqAdapter)
40
+
41
+ # Sidekiq adapter for Active Job
42
+ #
43
+ # To use Sidekiq set the queue_adapter config to +:sidekiq+.
44
+ #
45
+ # Rails.application.config.active_job.queue_adapter = :sidekiq
46
+ parent = const_defined?(:AbstractAdapter) ? AbstractAdapter : Object
47
+ class SidekiqAdapter < parent
48
+ @@stopping = false
53
49
 
54
- if scheduled_jobs.any?
55
- jids = Sidekiq::Client.push_bulk(
56
- "class" => Sidekiq::ActiveJob::Wrapper,
57
- "wrapped" => job_class,
58
- "queue" => queue,
59
- "args" => scheduled_jobs.map { |job| [job.serialize] },
60
- "at" => scheduled_jobs.map { |job| job.scheduled_at&.to_f }
61
- )
62
- enqueued_count += jids.compact.size
50
+ callback = -> { @@stopping = true }
51
+
52
+ Sidekiq.configure_client { |config| config.on(:quiet, &callback) }
53
+ Sidekiq.configure_server { |config| config.on(:quiet, &callback) }
54
+
55
+ # Defines whether enqueuing should happen implicitly to after commit when called
56
+ # from inside a transaction.
57
+ # @api private
58
+ def enqueue_after_transaction_commit?
59
+ true
60
+ end
61
+
62
+ # @api private
63
+ def enqueue(job)
64
+ job.provider_job_id = Sidekiq::ActiveJob::Wrapper.set(
65
+ wrapped: job.class,
66
+ queue: job.queue_name
67
+ ).perform_async(job.serialize)
68
+ end
69
+
70
+ # @api private
71
+ def enqueue_at(job, timestamp)
72
+ job.provider_job_id = Sidekiq::ActiveJob::Wrapper.set(
73
+ wrapped: job.class,
74
+ queue: job.queue_name
75
+ ).perform_at(timestamp, job.serialize)
76
+ end
77
+
78
+ # @api private
79
+ def enqueue_all(jobs)
80
+ enqueued_count = 0
81
+ jobs.group_by(&:class).each do |job_class, same_class_jobs|
82
+ same_class_jobs.group_by(&:queue_name).each do |queue, same_class_and_queue_jobs|
83
+ immediate_jobs, scheduled_jobs = same_class_and_queue_jobs.partition { |job| job.scheduled_at.nil? }
84
+
85
+ if immediate_jobs.any?
86
+ jids = Sidekiq::Client.push_bulk(
87
+ "class" => Sidekiq::ActiveJob::Wrapper,
88
+ "wrapped" => job_class,
89
+ "queue" => queue,
90
+ "args" => immediate_jobs.map { |job| [job.serialize] }
91
+ )
92
+ enqueued_count += jids.compact.size
93
+ end
94
+
95
+ if scheduled_jobs.any?
96
+ jids = Sidekiq::Client.push_bulk(
97
+ "class" => Sidekiq::ActiveJob::Wrapper,
98
+ "wrapped" => job_class,
99
+ "queue" => queue,
100
+ "args" => scheduled_jobs.map { |job| [job.serialize] },
101
+ "at" => scheduled_jobs.map { |job| job.scheduled_at&.to_f }
102
+ )
103
+ enqueued_count += jids.compact.size
104
+ end
63
105
  end
64
106
  end
107
+ enqueued_count
65
108
  end
66
- enqueued_count
67
- end
68
109
 
69
- # Defines a class alias for backwards compatibility with enqueued Active Job jobs.
70
- # @api private
71
- class JobWrapper < Sidekiq::ActiveJob::Wrapper
110
+ # @api private
111
+ def stopping? = !!@@stopping
112
+
113
+ # Defines a class alias for backwards compatibility with enqueued Active Job jobs.
114
+ # @api private
115
+ JobWrapper = Sidekiq::ActiveJob::Wrapper
72
116
  end
73
117
  end
74
118
  end
119
+ rescue Gem::LoadError
120
+ # ActiveJob not available or version requirement not met
75
121
  end
@@ -6,4 +6,4 @@ class <%= class_name %>Job
6
6
  # Do something
7
7
  end
8
8
  end
9
- <% end -%>
9
+ <% end -%>
data/lib/sidekiq/api.rb CHANGED
@@ -1059,9 +1059,9 @@ module Sidekiq
1059
1059
  # 'started_at' => <process start time>,
1060
1060
  # 'pid' => 12345,
1061
1061
  # 'tag' => 'myapp'
1062
- # 'concurrency' => 25,
1063
- # 'queues' => ['default', 'low'],
1064
- # 'busy' => 10,
1062
+ # 'concurrency' => 5,
1063
+ # 'capsules' => {"default" => {"mode" => "weighted", "concurrency" => 5, "weights" => {"default" => 2, "low" => 1}}},
1064
+ # 'busy' => 3,
1065
1065
  # 'beat' => <last heartbeat>,
1066
1066
  # 'identity' => <unique string identifying the process>,
1067
1067
  # 'embedded' => true,
@@ -1089,12 +1089,35 @@ module Sidekiq
1089
1089
  self["identity"]
1090
1090
  end
1091
1091
 
1092
+ # deprecated, use capsules below
1092
1093
  def queues
1093
- self["queues"]
1094
+ # Backwards compatibility with <8.0.8
1095
+ if !self["capsules"]
1096
+ self["queues"]
1097
+ else
1098
+ capsules.values.flat_map { |x| x["weights"].keys }.uniq
1099
+ end
1094
1100
  end
1095
1101
 
1102
+ # deprecated, use capsules below
1096
1103
  def weights
1097
- self["weights"]
1104
+ # Backwards compatibility with <8.0.8
1105
+ if !self["capsules"]
1106
+ self["weights"]
1107
+ else
1108
+ hash = {}
1109
+ capsules.values.each do |cap|
1110
+ # Note: will lose data if two capsules are processing the same named queue
1111
+ cap["weights"].each_pair do |queue, weight|
1112
+ hash[queue] = weight
1113
+ end
1114
+ end
1115
+ hash
1116
+ end
1117
+ end
1118
+
1119
+ def capsules
1120
+ self["capsules"]
1098
1121
  end
1099
1122
 
1100
1123
  def version
@@ -1168,7 +1191,6 @@ module Sidekiq
1168
1191
  # # thread_id is a unique identifier per thread
1169
1192
  # # work is a `Sidekiq::Work` instance that has the following accessor methods.
1170
1193
  # # [work.queue, work.run_at, work.payload]
1171
- # # run_at is an epoch Integer.
1172
1194
  # end
1173
1195
  #
1174
1196
  class WorkSet
@@ -1322,3 +1344,5 @@ module Sidekiq
1322
1344
  end
1323
1345
  end
1324
1346
  end
1347
+
1348
+ Sidekiq.loader.run_load_hooks(:api)
@@ -38,6 +38,10 @@ module Sidekiq
38
38
  @mode = :strict
39
39
  end
40
40
 
41
+ def to_h
42
+ {concurrency: concurrency, mode: mode, weights: weights}
43
+ end
44
+
41
45
  def fetcher
42
46
  @fetcher ||= begin
43
47
  instance = (config[:fetch_class] || Sidekiq::BasicFetch).new(self)
data/lib/sidekiq/cli.rb CHANGED
@@ -49,7 +49,7 @@ module Sidekiq # :nodoc:
49
49
  logger.info "Booted Rails #{::Rails.version} application in #{environment} environment" if rails_app?
50
50
 
51
51
  self_read, self_write = IO.pipe
52
- sigs = %w[INT TERM TTIN TSTP]
52
+ sigs = %w[INT TERM INFO TTIN TSTP]
53
53
  # USR1 and USR2 don't work on the JVM
54
54
  sigs << "USR2" if Sidekiq.pro? && !jruby?
55
55
  sigs.each do |sig|
@@ -201,7 +201,19 @@ module Sidekiq # :nodoc:
201
201
  cli.logger.info "Received TSTP, no longer accepting new work"
202
202
  cli.launcher.quiet
203
203
  },
204
+ # deprecated, use INFO
204
205
  "TTIN" => ->(cli) {
206
+ cli.logger.error { "DEPRECATED: Please use the INFO signal for backtraces, support for TTIN will be removed in Sidekiq 9.0." }
207
+ Thread.list.each do |thread|
208
+ cli.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
209
+ if thread.backtrace
210
+ cli.logger.warn thread.backtrace.join("\n")
211
+ else
212
+ cli.logger.warn "<no backtrace available>"
213
+ end
214
+ end
215
+ },
216
+ "INFO" => ->(cli) {
205
217
  Thread.list.each do |thread|
206
218
  cli.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
207
219
  if thread.backtrace
@@ -212,12 +224,12 @@ module Sidekiq # :nodoc:
212
224
  end
213
225
  }
214
226
  }
215
- UNHANDLED_SIGNAL_HANDLER = ->(cli) { cli.logger.info "No signal handler registered, ignoring" }
216
- SIGNAL_HANDLERS.default = UNHANDLED_SIGNAL_HANDLER
217
227
 
218
228
  def handle_signal(sig)
219
229
  logger.debug "Got #{sig} signal"
220
- SIGNAL_HANDLERS[sig].call(self)
230
+ hndlr = SIGNAL_HANDLERS[sig]
231
+ hndlr ? hndlr.call(self) :
232
+ logger.warn("No #{sig} signal handler registered, ignoring")
221
233
  end
222
234
 
223
235
  private
@@ -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|
@@ -19,7 +19,8 @@ module Sidekiq
19
19
  DEFAULT_THREAD_PRIORITY = -1
20
20
 
21
21
  ##
22
- # Sidekiq::Component assumes a config instance is available at @config
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
 
@@ -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
- iteration: {
21
- max_job_runtime: nil,
22
- retry_backoff: 0
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: {
@@ -36,7 +35,9 @@ module Sidekiq
36
35
  dead_max_jobs: 10_000,
37
36
  dead_timeout_in_seconds: 180 * 24 * 60 * 60, # 6 months
38
37
  reloader: proc { |&block| block.call },
39
- backtrace_cleaner: ->(backtrace) { backtrace }
38
+ backtrace_cleaner: ->(backtrace) { backtrace },
39
+ logged_job_attributes: ["bid", "tags"],
40
+ redis_idle_timeout: nil
40
41
  }
41
42
 
42
43
  ERROR_HANDLER = ->(ex, ctx, cfg = Sidekiq.default_configuration) {
@@ -145,11 +146,15 @@ module Sidekiq
145
146
  @redis_config = @redis_config.merge(hash)
146
147
  end
147
148
 
149
+ def reap_idle_redis_connections(timeout = 60)
150
+ self[:redis_idle_timeout] = timeout
151
+ end
152
+
148
153
  def redis_pool
149
154
  Thread.current[:sidekiq_redis_pool] || Thread.current[:sidekiq_capsule]&.redis_pool || local_redis_pool
150
155
  end
151
156
 
152
- private def local_redis_pool
157
+ def local_redis_pool
153
158
  # this is our internal client/housekeeping pool. each capsule has its
154
159
  # own pool for executing threads.
155
160
  @redis ||= new_redis_pool(10, "internal")
data/lib/sidekiq/fetch.rb CHANGED
@@ -7,6 +7,7 @@ require "sidekiq/capsule"
7
7
  module Sidekiq # :nodoc:
8
8
  class BasicFetch
9
9
  include Sidekiq::Component
10
+
10
11
  # We want the fetch operation to timeout every few seconds so the thread
11
12
  # can check if the process is shutting down.
12
13
  TIMEOUT = 2