sidekiq 6.5.8 → 7.1.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +133 -15
  3. data/README.md +40 -32
  4. data/bin/sidekiq +3 -8
  5. data/bin/sidekiqload +204 -118
  6. data/bin/sidekiqmon +3 -0
  7. data/lib/sidekiq/api.rb +114 -128
  8. data/lib/sidekiq/capsule.rb +127 -0
  9. data/lib/sidekiq/cli.rb +57 -74
  10. data/lib/sidekiq/client.rb +63 -37
  11. data/lib/sidekiq/component.rb +4 -1
  12. data/lib/sidekiq/config.rb +278 -0
  13. data/lib/sidekiq/deploy.rb +62 -0
  14. data/lib/sidekiq/embedded.rb +61 -0
  15. data/lib/sidekiq/fetch.rb +11 -14
  16. data/lib/sidekiq/job.rb +371 -10
  17. data/lib/sidekiq/job_logger.rb +2 -2
  18. data/lib/sidekiq/job_retry.rb +17 -14
  19. data/lib/sidekiq/job_util.rb +49 -15
  20. data/lib/sidekiq/launcher.rb +66 -62
  21. data/lib/sidekiq/logger.rb +1 -26
  22. data/lib/sidekiq/manager.rb +9 -11
  23. data/lib/sidekiq/metrics/query.rb +4 -4
  24. data/lib/sidekiq/metrics/shared.rb +7 -6
  25. data/lib/sidekiq/metrics/tracking.rb +20 -18
  26. data/lib/sidekiq/middleware/chain.rb +19 -18
  27. data/lib/sidekiq/middleware/current_attributes.rb +52 -20
  28. data/lib/sidekiq/monitor.rb +17 -4
  29. data/lib/sidekiq/paginator.rb +3 -3
  30. data/lib/sidekiq/processor.rb +21 -27
  31. data/lib/sidekiq/rails.rb +12 -7
  32. data/lib/sidekiq/redis_client_adapter.rb +11 -69
  33. data/lib/sidekiq/redis_connection.rb +11 -111
  34. data/lib/sidekiq/scheduled.rb +21 -22
  35. data/lib/sidekiq/testing.rb +5 -33
  36. data/lib/sidekiq/transaction_aware_client.rb +4 -5
  37. data/lib/sidekiq/version.rb +2 -1
  38. data/lib/sidekiq/web/application.rb +21 -6
  39. data/lib/sidekiq/web/csrf_protection.rb +1 -1
  40. data/lib/sidekiq/web/helpers.rb +19 -18
  41. data/lib/sidekiq/web.rb +7 -18
  42. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  43. data/lib/sidekiq.rb +76 -274
  44. data/sidekiq.gemspec +12 -10
  45. data/web/assets/javascripts/application.js +18 -0
  46. data/web/assets/javascripts/base-charts.js +106 -0
  47. data/web/assets/javascripts/dashboard-charts.js +166 -0
  48. data/web/assets/javascripts/dashboard.js +3 -223
  49. data/web/assets/javascripts/metrics.js +117 -115
  50. data/web/assets/stylesheets/application-dark.css +4 -0
  51. data/web/assets/stylesheets/application-rtl.css +2 -91
  52. data/web/assets/stylesheets/application.css +23 -298
  53. data/web/locales/ar.yml +70 -70
  54. data/web/locales/cs.yml +62 -62
  55. data/web/locales/da.yml +60 -53
  56. data/web/locales/de.yml +65 -65
  57. data/web/locales/el.yml +2 -7
  58. data/web/locales/en.yml +76 -70
  59. data/web/locales/es.yml +68 -68
  60. data/web/locales/fa.yml +65 -65
  61. data/web/locales/fr.yml +81 -67
  62. data/web/locales/gd.yml +99 -0
  63. data/web/locales/he.yml +65 -64
  64. data/web/locales/hi.yml +59 -59
  65. data/web/locales/it.yml +53 -53
  66. data/web/locales/ja.yml +67 -69
  67. data/web/locales/ko.yml +52 -52
  68. data/web/locales/lt.yml +66 -66
  69. data/web/locales/nb.yml +61 -61
  70. data/web/locales/nl.yml +52 -52
  71. data/web/locales/pl.yml +45 -45
  72. data/web/locales/pt-br.yml +59 -69
  73. data/web/locales/pt.yml +51 -51
  74. data/web/locales/ru.yml +67 -66
  75. data/web/locales/sv.yml +53 -53
  76. data/web/locales/ta.yml +60 -60
  77. data/web/locales/uk.yml +62 -61
  78. data/web/locales/ur.yml +64 -64
  79. data/web/locales/vi.yml +67 -67
  80. data/web/locales/zh-cn.yml +20 -18
  81. data/web/locales/zh-tw.yml +10 -1
  82. data/web/views/_footer.erb +5 -2
  83. data/web/views/_job_info.erb +18 -2
  84. data/web/views/_metrics_period_select.erb +12 -0
  85. data/web/views/_paging.erb +2 -0
  86. data/web/views/_poll_link.erb +1 -1
  87. data/web/views/busy.erb +39 -28
  88. data/web/views/dashboard.erb +36 -5
  89. data/web/views/metrics.erb +33 -20
  90. data/web/views/metrics_for_job.erb +25 -44
  91. data/web/views/morgue.erb +5 -9
  92. data/web/views/queue.erb +10 -14
  93. data/web/views/queues.erb +3 -1
  94. data/web/views/retries.erb +5 -9
  95. data/web/views/scheduled.erb +12 -13
  96. metadata +43 -39
  97. data/lib/sidekiq/delay.rb +0 -43
  98. data/lib/sidekiq/extensions/action_mailer.rb +0 -48
  99. data/lib/sidekiq/extensions/active_record.rb +0 -43
  100. data/lib/sidekiq/extensions/class_methods.rb +0 -43
  101. data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
  102. data/lib/sidekiq/metrics/deploy.rb +0 -47
  103. data/lib/sidekiq/worker.rb +0 -370
  104. data/web/assets/javascripts/graph.js +0 -16
  105. /data/{LICENSE → LICENSE.txt} +0 -0
