sidekiq 4.2.4 → 6.4.0

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 (143) hide show
  1. checksums.yaml +5 -5
  2. data/Changes.md +523 -0
  3. data/LICENSE +3 -3
  4. data/README.md +23 -36
  5. data/bin/sidekiq +26 -2
  6. data/bin/sidekiqload +28 -38
  7. data/bin/sidekiqmon +8 -0
  8. data/lib/generators/sidekiq/job_generator.rb +57 -0
  9. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  10. data/lib/generators/sidekiq/templates/job_spec.rb.erb +6 -0
  11. data/lib/generators/sidekiq/templates/job_test.rb.erb +8 -0
  12. data/lib/sidekiq/api.rb +403 -243
  13. data/lib/sidekiq/cli.rb +230 -211
  14. data/lib/sidekiq/client.rb +53 -64
  15. data/lib/sidekiq/delay.rb +43 -0
  16. data/lib/sidekiq/exception_handler.rb +12 -16
  17. data/lib/sidekiq/extensions/action_mailer.rb +15 -24
  18. data/lib/sidekiq/extensions/active_record.rb +15 -12
  19. data/lib/sidekiq/extensions/class_methods.rb +16 -13
  20. data/lib/sidekiq/extensions/generic_proxy.rb +14 -6
  21. data/lib/sidekiq/fetch.rb +39 -31
  22. data/lib/sidekiq/job.rb +13 -0
  23. data/lib/sidekiq/job_logger.rb +63 -0
  24. data/lib/sidekiq/job_retry.rb +261 -0
  25. data/lib/sidekiq/job_util.rb +65 -0
  26. data/lib/sidekiq/launcher.rb +170 -71
  27. data/lib/sidekiq/logger.rb +166 -0
  28. data/lib/sidekiq/manager.rb +21 -26
  29. data/lib/sidekiq/middleware/chain.rb +20 -8
  30. data/lib/sidekiq/middleware/current_attributes.rb +57 -0
  31. data/lib/sidekiq/middleware/i18n.rb +5 -7
  32. data/lib/sidekiq/monitor.rb +133 -0
  33. data/lib/sidekiq/paginator.rb +18 -14
  34. data/lib/sidekiq/processor.rb +161 -70
  35. data/lib/sidekiq/rails.rb +41 -73
  36. data/lib/sidekiq/redis_connection.rb +65 -20
  37. data/lib/sidekiq/scheduled.rb +95 -34
  38. data/lib/sidekiq/sd_notify.rb +149 -0
  39. data/lib/sidekiq/systemd.rb +24 -0
  40. data/lib/sidekiq/testing/inline.rb +2 -1
  41. data/lib/sidekiq/testing.rb +52 -26
  42. data/lib/sidekiq/util.rb +60 -14
  43. data/lib/sidekiq/version.rb +2 -1
  44. data/lib/sidekiq/web/action.rb +15 -15
  45. data/lib/sidekiq/web/application.rb +115 -89
  46. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  47. data/lib/sidekiq/web/helpers.rb +151 -83
  48. data/lib/sidekiq/web/router.rb +27 -19
  49. data/lib/sidekiq/web.rb +65 -109
  50. data/lib/sidekiq/worker.rb +284 -41
  51. data/lib/sidekiq.rb +93 -60
  52. data/sidekiq.gemspec +24 -22
  53. data/web/assets/images/apple-touch-icon.png +0 -0
  54. data/web/assets/javascripts/application.js +83 -64
  55. data/web/assets/javascripts/dashboard.js +81 -85
  56. data/web/assets/stylesheets/application-dark.css +143 -0
  57. data/web/assets/stylesheets/application-rtl.css +242 -0
  58. data/web/assets/stylesheets/application.css +319 -143
  59. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  60. data/web/assets/stylesheets/bootstrap.css +2 -2
  61. data/web/locales/ar.yml +87 -0
  62. data/web/locales/de.yml +14 -2
  63. data/web/locales/en.yml +8 -1
  64. data/web/locales/es.yml +22 -5
  65. data/web/locales/fa.yml +80 -0
  66. data/web/locales/fr.yml +10 -3
  67. data/web/locales/he.yml +79 -0
  68. data/web/locales/ja.yml +12 -4
  69. data/web/locales/lt.yml +83 -0
  70. data/web/locales/pl.yml +4 -4
  71. data/web/locales/ru.yml +4 -0
  72. data/web/locales/ur.yml +80 -0
  73. data/web/locales/vi.yml +83 -0
  74. data/web/views/_footer.erb +5 -2
  75. data/web/views/_job_info.erb +4 -3
  76. data/web/views/_nav.erb +4 -18
  77. data/web/views/_paging.erb +1 -1
  78. data/web/views/_poll_link.erb +2 -5
  79. data/web/views/_summary.erb +7 -7
  80. data/web/views/busy.erb +60 -22
  81. data/web/views/dashboard.erb +23 -15
  82. data/web/views/dead.erb +3 -3
  83. data/web/views/layout.erb +14 -3
  84. data/web/views/morgue.erb +19 -12
  85. data/web/views/queue.erb +24 -14
  86. data/web/views/queues.erb +14 -4
  87. data/web/views/retries.erb +22 -13
  88. data/web/views/retry.erb +4 -4
  89. data/web/views/scheduled.erb +7 -4
  90. metadata +49 -198
  91. data/.github/contributing.md +0 -32
  92. data/.github/issue_template.md +0 -4
  93. data/.gitignore +0 -12
  94. data/.travis.yml +0 -12
  95. data/3.0-Upgrade.md +0 -70
  96. data/4.0-Upgrade.md +0 -53
  97. data/COMM-LICENSE +0 -95
  98. data/Ent-Changes.md +0 -146
  99. data/Gemfile +0 -29
  100. data/Pro-2.0-Upgrade.md +0 -138
  101. data/Pro-3.0-Upgrade.md +0 -44
  102. data/Pro-Changes.md +0 -585
  103. data/Rakefile +0 -9
  104. data/bin/sidekiqctl +0 -99
  105. data/code_of_conduct.md +0 -50
  106. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +0 -6
  107. data/lib/generators/sidekiq/templates/worker_test.rb.erb +0 -8
  108. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  109. data/lib/sidekiq/core_ext.rb +0 -106
  110. data/lib/sidekiq/logging.rb +0 -106
  111. data/lib/sidekiq/middleware/server/active_record.rb +0 -13
  112. data/lib/sidekiq/middleware/server/logging.rb +0 -40
  113. data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
  114. data/test/config.yml +0 -9
  115. data/test/env_based_config.yml +0 -11
  116. data/test/fake_env.rb +0 -1
  117. data/test/fixtures/en.yml +0 -2
  118. data/test/helper.rb +0 -75
  119. data/test/test_actors.rb +0 -138
  120. data/test/test_api.rb +0 -528
  121. data/test/test_cli.rb +0 -418
  122. data/test/test_client.rb +0 -266
  123. data/test/test_exception_handler.rb +0 -56
  124. data/test/test_extensions.rb +0 -127
  125. data/test/test_fetch.rb +0 -50
  126. data/test/test_launcher.rb +0 -95
  127. data/test/test_logging.rb +0 -35
  128. data/test/test_manager.rb +0 -50
  129. data/test/test_middleware.rb +0 -158
  130. data/test/test_processor.rb +0 -235
  131. data/test/test_rails.rb +0 -22
  132. data/test/test_redis_connection.rb +0 -132
  133. data/test/test_retry.rb +0 -326
  134. data/test/test_retry_exhausted.rb +0 -149
  135. data/test/test_scheduled.rb +0 -115
  136. data/test/test_scheduling.rb +0 -58
  137. data/test/test_sidekiq.rb +0 -107
  138. data/test/test_testing.rb +0 -143
  139. data/test/test_testing_fake.rb +0 -357
  140. data/test/test_testing_inline.rb +0 -94
  141. data/test/test_util.rb +0 -13
  142. data/test/test_web.rb +0 -726
  143. data/test/test_web_helpers.rb +0 -54
