sidekiq 6.5.10 → 8.1.7

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 (177) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +568 -18
  3. data/README.md +66 -38
  4. data/bin/kiq +17 -0
  5. data/bin/lint-herb +13 -0
  6. data/bin/multi_queue_bench +271 -0
  7. data/bin/sidekiq +3 -8
  8. data/bin/sidekiqload +215 -118
  9. data/bin/sidekiqmon +3 -0
  10. data/bin/webload +69 -0
  11. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +124 -0
  12. data/lib/generators/sidekiq/job_generator.rb +17 -3
  13. data/lib/generators/sidekiq/templates/job.rb.erb +1 -1
  14. data/lib/sidekiq/api.rb +526 -218
  15. data/lib/sidekiq/capsule.rb +135 -0
  16. data/lib/sidekiq/cli.rb +88 -96
  17. data/lib/sidekiq/client.rb +114 -47
  18. data/lib/sidekiq/component.rb +71 -4
  19. data/lib/sidekiq/config.rb +316 -0
  20. data/lib/sidekiq/deploy.rb +64 -0
  21. data/lib/sidekiq/embedded.rb +64 -0
  22. data/lib/sidekiq/fetch.rb +12 -14
  23. data/lib/sidekiq/iterable_job.rb +56 -0
  24. data/lib/sidekiq/job/interrupt_handler.rb +24 -0
  25. data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
  26. data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
  27. data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
  28. data/lib/sidekiq/job/iterable.rb +322 -0
  29. data/lib/sidekiq/job.rb +397 -10
  30. data/lib/sidekiq/job_logger.rb +14 -11
  31. data/lib/sidekiq/job_retry.rb +78 -28
  32. data/lib/sidekiq/job_util.rb +58 -16
  33. data/lib/sidekiq/launcher.rb +86 -70
  34. data/lib/sidekiq/loader.rb +57 -0
  35. data/lib/sidekiq/logger.rb +25 -94
  36. data/lib/sidekiq/manager.rb +11 -13
  37. data/lib/sidekiq/metrics/query.rb +84 -48
  38. data/lib/sidekiq/metrics/shared.rb +29 -14
  39. data/lib/sidekiq/metrics/tracking.rb +49 -30
  40. data/lib/sidekiq/middleware/chain.rb +19 -18
  41. data/lib/sidekiq/middleware/current_attributes.rb +78 -21
  42. data/lib/sidekiq/middleware/i18n.rb +2 -0
  43. data/lib/sidekiq/middleware/modules.rb +2 -0
  44. data/lib/sidekiq/monitor.rb +15 -5
  45. data/lib/sidekiq/paginator.rb +26 -4
  46. data/lib/sidekiq/processor.rb +78 -65
  47. data/lib/sidekiq/profiler.rb +73 -0
  48. data/lib/sidekiq/rails.rb +45 -60
  49. data/lib/sidekiq/redis_client_adapter.rb +35 -72
  50. data/lib/sidekiq/redis_connection.rb +54 -114
  51. data/lib/sidekiq/ring_buffer.rb +3 -0
  52. data/lib/sidekiq/scheduled.rb +28 -30
  53. data/lib/sidekiq/systemd.rb +2 -0
  54. data/lib/sidekiq/test_api.rb +331 -0
  55. data/lib/sidekiq/testing/inline.rb +2 -30
  56. data/lib/sidekiq/testing.rb +2 -342
  57. data/lib/sidekiq/transaction_aware_client.rb +23 -9
  58. data/lib/sidekiq/tui/controls.rb +53 -0
  59. data/lib/sidekiq/tui/filtering.rb +53 -0
  60. data/lib/sidekiq/tui/tabs/base_tab.rb +230 -0
  61. data/lib/sidekiq/tui/tabs/busy.rb +104 -0
  62. data/lib/sidekiq/tui/tabs/dead.rb +14 -0
  63. data/lib/sidekiq/tui/tabs/home.rb +115 -0
  64. data/lib/sidekiq/tui/tabs/metrics.rb +114 -0
  65. data/lib/sidekiq/tui/tabs/queues.rb +83 -0
  66. data/lib/sidekiq/tui/tabs/retries.rb +14 -0
  67. data/lib/sidekiq/tui/tabs/scheduled.rb +14 -0
  68. data/lib/sidekiq/tui/tabs/set_tab.rb +91 -0
  69. data/lib/sidekiq/tui/tabs.rb +15 -0
  70. data/lib/sidekiq/tui.rb +390 -0
  71. data/lib/sidekiq/version.rb +6 -1
  72. data/lib/sidekiq/web/action.rb +149 -64
  73. data/lib/sidekiq/web/application.rb +379 -281
  74. data/lib/sidekiq/web/config.rb +117 -0
  75. data/lib/sidekiq/web/helpers.rb +190 -71
  76. data/lib/sidekiq/web/router.rb +62 -75
  77. data/lib/sidekiq/web.rb +68 -101
  78. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  79. data/lib/sidekiq.rb +85 -269
  80. data/sidekiq.gemspec +15 -11
  81. data/web/assets/images/logo.png +0 -0
  82. data/web/assets/images/status.png +0 -0
  83. data/web/assets/javascripts/application.js +84 -24
  84. data/web/assets/javascripts/base-charts.js +120 -0
  85. data/web/assets/javascripts/chartjs-adapter-date-fns.min.js +7 -0
  86. data/web/assets/javascripts/dashboard-charts.js +194 -0
  87. data/web/assets/javascripts/dashboard.js +18 -234
  88. data/web/assets/javascripts/metrics.js +138 -120
  89. data/web/assets/stylesheets/style.css +776 -0
  90. data/web/locales/ar.yml +72 -70
  91. data/web/locales/cs.yml +64 -62
  92. data/web/locales/da.yml +62 -53
  93. data/web/locales/de.yml +67 -65
  94. data/web/locales/el.yml +4 -7
  95. data/web/locales/en.yml +89 -71
  96. data/web/locales/es.yml +91 -68
  97. data/web/locales/fa.yml +67 -65
  98. data/web/locales/fr.yml +82 -67
  99. data/web/locales/gd.yml +110 -0
  100. data/web/locales/he.yml +67 -64
  101. data/web/locales/hi.yml +95 -59
  102. data/web/locales/it.yml +94 -54
  103. data/web/locales/ja.yml +69 -70
  104. data/web/locales/ko.yml +54 -52
  105. data/web/locales/lt.yml +68 -66
  106. data/web/locales/nb.yml +63 -61
  107. data/web/locales/nl.yml +54 -52
  108. data/web/locales/pl.yml +47 -45
  109. data/web/locales/{pt-br.yml → pt-BR.yml} +81 -70
  110. data/web/locales/pt.yml +53 -51
  111. data/web/locales/ru.yml +69 -66
  112. data/web/locales/sv.yml +55 -53
  113. data/web/locales/ta.yml +100 -64
  114. data/web/locales/tr.yml +102 -0
  115. data/web/locales/uk.yml +87 -61
  116. data/web/locales/ur.yml +66 -64
  117. data/web/locales/vi.yml +69 -67
  118. data/web/locales/zh-CN.yml +107 -0
  119. data/web/locales/{zh-tw.yml → zh-TW.yml} +13 -3
  120. data/web/views/_footer.html.erb +32 -0
  121. data/web/views/_job_info.html.erb +115 -0
  122. data/web/views/_metrics_period_select.html.erb +15 -0
  123. data/web/views/_nav.html.erb +45 -0
  124. data/web/views/_paging.html.erb +26 -0
  125. data/web/views/_poll_link.html.erb +4 -0
  126. data/web/views/_summary.html.erb +40 -0
  127. data/web/views/busy.html.erb +155 -0
  128. data/web/views/dashboard.html.erb +104 -0
  129. data/web/views/dead.html.erb +38 -0
  130. data/web/views/filtering.html.erb +6 -0
  131. data/web/views/layout.html.erb +26 -0
  132. data/web/views/metrics.html.erb +85 -0
  133. data/web/views/metrics_for_job.html.erb +58 -0
  134. data/web/views/morgue.html.erb +69 -0
  135. data/web/views/profiles.html.erb +43 -0
  136. data/web/views/queue.html.erb +57 -0
  137. data/web/views/queues.html.erb +46 -0
  138. data/web/views/retries.html.erb +77 -0
  139. data/web/views/retry.html.erb +39 -0
  140. data/web/views/scheduled.html.erb +64 -0
  141. data/web/views/{scheduled_job_info.erb → scheduled_job_info.html.erb} +3 -3
  142. metadata +113 -70
  143. data/lib/sidekiq/delay.rb +0 -43
  144. data/lib/sidekiq/extensions/action_mailer.rb +0 -48
  145. data/lib/sidekiq/extensions/active_record.rb +0 -43
  146. data/lib/sidekiq/extensions/class_methods.rb +0 -43
  147. data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
  148. data/lib/sidekiq/metrics/deploy.rb +0 -47
  149. data/lib/sidekiq/web/csrf_protection.rb +0 -180
  150. data/lib/sidekiq/worker.rb +0 -370
  151. data/web/assets/javascripts/graph.js +0 -16
  152. data/web/assets/stylesheets/application-dark.css +0 -143
  153. data/web/assets/stylesheets/application-rtl.css +0 -242
  154. data/web/assets/stylesheets/application.css +0 -999
  155. data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
  156. data/web/assets/stylesheets/bootstrap.css +0 -5
  157. data/web/locales/zh-cn.yml +0 -93
  158. data/web/views/_footer.erb +0 -20
  159. data/web/views/_job_info.erb +0 -89
  160. data/web/views/_nav.erb +0 -52
  161. data/web/views/_paging.erb +0 -23
  162. data/web/views/_poll_link.erb +0 -4
  163. data/web/views/_status.erb +0 -4
  164. data/web/views/_summary.erb +0 -40
  165. data/web/views/busy.erb +0 -137
  166. data/web/views/dashboard.erb +0 -84
  167. data/web/views/dead.erb +0 -34
  168. data/web/views/layout.erb +0 -42
  169. data/web/views/metrics.erb +0 -69
  170. data/web/views/metrics_for_job.erb +0 -87
  171. data/web/views/morgue.erb +0 -78
  172. data/web/views/queue.erb +0 -59
  173. data/web/views/queues.erb +0 -38
  174. data/web/views/retries.erb +0 -83
  175. data/web/views/retry.erb +0 -34
  176. data/web/views/scheduled.erb +0 -57
  177. /data/{LICENSE → LICENSE.txt} +0 -0
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sidekiq/component"
4
+
5
+ module Sidekiq
6
+ # A Sidekiq::Capsule is the set of resources necessary to
7
+ # process one or more queues with a given concurrency.
8
+ # One "default" Capsule is started but the user may declare additional
9
+ # Capsules in their initializer.
10
+ #
11
+ # This capsule will pull jobs from the "single" queue and process
12
+ # the jobs with one thread, meaning the jobs will be processed serially.
13
+ #
14
+ # Sidekiq.configure_server do |config|
15
+ # config.capsule("single-threaded") do |cap|
16
+ # cap.concurrency = 1
17
+ # cap.queues = %w(single)
18
+ # end
19
+ # end
20
+ class Capsule
21
+ include Sidekiq::Component
22
+ extend Forwardable
23
+
24
+ attr_reader :name
25
+ attr_reader :queues
26
+ attr_accessor :concurrency
27
+ attr_reader :mode
28
+ attr_reader :weights
29
+
30
+ def_delegators :@config, :[], :[]=, :fetch, :key?, :has_key?, :merge!, :dig, :thread_priority
31
+
32
+ def initialize(name, config)
33
+ @name = name
34
+ @config = config
35
+ @queues = ["default"]
36
+ @weights = {"default" => 0}
37
+ @concurrency = config[:concurrency]
38
+ @mode = :strict
39
+ end
40
+
41
+ def to_h
42
+ {concurrency: concurrency, mode: mode, weights: weights}
43
+ end
44
+
45
+ def fetcher
46
+ @fetcher ||= begin
47
+ instance = (config[:fetch_class] || Sidekiq::BasicFetch).new(self)
48
+ instance.setup(config[:fetch_setup]) if instance.respond_to?(:setup)
49
+ instance
50
+ end
51
+ end
52
+
53
+ def stop
54
+ end
55
+
56
+ # Sidekiq checks queues in three modes:
57
+ # - :strict - all queues have 0 weight and are checked strictly in order
58
+ # - :weighted - queues have arbitrary weight between 1 and N
59
+ # - :random - all queues have weight of 1
60
+ def queues=(val)
61
+ @weights = {}
62
+ @queues = Array(val).each_with_object([]) do |qstr, memo|
63
+ arr = qstr
64
+ arr = qstr.split(",") if qstr.is_a?(String)
65
+ name, weight = arr
66
+ @weights[name] = weight.to_i
67
+ [weight.to_i, 1].max.times do
68
+ memo << name
69
+ end
70
+ end
71
+ @mode = if @weights.values.all?(&:zero?)
72
+ :strict
73
+ elsif @weights.values.all? { |x| x == 1 }
74
+ :random
75
+ else
76
+ :weighted
77
+ end
78
+ end
79
+
80
+ # Allow the middleware to be different per-capsule.
81
+ # Avoid if possible and add middleware globally so all
82
+ # capsules share the same chains. Easier to debug that way.
83
+ def client_middleware
84
+ @client_chain ||= config.client_middleware.copy_for(self)
85
+ yield @client_chain if block_given?
86
+ @client_chain
87
+ end
88
+
89
+ def server_middleware
90
+ @server_chain ||= config.server_middleware.copy_for(self)
91
+ yield @server_chain if block_given?
92
+ @server_chain
93
+ end
94
+
95
+ def redis_pool
96
+ Thread.current[:sidekiq_redis_pool] || local_redis_pool
97
+ end
98
+
99
+ def local_redis_pool
100
+ # connection pool is lazy, it will not create connections unless you actually need them
101
+ # so don't be skimpy!
102
+ @redis ||= config.new_redis_pool(@concurrency, name)
103
+ end
104
+
105
+ def redis
106
+ raise ArgumentError, "requires a block" unless block_given?
107
+ redis_pool.with do |conn|
108
+ retryable = true
109
+ begin
110
+ yield conn
111
+ rescue RedisClientAdapter::BaseError => ex
112
+ # 2550 Failover can cause the server to become a replica, need
113
+ # to disconnect and reopen the socket to get back to the primary.
114
+ # 4495 Use the same logic if we have a "Not enough replicas" error from the primary
115
+ # 4985 Use the same logic when a blocking command is force-unblocked
116
+ # The same retry logic is also used in client.rb
117
+ if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
118
+ conn.close
119
+ retryable = false
120
+ retry
121
+ end
122
+ raise
123
+ end
124
+ end
125
+ end
126
+
127
+ def lookup(name)
128
+ config.lookup(name)
129
+ end
130
+
131
+ def logger
132
+ config.logger
133
+ end
134
+ end
135
+ end
data/lib/sidekiq/cli.rb CHANGED
@@ -2,46 +2,36 @@
2
2
 