data/bin/sidekiqload CHANGED
@@ -1,32 +1,63 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ #
4
+ # bin/sidekiqload is a helpful script to load test and
5
+ # performance tune Sidekiq's core. It creates 500,000 no-op
6
+ # jobs and executes them as fast as possible.
7
+ # Example Usage:
8
+ #
9
+ # > RUBY_YJIT_ENABLE=1 LATENCY=0 THREADS=10 bin/sidekiqload
10
+ # Result: Done, 500000 jobs in 20.264945 sec, 24673 jobs/sec
11
+ #
12
+ # Use LATENCY=1 to get a more real world network setup
13
+ # but you'll need to setup and start toxiproxy as noted below.
14
+ #
15
+ # Use AJ=1 to test ActiveJob instead of plain old Sidekiq::Jobs so
16
+ # you can see the runtime performance difference between the two APIs.
17
+ #
18
+ # None of this script is considered a public API and may change over time.
19
+ #
20
+
3
21
  # Quiet some warnings we see when running in warning mode:
4
22
  # RUBYOPT=-w bundle exec sidekiq
5
23
  $TESTING = false
24
+ puts RUBY_DESCRIPTION
25
+ puts(%w[THREADS LATENCY AJ PROFILE].map { |x| "#{x}: #{ENV[x] || "nil"}" }.join(", "))
6
26
 
7
- # require "ruby-prof"
27
+ require "ruby-prof" if ENV["PROFILE"]
8
28
  require "bundler/setup"
9
29
  Bundler.require(:default, :load_test)
10
30
 
11
- require_relative "../lib/sidekiq/cli"
12
- require_relative "../lib/sidekiq/launcher"
13
-
14
- if ENV["SIDEKIQ_REDIS_CLIENT"]
15
- Sidekiq::RedisConnection.adapter = :redis_client
31
+ latency = Integer(ENV["LATENCY"] || 1)
32
+ if latency > 0
33
+ # brew tap shopify/shopify
34
+ # brew install toxiproxy
35
+ # run `toxiproxy-server` in a separate terminal window.
36
+ require "toxiproxy"
37
+ # simulate a non-localhost network for realer-world conditions.
38
+ # adding 1ms of network latency has an ENORMOUS impact on benchmarks
39
+ Toxiproxy.populate([{
40
+ name: "redis",
41
+ listen: "127.0.0.1:6380",
42
+ upstream: "127.0.0.1:6379"
43
+ }])
16
44
  end