data/lib/sidekiq/cli.rb CHANGED
@@ -1,216 +1,259 @@
1
1
  # frozen_string_literal: true
2
- # encoding: utf-8
2
+
3
3
  $stdout.sync = true
4
4
 
5
- require 'yaml'
6
- require 'singleton'
7
- require 'optparse'
8
- require 'erb'
9
- require 'fileutils'
5
+ require "yaml"
6
+ require "singleton"
7
+ require "optparse"
8
+ require "erb"
9
+ require "fileutils"
10
10
 
11
- require 'sidekiq'
12
- require 'sidekiq/util'
11
+ require "sidekiq"
12
+ require "sidekiq/launcher"
13
+ require "sidekiq/util"
13
14
 
14
15
  module Sidekiq
15
16
  class CLI
16
17
  include Util
17
18
  include Singleton unless $TESTING
18
19
 
19
- PROCTITLES = [
20
- proc { 'sidekiq'.freeze },
21
- proc { Sidekiq::VERSION },
22
- proc { |me, data| data['tag'] },
23
- proc { |me, data| "[#{Processor::WORKER_STATE.size} of #{data['concurrency']} busy]" },
24
- proc { |me, data| "stopping" if me.stopping? },
25
- ]
26
-
27
- # Used for CLI testing
28
- attr_accessor :code
29
20
  attr_accessor :launcher