3
3
  $stdout.sync = true
4
4
 
5
- require "yaml"
6
- require "singleton"
7
5
  require "optparse"
8
6
  require "erb"
9
7
  require "fileutils"
10
8
 
11
9
  require "sidekiq"
10
+ require "sidekiq/config"
12
11
  require "sidekiq/component"
12
+ require "sidekiq/capsule"
13
13
  require "sidekiq/launcher"
14
14
 
15
- # module ScoutApm
16
- # VERSION = "5.3.1"
17
- # end
18
- fail <<~EOM if defined?(ScoutApm::VERSION) && ScoutApm::VERSION < "5.2.0"
19
-
20
-
21
- scout_apm v#{ScoutApm::VERSION} is unsafe with Sidekiq 6.5. Please run `bundle up scout_apm` to upgrade to 5.2.0 or greater.
22
-
23
-
24
- EOM
25
-
26
15
  module Sidekiq # :nodoc:
27
16
  class CLI
28
17
  include Sidekiq::Component
29
- include Singleton unless $TESTING
30
18
 
31
19
  attr_accessor :launcher
32
20
  attr_accessor :environment
33
21
  attr_accessor :config
34
22
 
35
23
  def parse(args = ARGV.dup)
36
- @config = Sidekiq
37
- @config[:error_handlers].clear
38
- @config[:error_handlers] << @config.method(:default_error_handler)
24
+ @config ||= Sidekiq.default_configuration
39
25
 
