sidekiq 6.4.1 → 7.0.7

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