17
45
 
18
- Sidekiq.configure_server do |config|
19
- config.options[:concurrency] = 10
20
- config.redis = {db: 13, port: 6380}
21
- # config.redis = { db: 13, port: 6380, driver: :hiredis}
22
- config.options[:queues] << "default"
23
- config.logger.level = Logger::ERROR
24
- config.average_scheduled_poll_interval = 2
25
- config.reliable! if defined?(Sidekiq::Pro)
46
+ if ENV["AJ"]
47
+ require "active_job"
48
+ puts "Using ActiveJob #{ActiveJob::VERSION::STRING}"
49
+ ActiveJob::Base.queue_adapter = :sidekiq
50
+ ActiveJob::Base.logger.level = Logger::WARN
51
+
52
+ class LoadJob < ActiveJob::Base
53
+ def perform(idx, ts = nil)
54
+ puts(Time.now.to_f - ts) if !ts.nil?
55
+ end
56
+ end
26
57
  end
27
58
 
28
59
  class LoadWorker
29
- include Sidekiq::Worker
60
+ include Sidekiq::Job
30
61
  sidekiq_options retry: 1
31
62
  sidekiq_retry_in do |x|
32
63
  1
@@ -38,124 +69,179 @@ class LoadWorker
38
69
  end
39
70
  end
40
71
 
41
- # brew tap shopify/shopify
42
- # brew install toxiproxy
43
- # run `toxiproxy-server` in a separate terminal window.
44
- require "toxiproxy"
45
- # simulate a non-localhost network for realer-world conditions.
46
- # adding 1ms of network latency has an ENORMOUS impact on benchmarks
47
- Toxiproxy.populate([{
48
- name: "redis",
49
- listen: "127.0.0.1:6380",
50
- upstream: "127.0.0.1:6379"
51
- }])
52
-
53
- self_read, self_write = IO.pipe
54
- %w[INT TERM TSTP TTIN].each do |sig|
55
- trap sig do
56
- self_write.puts(sig)
57
- end
58
- rescue ArgumentError
59
- puts "Signal #{sig} not supported"
72
+ def Process.rss
73
+ `ps -o rss= -p #{Process.pid}`.chomp.to_i
60
74
  end
61
75
 
62
- Sidekiq.redis { |c| c.flushdb }
63
- def handle_signal(launcher, sig)
64
- Sidekiq.logger.debug "Got #{sig} signal"
65
- case sig
66
- when "INT"
67
- # Handle Ctrl-C in JRuby like MRI
68
- # http://jira.codehaus.org/browse/JRUBY-4637
69
- raise Interrupt
70
- when "TERM"
71
- # Heroku sends TERM and then waits 30 seconds for process to exit.
72
- raise Interrupt
73
- when "TSTP"
74
- Sidekiq.logger.info "Received TSTP, no longer accepting new work"
75
- launcher.quiet
76
- when "TTIN"
77
- Thread.list.each do |thread|
78
- Sidekiq.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread["label"]}"
79
- if thread.backtrace
80
- Sidekiq.logger.warn thread.backtrace.join("\n")
81
- else
82
- Sidekiq.logger.warn "<no backtrace available>"
76
+ class Loader
77
+ def initialize
78
+ @iter = ENV["GC"] ? 10 : 500
79
+ @count = Integer(ENV["COUNT"] || 1_000)
80
+ @latency = Integer(ENV["LATENCY"] || 1)
81
+ end
82
+
83
+ def configure
84
+ @x = Sidekiq.configure_embed do |config|
85
+ config.redis = {db: 13, port: ((@latency > 0) ? 6380 : 6379)}
86
+ config.concurrency = Integer(ENV.fetch("THREADS", "10"))
87
+ # config.redis = { db: 13, port: 6380, driver: :hiredis}
88
+ config.queues = %w[default]
89
+ config.logger.level = Logger::WARN
90
+ config.average_scheduled_poll_interval = 2
91
+ config.reliable! if defined?(Sidekiq::Pro)
92
+ end
93
+
94
+ @self_read, @self_write = IO.pipe
95
+ %w[INT TERM TSTP TTIN].each do |sig|
96
+ trap sig do
97
+ @self_write.puts(sig)
83
98
  end