40
26
  setup_options(args)
41
27
  initialize_logger
42
28
  validate!
43
29
  end
44
30
 
31
+ def self.instance
32
+ @instance ||= new
33
+ end
34
+
45
35
  def jruby?
46
36
  defined?(::JRUBY_VERSION)
47
37
  end
@@ -49,16 +39,16 @@ module Sidekiq # :nodoc:
49
39
  # Code within this method is not tested because it alters
50
40
  # global process state irreversibly. PRs which improve the
51
41
  # test coverage of Sidekiq::CLI are welcomed.
52
- def run(boot_app: true)
42
+ def run(boot_app: true, warmup: true)
53
43
  boot_application if boot_app
54
44
 
55
- if environment == "development" && $stdout.tty? && @config.log_formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
45
+ if environment == "development" && $stdout.tty? && @config.logger.formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
56
46
  print_banner
57
47
  end
58
48
  logger.info "Booted Rails #{::Rails.version} application in #{environment} environment" if rails_app?
59
49
 
60
50
  self_read, self_write = IO.pipe
61
- sigs = %w[INT TERM TTIN TSTP]
51
+ sigs = %w[INT TERM INFO TTIN TSTP]
62
52
  # USR1 and USR2 don't work on the JVM
63
53
  sigs << "USR2" if Sidekiq.pro? && !jruby?