30
21
  attr_accessor :environment
31
22
 
32
- def initialize
33
- @code = nil
34
- end
35
-
36
- def parse(args=ARGV)
37
- @code = nil
38
-
23
+ def parse(args = ARGV)
39
24
  setup_options(args)
40
25
  initialize_logger
41
26
  validate!
42
- daemonize
43
- write_pid
27
+ end
28
+
29
+ def jruby?
30
+ defined?(::JRUBY_VERSION)
44
31
  end
45
32
 
46
33
  # Code within this method is not tested because it alters
47
34
  # global process state irreversibly. PRs which improve the
48
35
  # test coverage of Sidekiq::CLI are welcomed.
49
- def run
50
- boot_system
51
- print_banner
36
+ def run(boot_app: true)
37
+ boot_application if boot_app
52
38
 
53
- self_read, self_write = IO.pipe
39
+ if environment == "development" && $stdout.tty? && Sidekiq.log_formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
40
+ print_banner
41
+ end
42
+ logger.info "Booted Rails #{::Rails.version} application in #{environment} environment" if rails_app?
54
43
 
55
- %w(INT TERM USR1 USR2 TTIN).each do |sig|
56
- begin
57
- trap sig do
58
- self_write.puts(sig)
44
+ self_read, self_write = IO.pipe
45
+ sigs = %w[INT TERM TTIN TSTP]
46
+ # USR1 and USR2 don't work on the JVM
47
+ sigs << "USR2" if Sidekiq.pro? && !jruby?
48
+ sigs.each do |sig|
49
+ old_handler = Signal.trap(sig) do
50
+ if old_handler.respond_to?(:call)
51
+ begin
52
+ old_handler.call
53
+ rescue Exception => exc
54
+ # signal handlers can't use Logger so puts only
55
+ puts ["Error in #{sig} handler", exc].inspect
56
+ end
59
57
  end
60
- rescue ArgumentError
61
- puts "Signal #{sig} not supported"
58
+ self_write.puts(sig)
62
59
  end
60
+ rescue ArgumentError
61
+ puts "Signal #{sig} not supported"
63
62
  end
64
63
 
65
64
  logger.info "Running in #{RUBY_DESCRIPTION}"
66
65
  logger.info Sidekiq::LICENSE
67
- logger.info "Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org" unless defined?(::Sidekiq::Pro)
66
+ logger.info "Upgrade to Sidekiq Pro for more features and support: https://sidekiq.org" unless defined?(::Sidekiq::Pro)
68
67
 
69
68
  # touch the connection pool so it is created before we
70
69
  # fire startup and start multithreading.
71
- ver = Sidekiq.redis_info['redis_version']
72
- raise "You are using Redis v#{ver}, Sidekiq requires Redis v2.8.0 or greater" if ver < '2.8'
70
+ info = Sidekiq.redis_info
71
+ ver = info["redis_version"]
72
+ raise "You are connecting to Redis v#{ver}, Sidekiq requires Redis v4.0.0 or greater" if ver < "4"
73
+
74
+ maxmemory_policy = info["maxmemory_policy"]
75
+ if maxmemory_policy != "noeviction"
76
+ logger.warn <<~EOM
77
+
78
+
79
+ WARNING: Your Redis instance will evict Sidekiq data under heavy load.
80
+ The 'noeviction' maxmemory policy is recommended (current policy: '#{maxmemory_policy}').
81
+ See: https://github.com/mperham/sidekiq/wiki/Using-Redis#memory
82
+
83
+ EOM
84
+ end
85
+
86
+ # Since the user can pass us a connection pool explicitly in the initializer, we
87
+ # need to verify the size is large enough or else Sidekiq's performance is dramatically slowed.
88
+ cursize = Sidekiq.redis_pool.size
89
+ needed = Sidekiq.options[:concurrency] + 2
90
+ raise "Your pool of #{cursize} Redis connections is too small, please increase the size to at least #{needed}" if cursize < needed
91
+
92
+ # cache process identity
93
+ Sidekiq.options[:identity] = identity
73
94
 