99
+ rescue ArgumentError
100
+ puts "Signal #{sig} not supported"
84
101
  end
85
102
  end
86
- end
87
103
 
88
- def Process.rss
89
- `ps -o rss= -p #{Process.pid}`.chomp.to_i
90
- end
104
+ def handle_signal(sig)
105
+ launcher = @x
106
+ Sidekiq.logger.debug "Got #{sig} signal"
107
+ case sig
108
+ when "INT"
109
+ # Handle Ctrl-C in JRuby like MRI
110
+ # http://jira.codehaus.org/browse/JRUBY-4637
111
+ raise Interrupt
112
+ when "TERM"
113
+ # Heroku sends TERM and then waits 30 seconds for process to exit.
114
+ raise Interrupt
115
+ when "TSTP"
116
+ Sidekiq.logger.info "Received TSTP, no longer accepting new work"
117
+ launcher.quiet
118
+ when "TTIN"
119
+ Thread.list.each do |thread|
120
+ Sidekiq.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread["label"]}"
121
+ if thread.backtrace
122
+ Sidekiq.logger.warn thread.backtrace.join("\n")
123
+ else
124
+ Sidekiq.logger.warn "<no backtrace available>"
125
+ end
126
+ end
127
+ end
128
+ end
91
129
 
92
- iter = 10
93
- count = 10_000
130
+ def setup
131
+ Sidekiq.logger.error("Setup RSS: #{Process.rss}")
132
+ Sidekiq.redis { |c| c.flushdb }
133
+ start = Time.now
134
+ if ENV["AJ"]
135
+ @iter.times do
136
+ @count.times do |idx|
137
+ LoadJob.perform_later(idx)
138
+ end
139
+ end
140
+ else
141
+ @iter.times do
142
+ arr = Array.new(@count) { |idx| [idx] }
143
+ Sidekiq::Client.push_bulk("class" => LoadWorker, "args" => arr)
144
+ end
145
+ end
146
+ Sidekiq.logger.warn "Created #{@count * @iter} jobs in #{Time.now - start} sec"
147
+ end
94
148
 
95
- iter.times do
96
- arr = Array.new(count) { |idx| [idx] }
97
- Sidekiq::Client.push_bulk("class" => LoadWorker, "args" => arr)
98
- end
99
- Sidekiq.logger.error "Created #{count * iter} jobs"
149
+ def monitor
150
+ @monitor = Thread.new do
151
+ GC.start
152
+ loop do
153
+ sleep 0.2
154
+ qsize = Sidekiq.redis do |conn|
155
+ conn.llen "queue:default"
156
+ end
157
+ total = qsize
158
+ if total == 0
159
+ ending = Time.now - @start
160
+ size = @iter * @count
161
+ Sidekiq.logger.error("Done, #{size} jobs in #{ending} sec, #{(size / ending).to_i} jobs/sec")
162
+ Sidekiq.logger.error("Ending RSS: #{Process.rss}")
163
+ Sidekiq.logger.error("Now here's the latency for three jobs")
164
+
165
+ if ENV["AJ"]
166
+ LoadJob.perform_later(1, Time.now.to_f)
167
+ LoadJob.perform_later(2, Time.now.to_f)
168
+ LoadJob.perform_later(3, Time.now.to_f)
169
+ else
170
+ LoadWorker.perform_async(1, Time.now.to_f)
171
+ LoadWorker.perform_async(2, Time.now.to_f)
172
+ LoadWorker.perform_async(3, Time.now.to_f)
173
+ end
174
+
175
+ sleep 0.1
176
+ @x.stop
177
+ Process.kill("INT", $$)
178
+ break
179
+ end
180
+ end
181
+ end
182
+ end
100
183
 
101
- start = Time.now
184
+ def with_latency(latency, &block)
185
+ Sidekiq.logger.error "Simulating #{latency}ms of latency between Sidekiq and redis"
186
+ if latency > 0
187
+ Toxiproxy[:redis].downstream(:latency, latency: latency).apply(&block)
188
+ else
189
+ yield
190
+ end
191
+ end
102
192
 