64
54
  sigs.each do |sig|
@@ -84,26 +74,27 @@ module Sidekiq # :nodoc:
84
74
  # touch the connection pool so it is created before we
85
75
  # fire startup and start multithreading.
86
76
  info = @config.redis_info
87
- ver = info["redis_version"]
88
- raise "You are connecting to Redis v#{ver}, Sidekiq requires Redis v4.0.0 or greater" if ver < "4"
77
+ ver = Gem::Version.new(info["redis_version"])
78
+ raise "You are connected to Redis #{ver}, Sidekiq requires Redis 7.0.0 or greater" if ver < Gem::Version.new("7.0.0")
89
79
 
90
80
  maxmemory_policy = info["maxmemory_policy"]
91
- if maxmemory_policy != "noeviction"
81
+ if maxmemory_policy != "noeviction" && maxmemory_policy != ""
82
+ # Redis Enterprise Cloud returns "" for their policy 😳
92
83
  logger.warn <<~EOM
93
84
 
94
85
 
95
86
  WARNING: Your Redis instance will evict Sidekiq data under heavy load.
96
87
  The 'noeviction' maxmemory policy is recommended (current policy: '#{maxmemory_policy}').
97
- See: https://github.com/mperham/sidekiq/wiki/Using-Redis#memory
88
+ See: https://github.com/sidekiq/sidekiq/wiki/Using-Redis#memory
98
89
 