74
95
  # Touch middleware so it isn't lazy loaded by multiple threads, #3043
75
96
  Sidekiq.server_middleware
76
97
 
77
98
  # Before this point, the process is initializing with just the main thread.
78
99
  # Starting here the process will now have multiple threads running.
79
- fire_event(:startup)
100
+ fire_event(:startup, reverse: false, reraise: true)
80
101
 
81
- logger.debug { "Client Middleware: #{Sidekiq.client_middleware.map(&:klass).join(', ')}" }
82
- logger.debug { "Server Middleware: #{Sidekiq.server_middleware.map(&:klass).join(', ')}" }
102
+ logger.debug { "Client Middleware: #{Sidekiq.client_middleware.map(&:klass).join(", ")}" }
103
+ logger.debug { "Server Middleware: #{Sidekiq.server_middleware.map(&:klass).join(", ")}" }
83
104
 
84
- if !options[:daemon]
85
- logger.info 'Starting processing, hit Ctrl-C to stop'
105
+ launch(self_read)
106
+ end
107
+
108
+ def launch(self_read)
109
+ if environment == "development" && $stdout.tty?
110
+ logger.info "Starting processing, hit Ctrl-C to stop"
86
111
  end
87
112
 
88
- require 'sidekiq/launcher'
89
113
  @launcher = Sidekiq::Launcher.new(options)
90
114
 
91
115
  begin
92
116
  launcher.run
93
117
 
94
- while readable_io = IO.select([self_read])
118
+ while (readable_io = IO.select([self_read]))
95
119
  signal = readable_io.first[0].gets.strip
96
120
  handle_signal(signal)
97
121
  end
98
122
  rescue Interrupt
99
- logger.info 'Shutting down'
123
+ logger.info "Shutting down"
100
124
  launcher.stop
101
- # Explicitly exit so busy Processor threads can't block
102
- # process shutdown.
103
125
  logger.info "Bye!"
126
+
127
+ # Explicitly exit so busy Processor threads won't block process shutdown.
128
+ #
129
+ # NB: slow at_exit handlers will prevent a timely exit if they take
130
+ # a while to run. If Sidekiq is getting here but the process isn't exiting,
131
+ # use the TTIN signal to determine where things are stuck.
104
132
  exit(0)
105
133
  end
106
134
  end
107
135
 
136
+ def self.w
137
+ "\e[37m"
138
+ end
139
+
140
+ def self.r
141
+ "\e[31m"
142
+ end
143
+
144
+ def self.b
145
+ "\e[30m"
146
+ end
147
+
148
+ def self.reset
149
+ "\e[0m"
150
+ end
151
+
108
152
  def self.banner