103
- Monitoring = Thread.new do
104
- while true
105
- sleep 0.2
106
- qsize = Sidekiq.redis do |conn|
107
- conn.llen "queue:default"
193
+ def run(name)
194
+ Sidekiq.logger.warn("Starting #{name}")
195
+ monitor
196
+
197
+ if ENV["PROFILE"]
198
+ RubyProf.exclude_threads = [@monitor]
199
+ RubyProf.start
200
+ elsif ENV["GC"]
201
+ GC.start
202
+ GC.compact
203
+ GC.disable
204
+ Sidekiq.logger.error("GC Start RSS: #{Process.rss}")
108
205
  end
109
- total = qsize
110
- # Sidekiq.logger.error("RSS: #{Process.rss} Pending: #{total}")
111
- if total == 0
112
- Sidekiq.logger.error("Done, #{iter * count} jobs in #{Time.now - start} sec")
113
- Sidekiq.logger.error("Now here's the latency for three jobs")
114
-
115
- LoadWorker.perform_async(1, Time.now.to_f)
116
- LoadWorker.perform_async(2, Time.now.to_f)
117
- LoadWorker.perform_async(3, Time.now.to_f)
118
-
119
- sleep 0.2
120
- exit(0)
206
+ @start = Time.now
207
+ with_latency(@latency) do
208
+ @x.run
209
+
210
+ while (readable_io = IO.select([@self_read]))
211
+ signal = readable_io.first[0].gets.strip
212
+ handle_signal(signal)
213
+ end
121
214
  end
215
+ # normal
216
+ rescue Interrupt
217
+ rescue => e
218
+ raise e if $DEBUG
219
+ warn e.message
220
+ warn e.backtrace.join("\n")
221
+ exit 1
222
+ ensure
223
+ @x.stop
122
224
  end
123
- end
124
225
 
125
- def with_latency(latency, &block)
126
- Sidekiq.logger.error "Simulating #{latency}ms of latency between Sidekiq and redis"
127
- if latency > 0
128
- Toxiproxy[:redis].downstream(:latency, latency: latency).apply(&block)
129
- else
130
- yield
226
+ def done
227
+ Sidekiq.logger.error("GC End RSS: #{Process.rss}") if ENV["GC"]
228
+ if ENV["PROFILE"]
229
+ Sidekiq.logger.error("Profiling...")
230
+ result = RubyProf.stop
231
+ printer = RubyProf::GraphHtmlPrinter.new(result)
232
+ printer.print(File.new("output.html", "w"), min_percent: 1)
233
+ end
131
234
  end
132
235
  end
133
236
 
134
- begin
135
- # RubyProf::exclude_threads = [ Monitoring ]
136
- # RubyProf.start
137
- events = Sidekiq.options[:lifecycle_events][:startup]
138
- events.each(&:call)
139
- events.clear
140
-
141
- with_latency(Integer(ENV.fetch("LATENCY", "1"))) do
142
- launcher = Sidekiq::Launcher.new(Sidekiq)
143
- launcher.run
144
-
145
- while readable_io = IO.select([self_read])
146
- signal = readable_io.first[0].gets.strip
147
- handle_signal(launcher, signal)
148
- end
149
- end
150
- rescue SystemExit => e
151
- # Sidekiq.logger.error("Profiling...")
152
- # result = RubyProf.stop
153
- # printer = RubyProf::GraphHtmlPrinter.new(result)
154
- # printer.print(File.new("output.html", "w"), :min_percent => 1)
155
- # normal
156
- rescue => e
157
- raise e if $DEBUG
158
- warn e.message
159
- warn e.backtrace.join("\n")
160
- exit 1
237
+ ll = Loader.new
238
+ ll.configure
239
+
240
+ if ENV["WARM"]
241
+ ll.setup
242
+ ll.run("warmup")
161
243
  end
244
+
245
+ ll.setup
246
+ ll.run("load")
247
+ ll.done
data/bin/sidekiqmon CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  require "sidekiq/monitor"
4
4
 
5
+ # disable the Redis connection pool logging
6
+ Sidekiq.default_configuration.logger.level = :warn
7
+
5
8
  section = "all"
6
9
  section = ARGV[0] if ARGV.size == 1
7
10