99
90
  EOM
100
91
  end
101
92
 
102
93
  # Since the user can pass us a connection pool explicitly in the initializer, we
103
94
  # need to verify the size is large enough or else Sidekiq's performance is dramatically slowed.
104
- cursize = @config.redis_pool.size
105
- needed = @config[:concurrency] + 2
106
- raise "Your pool of #{cursize} Redis connections is too small, please increase the size to at least #{needed}" if cursize < needed
95
+ @config.capsules.each_pair do |name, cap|
96
+ raise ArgumentError, "Pool size too small for #{name}" if cap.redis_pool.size < cap.concurrency
97
+ end
107
98
 
108
99
  # cache process identity
109
100
  @config[:identity] = identity
@@ -111,12 +102,14 @@ module Sidekiq # :nodoc:
111
102
  # Touch middleware so it isn't lazy loaded by multiple threads, #3043
112
103
  @config.server_middleware
113
104
 
105
+ ::Process.warmup if warmup && ::Process.respond_to?(:warmup) && ENV["RUBY_DISABLE_WARMUP"] != "1"
106
+
114
107
  # Before this point, the process is initializing with just the main thread.
115
108
  # Starting here the process will now have multiple threads running.
116
109
  fire_event(:startup, reverse: false, reraise: true)
117
110
 