109
- %q{
110
- m,
111
- `$b
112
- .ss, $$: .,d$
113
- `$$P,d$P' .,md$P"'
114
- ,$$$$$bmmd$$$P^'
115
- .d$$$$$$$$$$P'
116
- $$^' `"^$$$' ____ _ _ _ _
117
- $: ,$$: / ___|(_) __| | ___| | _(_) __ _
118
- `b :$$ \___ \| |/ _` |/ _ \ |/ / |/ _` |
119
- $$: ___) | | (_| | __/ <| | (_| |
120
- $$ |____/|_|\__,_|\___|_|\_\_|\__, |
121
- .d$$ |_|
122
- }
153
+ %{
154
+ #{w} m,
155
+ #{w} `$b
156
+ #{w} .ss, $$: .,d$
157
+ #{w} `$$P,d$P' .,md$P"'
158
+ #{w} ,$$$$$b#{b}/#{w}md$$$P^'
159
+ #{w} .d$$$$$$#{b}/#{w}$$$P'
160
+ #{w} $$^' `"#{b}/#{w}$$$' #{r}____ _ _ _ _
161
+ #{w} $: ,$$: #{r} / ___|(_) __| | ___| | _(_) __ _
162
+ #{w} `b :$$ #{r} \\___ \\| |/ _` |/ _ \\ |/ / |/ _` |
163
+ #{w} $$: #{r} ___) | | (_| | __/ <| | (_| |
164
+ #{w} $$ #{r}|____/|_|\\__,_|\\___|_|\\_\\_|\\__, |
165
+ #{w} .d$$ #{r} |_|
166
+ #{reset}}
123
167
  end
124
168
 
125
- def handle_signal(sig)
126
- Sidekiq.logger.debug "Got #{sig} signal"
127
- case sig
128
- when 'INT'
129
- # Handle Ctrl-C in JRuby like MRI
130
- # http://jira.codehaus.org/browse/JRUBY-4637
131
- raise Interrupt
132
- when 'TERM'
133
- # Heroku sends TERM and then waits 10 seconds for process to exit.
134
- raise Interrupt
135
- when 'USR1'
136
- Sidekiq.logger.info "Received USR1, no longer accepting new work"
137
- launcher.quiet
138
- when 'USR2'
139
- if Sidekiq.options[:logfile]
140
- Sidekiq.logger.info "Received USR2, reopening log file"
141
- Sidekiq::Logging.reopen_logs
142
- end
143
- when 'TTIN'
169
+ SIGNAL_HANDLERS = {
170
+ # Ctrl-C in terminal
171
+ "INT" => ->(cli) { raise Interrupt },
172
+ # TERM is the signal that Sidekiq must exit.
173
+ # Heroku sends TERM and then waits 30 seconds for process to exit.
174
+ "TERM" => ->(cli) { raise Interrupt },
175
+ "TSTP" => ->(cli) {
176
+ Sidekiq.logger.info "Received TSTP, no longer accepting new work"
177
+ cli.launcher.quiet
178
+ },
179
+ "TTIN" => ->(cli) {
144
180
  Thread.list.each do |thread|
145
- Sidekiq.logger.warn "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
181
+ Sidekiq.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread.name}"
146
182
  if thread.backtrace
147
183
  Sidekiq.logger.warn thread.backtrace.join("\n")
148
184
  else
149
185
  Sidekiq.logger.warn "<no backtrace available>"
150
186
  end
151
187
  end
152
- end
188
+ }
189
+ }
190
+ UNHANDLED_SIGNAL_HANDLER = ->(cli) { Sidekiq.logger.info "No signal handler registered, ignoring" }
191
+ SIGNAL_HANDLERS.default = UNHANDLED_SIGNAL_HANDLER
192
+
193
+ def handle_signal(sig)
194
+ Sidekiq.logger.debug "Got #{sig} signal"
195
+ SIGNAL_HANDLERS[sig].call(self)
153
196
  end
154
197
 
155
198
  private
156
199
 
157
200
  def print_banner
158
- # Print logo and banner for development
159
- if environment == 'development' && $stdout.tty?
160
- puts "\e[#{31}m"
161
- puts Sidekiq::CLI.banner
162
- puts "\e[0m"
163
- end
201
+ puts "\e[31m"
202
+ puts Sidekiq::CLI.banner
203
+ puts "\e[0m"
164
204
  end
165
205
 
166
- def daemonize
167
- return unless options[:daemon]
168
-
169
- raise ArgumentError, "You really should set a logfile if you're going to daemonize" unless options[:logfile]
170
- files_to_reopen = []
171
- ObjectSpace.each_object(File) do |file|
172
- files_to_reopen << file unless file.closed?
173
- end
174
-
175
- ::Process.daemon(true, true)
176
-
177
- files_to_reopen.each do |file|
178
- begin
179
- file.reopen file.path, "a+"
180
- file.sync = true
181
- rescue ::Exception
182
- end
183
- end
184
-
185
- [$stdout, $stderr].each do |io|
186
- File.open(options[:logfile], 'ab') do |f|
187
- io.reopen(f)
188
- end
189
- io.sync = true
190
- end
191
- $stdin.reopen('/dev/null')
192
-
193
- initialize_logger
206
+ def set_environment(cli_env)
207
+ # See #984 for discussion.
208
+ # APP_ENV is now the preferred ENV term since it is not tech-specific.
209
+ # Both Sinatra 2.0+ and Sidekiq support this term.
210
+ # RAILS_ENV and RACK_ENV are there for legacy support.
211
+ @environment = cli_env || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
194
212
  end
195
213
 
196
- def set_environment(cli_env)
197
- @environment = cli_env || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
214
+ def symbolize_keys_deep!(hash)
215
+ hash.keys.each do |k|
216
+ symkey = k.respond_to?(:to_sym) ? k.to_sym : k
217
+ hash[symkey] = hash.delete k
218
+ symbolize_keys_deep! hash[symkey] if hash[symkey].is_a? Hash
219
+ end
198
220
  end
199
221
 
200
222
  alias_method :die, :exit
201
223
  alias_method :☠, :exit
202
224
 
203
225
  def setup_options(args)
226
+ # parse CLI options
204
227
  opts = parse_options(args)
228
+
205
229
  set_environment opts[:environment]
206
230
 
207
- cfile = opts[:config_file]
208
- opts = parse_config(cfile).merge(opts) if cfile
231
+ # check config file presence
232
+ if opts[:config_file]
233
+ unless File.exist?(opts[:config_file])
234
+ raise ArgumentError, "No such file #{opts[:config_file]}"
235
+ end
236
+ else
237
+ config_dir = if File.directory?(opts[:require].to_s)
238
+ File.join(opts[:require], "config")
239
+ else
240
+ File.join(options[:require], "config")
241
+ end
209
242
 
210
- opts[:strict] = true if opts[:strict].nil?
211
- opts[:concurrency] = Integer(ENV["RAILS_MAX_THREADS"]) if !opts[:concurrency] && ENV["RAILS_MAX_THREADS"]
212
- opts[:identity] = identity
243
+ %w[sidekiq.yml sidekiq.yml.erb].each do |config_file|
244
+ path = File.join(config_dir, config_file)
245
+ opts[:config_file] ||= path if File.exist?(path)
246
+ end
247
+ end
213
248
 
249
+ # parse config file options
250
+ opts = parse_config(opts[:config_file]).merge(opts) if opts[:config_file]
251
+
252
+ # set defaults
253
+ opts[:queues] = ["default"] if opts[:queues].nil?
254
+ opts[:concurrency] = Integer(ENV["RAILS_MAX_THREADS"]) if opts[:concurrency].nil? && ENV["RAILS_MAX_THREADS"]
255
+
256
+ # merge with defaults
214
257
  options.merge!(opts)
215
258
  end
216
259
 
@@ -218,43 +261,29 @@ module Sidekiq
218
261
  Sidekiq.options
219
262
  end
220
263
 
221
- def boot_system
222
- ENV['RACK_ENV'] = ENV['RAILS_ENV'] = environment
223
-
224
- raise ArgumentError, "#{options[:require]} does not exist" unless File.exist?(options[:require])
264
+ def boot_application
265
+ ENV["RACK_ENV"] = ENV["RAILS_ENV"] = environment
225
266
 
226
267
  if File.directory?(options[:require])
227
- require 'rails'
228
- if ::Rails::VERSION::MAJOR < 4
229
- require 'sidekiq/rails'
230
- require File.expand_path("#{options[:require]}/config/environment.rb")
231
- ::Rails.application.eager_load!
232
- elsif ::Rails::VERSION::MAJOR == 4
233
- # Painful contortions, see 1791 for discussion
234
- require File.expand_path("#{options[:require]}/config/application.rb")
235
- ::Rails::Application.initializer "sidekiq.eager_load" do
236
- ::Rails.application.config.eager_load = true
237
- end
238
- require 'sidekiq/rails'
239
- require File.expand_path("#{options[:require]}/config/environment.rb")
268
+ require "rails"
269
+ if ::Rails::VERSION::MAJOR < 5
270
+ raise "Sidekiq no longer supports this version of Rails"
240
271
  else
241
- require 'sidekiq/rails'
272
+ require "sidekiq/rails"
242
273
  require File.expand_path("#{options[:require]}/config/environment.rb")
243
274
  end
244
275
  options[:tag] ||= default_tag
245
276
  else
246
- not_required_message = "#{options[:require]} was not required, you should use an explicit path: " +
247
- "./#{options[:require]} or /path/to/#{options[:require]}"
248
-
249
- require(options[:require]) || raise(ArgumentError, not_required_message)
277
+ require options[:require]
250
278
  end
251
279
  end
252
280
 
253
281
  def default_tag
254
282
  dir = ::Rails.root
255
283
  name = File.basename(dir)
256
- if name.to_i != 0 && prevdir = File.dirname(dir) # Capistrano release directory?
257
- if File.basename(prevdir) == 'releases'
284
+ prevdir = File.dirname(dir) # Capistrano release directory?
285
+ if name.to_i != 0 && prevdir
286
+ if File.basename(prevdir) == "releases"
258
287
  return File.basename(File.dirname(prevdir))