118
- logger.debug { "Client Middleware: #{@config.client_middleware.map(&:klass).join(", ")}" }
119
- logger.debug { "Server Middleware: #{@config.server_middleware.map(&:klass).join(", ")}" }
111
+ logger.debug { "Client Middleware: #{@config.default_capsule.client_middleware.map(&:klass).join(", ")}" }
112
+ logger.debug { "Server Middleware: #{@config.default_capsule.server_middleware.map(&:klass).join(", ")}" }
120
113
 
121
114
  launch(self_read)
122
115
  end
@@ -149,19 +142,34 @@ module Sidekiq # :nodoc:
149
142
  end
150
143
  end
151
144
 
152
- def self.w
153
- "\e[37m"
145
+ HOLIDAY_COLORS = {
146
+ # got other color-specific holidays from around the world?
147
+ # https://developer-book.com/post/definitive-guide-for-colored-text-in-terminal/#256-color-escape-codes
148
+ "3-17" => "\e[1;32m", # St. Patrick's Day green
149
+ "10-31" => "\e[38;5;208m" # Halloween orange
150
+ }
151
+
152
+ def self.day
153
+ @@day ||= begin
154
+ t = Date.today
155
+ "#{t.month}-#{t.day}"
156
+ end
154
157
  end
155
158
 
156
159
  def self.r
157
- "\e[31m"
160
+ @@r ||= HOLIDAY_COLORS[day] || "\e[1;31m"
158
161
  end
159
162
 
160
163
  def self.b
161
- "\e[30m"
164
+ @@b ||= HOLIDAY_COLORS[day] || "\e[30m"
165
+ end
166
+
167
+ def self.w
168
+ "\e[1;37m"
162
169
  end
163
170
 
164
171
  def self.reset
172
+ @@b = @@r = @@day = nil
165
173
  "\e[0m"
166
174
  end
167
175
 
@@ -174,7 +182,7 @@ module Sidekiq # :nodoc:
174
182
  #{w} ,$$$$$b#{b}/#{w}md$$$P^'
175
183
  #{w} .d$$$$$$#{b}/#{w}$$$P'
176
184
  #{w} $$^' `"#{b}/#{w}$$$' #{r}____ _ _ _ _
177
- #{w} $: ,$$: #{r} / ___|(_) __| | ___| | _(_) __ _
185
+ #{w} $: #{b}'#{w},$$: #{r} / ___|(_) __| | ___| | _(_) __ _
178
186
  #{w} `b :$$ #{r} \\___ \\| |/ _` |/ _ \\ |/ / |/ _` |
179
187
  #{w} $$: #{r} ___) | | (_| | __/ <| | (_| |
180
188
  #{w} $$ #{r}|____/|_|\\__,_|\\___|_|\\_\\_|\\__, |
@@ -201,14 +209,25 @@ module Sidekiq # :nodoc:
201
209
  cli.logger.warn "<no backtrace available>"
202
210
  end
203
211
  end