259
288
  end
260
289
  end
@@ -262,12 +291,10 @@ module Sidekiq
262
291
  end
263
292
 
264
293
  def validate!
265
- options[:queues] << 'default' if options[:queues].empty?
266
-
267
294
  if !File.exist?(options[:require]) ||
268
- (File.directory?(options[:require]) && !File.exist?("#{options[:require]}/config/application.rb"))
295
+ (File.directory?(options[:require]) && !File.exist?("#{options[:require]}/config/application.rb"))
269
296
  logger.info "=================================================================="
270
- logger.info " Please point sidekiq to a Rails 3/4 application or a Ruby file "
297
+ logger.info " Please point Sidekiq to a Rails application or a Ruby file "
271
298
  logger.info " to load your worker classes with -r [DIR|FILE]."
272
299
  logger.info "=================================================================="
273
300
  logger.info @parser
@@ -275,44 +302,45 @@ module Sidekiq
275
302
  end
276
303
 
277
304
  [:concurrency, :timeout].each do |opt|
278
- raise ArgumentError, "#{opt}: #{options[opt]} is not a valid value" if options.has_key?(opt) && options[opt].to_i <= 0
305
+ raise ArgumentError, "#{opt}: #{options[opt]} is not a valid value" if options.key?(opt) && options[opt].to_i <= 0
279
306
  end
280
307
  end
281
308
 
282
309
  def parse_options(argv)
283
310
  opts = {}
311
+ @parser = option_parser(opts)
312
+ @parser.parse!(argv)
313
+ opts
314
+ end
284
315
 
285
- @parser = OptionParser.new do |o|
286
- o.on '-c', '--concurrency INT', "processor threads to use" do |arg|
316
+ def option_parser(opts)
317
+ parser = OptionParser.new { |o|
318
+ o.on "-c", "--concurrency INT", "processor threads to use" do |arg|
287
319
  opts[:concurrency] = Integer(arg)
288
320
  end
289
321
 
290
- o.on '-d', '--daemon', "Daemonize process" do |arg|
291
- opts[:daemon] = arg
322
+ o.on "-d", "--daemon", "Daemonize process" do |arg|
323
+ puts "ERROR: Daemonization mode was removed in Sidekiq 6.0, please use a proper process supervisor to start and manage your services"
292
324
  end
293
325
 
294
- o.on '-e', '--environment ENV', "Application environment" do |arg|
326
+ o.on "-e", "--environment ENV", "Application environment" do |arg|
295
327
  opts[:environment] = arg
296
328
  end
297
329
 
298
- o.on '-g', '--tag TAG', "Process tag for procline" do |arg|
330
+ o.on "-g", "--tag TAG", "Process tag for procline" do |arg|
299
331
  opts[:tag] = arg
300
332
  end
301
333
 
302
- o.on '-i', '--index INT', "unique process index on this machine" do |arg|
303
- opts[:index] = Integer(arg.match(/\d+/)[0])
304
- end
305
-
306
334
  o.on "-q", "--queue QUEUE[,WEIGHT]", "Queues to process with optional weights" do |arg|
307
335
  queue, weight = arg.split(",")
308
336
  parse_queue opts, queue, weight
309
337
  end
310
338
 
311
- o.on '-r', '--require [PATH|DIR]', "Location of Rails application with workers or file to require" do |arg|
339
+ o.on "-r", "--require [PATH|DIR]", "Location of Rails application with workers or file to require" do |arg|
312
340
  opts[:require] = arg
313
341
  end
314
342
 
315
- o.on '-t', '--timeout NUM', "Shutdown timeout" do |arg|
343
+ o.on "-t", "--timeout NUM", "Shutdown timeout" do |arg|
316
344
  opts[:timeout] = Integer(arg)
317
345
  end
318
346
 
@@ -320,69 +348,53 @@ module Sidekiq
320
348
  opts[:verbose] = arg
321
349
  end
322
350
 
323
- o.on '-C', '--config PATH', "path to YAML config file" do |arg|
351
+ o.on "-C", "--config PATH", "path to YAML config file" do |arg|
324
352
  opts[:config_file] = arg
325
353
  end
326
354
 
327
- o.on '-L', '--logfile PATH', "path to writable logfile" do |arg|
328
- opts[:logfile] = arg
355
+ o.on "-L", "--logfile PATH", "path to writable logfile" do |arg|
356
+ puts "ERROR: Logfile redirection was removed in Sidekiq 6.0, Sidekiq will only log to STDOUT"
329
357
  end
330
358
 
331
- o.on '-P', '--pidfile PATH', "path to pidfile" do |arg|
332
- opts[:pidfile] = arg
359
+ o.on "-P", "--pidfile PATH", "path to pidfile" do |arg|
360
+ puts "ERROR: PID file creation was removed in Sidekiq 6.0, please use a proper process supervisor to start and manage your services"
333
361
  end
334
362
 
335
- o.on '-V', '--version', "Print version and exit" do |arg|
363
+ o.on "-V", "--version", "Print version and exit" do |arg|
336
364
  puts "Sidekiq #{Sidekiq::VERSION}"
337
365
  die(0)
338
366
  end
339
- end
367
+ }
340
368
 
341
- @parser.banner = "sidekiq [options]"
342
- @parser.on_tail "-h", "--help", "Show help" do
343
- logger.info @parser
369
+ parser.banner = "sidekiq [options]"
370
+ parser.on_tail "-h", "--help", "Show help" do
371
+ logger.info parser
344
372
  die 1
345
373
  end
346
- @parser.parse!(argv)
347
374
 
348
- %w[config/sidekiq.yml config/sidekiq.yml.erb].each do |filename|
349
- opts[:config_file] ||= filename if File.exist?(filename)
350
- end
351
-
352
- opts
375
+ parser
353
376
  end
354
377
 
355
378
  def initialize_logger
356
- Sidekiq::Logging.initialize_logger(options[:logfile]) if options[:logfile]
357
-
358
379
  Sidekiq.logger.level = ::Logger::DEBUG if options[:verbose]
359
380
  end
360
381
 
361
- def write_pid
362
- if path = options[:pidfile]
363
- pidfile = File.expand_path(path)
364
- File.open(pidfile, 'w') do |f|
365
- f.puts ::Process.pid
366
- end
367
- end
368
- end
382
+ def parse_config(path)
383
+ erb = ERB.new(File.read(path))
384
+ erb.filename = File.expand_path(path)
385
+ opts = YAML.load(erb.result) || {}
369
386
 
370
- def parse_config(cfile)
371
- opts = {}
372
- if File.exist?(cfile)
373
- opts = YAML.load(ERB.new(IO.read(cfile)).result) || opts
374
- opts = opts.merge(opts.delete(environment) || {})
375
- parse_queues(opts, opts.delete(:queues) || [])
387
+ if opts.respond_to? :deep_symbolize_keys!
388
+ opts.deep_symbolize_keys!
376
389
  else
377
- # allow a non-existent config file so Sidekiq
378
- # can be deployed by cap with just the defaults.
379
- end
380
- ns = opts.delete(:namespace)
381
- if ns
382
- # logger hasn't been initialized yet, puts is all we have.
383
- puts("namespace should be set in your ruby initializer, is ignored in config file")
384
- puts("config.redis = { :url => ..., :namespace => '#{ns}' }")
390
+ symbolize_keys_deep!(opts)
385
391
  end
392
+
393
+ opts = opts.merge(opts.delete(environment.to_sym) || {})
394
+ opts.delete(:strict)
395
+
396
+ parse_queues(opts, opts.delete(:queues) || [])
397
+
386
398
  opts
387
399
  end
388
400
 
@@ -390,11 +402,18 @@ module Sidekiq
390
402
  queues_and_weights.each { |queue_and_weight| parse_queue(opts, *queue_and_weight) }
391
403
  end
392
404
 
393
- def parse_queue(opts, q, weight=nil)
394
- [weight.to_i, 1].max.times do
395
- (opts[:queues] ||= []) << q
396
- end
405
+ def parse_queue(opts, queue, weight = nil)
406
+ opts[:queues] ||= []
407
+ opts[:strict] = true if opts[:strict].nil?
408
+ raise ArgumentError, "queues: #{queue} cannot be defined twice" if opts[:queues].include?(queue)
409
+ [weight.to_i, 1].max.times { opts[:queues] << queue.to_s }
397
410
  opts[:strict] = false if weight.to_i > 0
398
411
  end
412
+
413
+ def rails_app?
414
+ defined?(::Rails) && ::Rails.respond_to?(:application)
415
+ end
399
416
  end
400
417
  end
418
+
419
+ require "sidekiq/systemd"