212
+ },
213
+ "INFO" => ->(cli) {
214
+ cli.logger.error { "DEPRECATED: the INFO signal does not work on Linux, use TTIN instead." }
215
+ Thread.list.each do |thread|
216
+ cli.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
217
+ if thread.backtrace
218
+ cli.logger.warn thread.backtrace.join("\n")
219
+ else
220
+ cli.logger.warn "<no backtrace available>"
221
+ end
222
+ end
204
223
  }
205
224
  }
206
- UNHANDLED_SIGNAL_HANDLER = ->(cli) { cli.logger.info "No signal handler registered, ignoring" }
207
- SIGNAL_HANDLERS.default = UNHANDLED_SIGNAL_HANDLER
208
225
 
209
226
  def handle_signal(sig)
210
227
  logger.debug "Got #{sig} signal"
211
- SIGNAL_HANDLERS[sig].call(self)
228
+ hndlr = SIGNAL_HANDLERS[sig]
229
+ hndlr ? hndlr.call(self) :
230
+ logger.warn("No #{sig} signal handler registered, ignoring")
212
231
  end
213
232
 
214
233
  private
@@ -272,6 +291,18 @@ module Sidekiq # :nodoc:
272
291
 
273
292
  # merge with defaults
274
293
  @config.merge!(opts)
294
+
295
+ @config.default_capsule.tap do |cap|
296
+ cap.queues = opts[:queues]
297
+ cap.concurrency = opts[:concurrency] || @config[:concurrency]
298
+ end
299
+
300
+ opts[:capsules]&.each do |name, cap_config|
301
+ @config.capsule(name.to_s) do |cap|
302
+ cap.queues = cap_config[:queues]
303
+ cap.concurrency = cap_config[:concurrency]
304
+ end
305
+ end
275
306
  end
276
307
 
277
308
  def boot_application
@@ -279,30 +310,18 @@ module Sidekiq # :nodoc:
279
310
 
280
311
  if File.directory?(@config[:require])
281
312
  require "rails"
282
- if ::Rails::VERSION::MAJOR < 5
283
- raise "Sidekiq no longer supports this version of Rails"
284
- else
285
- require "sidekiq/rails"
286
- require File.expand_path("#{@config[:require]}/config/environment.rb")
313
+ if ::Rails::VERSION::MAJOR < 7
314
+ warn "Sidekiq #{Sidekiq::VERSION} only supports Rails 7+"
287
315
  end
288
- @config[:tag] ||= default_tag
316
+ require "sidekiq/rails"
317
+ require File.expand_path("#{@config[:require]}/config/environment.rb")
318
+ @config[:tag] ||= default_tag(::Rails.root)
289
319
  else
290
320
  require @config[:require]
321
+ @config[:tag] ||= default_tag
291
322
  end
292
323
  end
293
324
 
294
- def default_tag
295
- dir = ::Rails.root
296
- name = File.basename(dir)
297
- prevdir = File.dirname(dir) # Capistrano release directory?
298
- if name.to_i != 0 && prevdir
299
- if File.basename(prevdir) == "releases"
300
- return File.basename(File.dirname(prevdir))
301
- end
302
- end
303
- name
304
- end
305
-
306
325
  def validate!
307
326
  if !File.exist?(@config[:require]) ||
308
327
  (File.directory?(@config[:require]) && !File.exist?("#{@config[:require]}/config/application.rb"))
@@ -332,10 +351,6 @@ module Sidekiq # :nodoc:
332
351
  opts[:concurrency] = Integer(arg)
333
352
  end
334
353
 
335
- o.on "-d", "--daemon", "Daemonize process" do |arg|
336
- puts "ERROR: Daemonization mode was removed in Sidekiq 6.0, please use a proper process supervisor to start and manage your services"
337
- end
338
-
339
354
  o.on "-e", "--environment ENV", "Application environment" do |arg|
340
355
  opts[:environment] = arg
341
356
  end
@@ -345,8 +360,8 @@ module Sidekiq # :nodoc:
345
360
  end
346
361
 
347
362
  o.on "-q", "--queue QUEUE[,WEIGHT]", "Queues to process with optional weights" do |arg|
348
- queue, weight = arg.split(",")
349
- parse_queue opts, queue, weight
363
+ opts[:queues] ||= []
364
+ opts[:queues] << arg
350
365
  end
351
366
 
352
367
  o.on "-r", "--require [PATH|DIR]", "Location of Rails application with jobs or file to require" do |arg|
@@ -365,15 +380,7 @@ module Sidekiq # :nodoc:
365
380
  opts[:config_file] = arg
366
381
  end
367
382
 
368
- o.on "-L", "--logfile PATH", "path to writable logfile" do |arg|
369
- puts "ERROR: Logfile redirection was removed in Sidekiq 6.0, Sidekiq will only log to STDOUT"
370
- end
371
-
372
- o.on "-P", "--pidfile PATH", "path to pidfile" do |arg|
373
- puts "ERROR: PID file creation was removed in Sidekiq 6.0, please use a proper process supervisor to start and manage your services"
374
- end
375
-
376
- o.on "-V", "--version", "Print version and exit" do |arg|
383
+ o.on "-V", "--version", "Print version and exit" do
377
384
  puts "Sidekiq #{Sidekiq::VERSION}"
378
385
  die(0)
379
386
  end
@@ -389,13 +396,19 @@ module Sidekiq # :nodoc:
389
396
  end
390
397
 
391
398
  def initialize_logger
392
- @config.logger.level = ::Logger::DEBUG if @config[:verbose]
399
+ if @config[:verbose] || ENV["DEBUG_INVOCATION"] == "1"
400
+ # DEBUG_INVOCATION is a systemd-ism triggered by
401
+ # RestartMode=debug. We turn on debugging when the
402
+ # sidekiq process crashes and is restarted with this flag.
403
+ @config.logger.level = ::Logger::DEBUG
404
+ end
393
405
  end
394
406
 
395
407
  def parse_config(path)
396
- erb = ERB.new(File.read(path))
408
+ erb = ERB.new(File.read(path), trim_mode: "-")
397
409
  erb.filename = File.expand_path(path)
398
- opts = load_yaml(erb.result) || {}
410
+ require "yaml"
411
+ opts = YAML.safe_load(erb.result, permitted_classes: [Symbol], aliases: true) || {}
399
412
 
400
413
  if opts.respond_to? :deep_symbolize_keys!
401
414
  opts.deep_symbolize_keys!
@@ -406,31 +419,9 @@ module Sidekiq # :nodoc:
406
419
  opts = opts.merge(opts.delete(environment.to_sym) || {})
407
420
  opts.delete(:strict)
408
421
 
409
- parse_queues(opts, opts.delete(:queues) || [])
410
-
411
422
  opts
412
423
  end
413
424
 
414
- def load_yaml(src)
415
- if Psych::VERSION > "4.0"
416
- YAML.safe_load(src, permitted_classes: [Symbol], aliases: true)
417
- else
418
- YAML.load(src)
419
- end
420
- end
421
-
422
- def parse_queues(opts, queues_and_weights)
423
- queues_and_weights.each { |queue_and_weight| parse_queue(opts, *queue_and_weight) }
424
- end
425
-
426
- def parse_queue(opts, queue, weight = nil)
427
- opts[:queues] ||= []
428
- opts[:strict] = true if opts[:strict].nil?
429
- raise ArgumentError, "queues: #{queue} cannot be defined twice" if opts[:queues].include?(queue)
430
- [weight.to_i, 1].max.times { opts[:queues] << queue.to_s }
431
- opts[:strict] = false if weight.to_i > 0
432
- end
433
-
434
425
  def rails_app?
435
426
  defined?(::Rails) && ::Rails.respond_to?(:application)
436
427
  end
@@ -438,4 +429,5 @@ module Sidekiq # :nodoc:
438
429
  end
439
430
 
440
431
  require "sidekiq/systemd"
441
- require "sidekiq/metrics/tracking" if ENV["SIDEKIQ_METRICS_BETA"]
432
+ require "sidekiq/metrics/tracking"
433
+ require "sidekiq/job/interrupt_handler"