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
data/bin/sidekiqload CHANGED
@@ -1,161 +1,258 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ ENV["RUBY_TCP_NO_FAST_FALLBACK"] = "1"
4
+
5
+ #
6
+ # bin/sidekiqload is a helpful script to load test and
7
+ # performance tune Sidekiq's core. It creates 500,000 no-op
8
+ # jobs and executes them as fast as possible.
9
+ # Example Usage:
10
+ #
11
+ # > RUBY_YJIT_ENABLE=1 LATENCY=0 THREADS=10 bin/sidekiqload
12
+ # Result: Done, 500000 jobs in 20.264945 sec, 24673 jobs/sec
13
+ #
14
+ # Use LATENCY=1 to get a more real world network setup
15
+ # but you'll need to setup and start toxiproxy as noted below.
16
+ #
17
+ # Use AJ=1 to test ActiveJob instead of plain old Sidekiq::Jobs so
18
+ # you can see the runtime performance difference between the two APIs.
19
+ #
20
+ # None of this script is considered a public API and may change over time.
21
+ #
22
+
3
23
  # Quiet some warnings we see when running in warning mode:
4
24
  # RUBYOPT=-w bundle exec sidekiq
5
25
  $TESTING = false
26
+ puts RUBY_DESCRIPTION
27
+ puts(%w[THREADS LATENCY AJ PROFILE].map { |x| "#{x}: #{ENV[x] || "nil"}" }.join(", "))
6
28
 
7
- # require "ruby-prof"
29
+ require "ruby-prof" if ENV["PROFILE"]
8
30
  require "bundler/setup"
9
31
  Bundler.require(:default, :load_test)
10
32
 
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
33
+ latency = Integer(ENV["LATENCY"] || 0)
34
+ if latency > 0
35
+ # brew tap shopify/shopify
36
+ # brew install toxiproxy
37
+ # run `toxiproxy-server` in a separate terminal window.
38
+ require "toxiproxy"
39
+ # simulate a non-localhost network for realer-world conditions.
40
+ # adding 1ms of network latency has an ENORMOUS impact on benchmarks
41
+ Toxiproxy.populate([{
42
+ name: "redis",
43
+ listen: "127.0.0.1:6380",
44
+ upstream: "127.0.0.1:6379"
45
+ }])
16
46
  end
17
47
 
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)
48
+ if ENV["AJ"]
49
+ require "active_job"
50
+ puts "Using ActiveJob #{ActiveJob::VERSION::STRING}"
51
+ ActiveJob::Base.queue_adapter = :sidekiq
52
+ ActiveJob::Base.logger.level = Logger::WARN
53
+
54
+ class LoadJob < ActiveJob::Base
55
+ def perform(string, idx, hash, ts = nil)
56
+ puts(Time.now.to_f - ts) if !ts.nil?
57
+ end
58
+ end
26
59
  end
27
60
 
28
61
  class LoadWorker
29
- include Sidekiq::Worker
62
+ include Sidekiq::Job
63
+ $count = 0
64
+ $lock = Mutex.new
65
+
30
66
  sidekiq_options retry: 1
31
67
  sidekiq_retry_in do |x|
32
68
  1
33
69
  end
34
70
 
35
- def perform(idx, ts = nil)
71
+ def perform(string, idx, hash, ts = nil)
72
+ $lock.synchronize do
73
+ $count += 1
74
+ if $count % 100_000 == 0
75
+ logger.warn("#{Time.now} Done #{$count}")
76
+ end
77
+ end
36
78
  puts(Time.now.to_f - ts) if !ts.nil?
37
79
  # raise idx.to_s if idx % 100 == 1
38
80
  end
39
81
  end
40
82
 
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"
83
+ def Process.rss
84
+ `ps -o rss= -p #{Process.pid}`.chomp.to_i
60
85
  end
61
86
 
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>"
87
+ class Loader
88
+ def initialize
89
+ @iter = ENV["GC"] ? 10 : 500
90
+ @count = Integer(ENV["COUNT"] || 1_000)
91
+ @latency = Integer(ENV["LATENCY"] || 0)
92
+ end
93
+
94
+ def configure
95
+ @x = Sidekiq.configure_embed do |config|
96
+ config.redis = {db: 13, port: ((@latency > 0) ? 6380 : 6379)}
97
+ config.concurrency = Integer(ENV.fetch("THREADS", "10"))
98
+ # config.redis = { db: 13, port: 6380, driver: :hiredis}
99
+ config.queues = %w[default]
100
+ config.logger.level = Logger::WARN
101
+ config.average_scheduled_poll_interval = 2
102
+ config.reliable! if defined?(Sidekiq::Pro)
103
+ end
104
+
105
+ @self_read, @self_write = IO.pipe
106
+ %w[INT TERM TSTP TTIN].each do |sig|
107
+ trap sig do
108
+ @self_write.puts(sig)
83
109
  end
110
+ rescue ArgumentError
111
+ puts "Signal #{sig} not supported"
84
112
  end
85
113
  end
86
- end
87
114
 
88
- def Process.rss
89
- `ps -o rss= -p #{Process.pid}`.chomp.to_i
90
- end
115
+ def handle_signal(sig)
116
+ launcher = @x
117
+ Sidekiq.logger.debug "Got #{sig} signal"
118
+ case sig
119
+ when "INT"
120
+ # Handle Ctrl-C in JRuby like MRI
121
+ # http://jira.codehaus.org/browse/JRUBY-4637
122
+ raise Interrupt
123
+ when "TERM"
124
+ # Heroku sends TERM and then waits 30 seconds for process to exit.
125
+ raise Interrupt
126
+ when "TSTP"
127
+ Sidekiq.logger.info "Received TSTP, no longer accepting new work"
128
+ launcher.quiet
129
+ when "TTIN"
130
+ Thread.list.each do |thread|
131
+ Sidekiq.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread["label"]}"
132
+ if thread.backtrace
133
+ Sidekiq.logger.warn thread.backtrace.join("\n")
134
+ else
135
+ Sidekiq.logger.warn "<no backtrace available>"
136
+ end
137
+ end
138
+ end
139
+ end
91
140
 
92
- iter = 10
93
- count = 10_000
141
+ def setup
142
+ Sidekiq.logger.warn("Setup RSS: #{Process.rss}")
143
+ Sidekiq.redis { |c| c.flushdb }
144
+ start = Time.now
145
+ if ENV["AJ"]
146
+ @iter.times do
147
+ ActiveJob.perform_all_later(@count.times.map do |idx|
148
+ LoadJob.new("mike", idx, {mike: "bob"})
149
+ end)
150
+ end
151
+ else
152
+ @iter.times do
153
+ arr = Array.new(@count) { |idx| ["string", idx, {"mike" => "bob"}] }
154
+ Sidekiq::Client.push_bulk("class" => LoadWorker, "args" => arr)
155
+ end
156
+ end
157
+ Sidekiq.logger.warn "Created #{@count * @iter} jobs in #{Time.now - start} sec"
158
+ end
94
159
 
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"
160
+ def monitor
161
+ @monitor = Thread.new do
162
+ GC.start
163
+ loop do
164
+ sleep 0.2
165
+ qsize = Sidekiq.redis do |conn|
166
+ conn.llen "queue:default"
167
+ end
168
+ total = qsize
169
+ if total == 0
170
+ ending = Time.now - @start
171
+ size = @iter * @count
172
+ Sidekiq.logger.warn("Done, #{size} jobs in #{ending} sec, #{(size / ending).to_i} jobs/sec")
173
+ Sidekiq.logger.warn("Ending RSS: #{Process.rss}")
174
+ Sidekiq.logger.warn("Now here's the latency for three jobs")
100
175
 
101
- start = Time.now
176
+ if ENV["AJ"]
177
+ LoadJob.perform_later("", 1, {}, Time.now.to_f)
178
+ LoadJob.perform_later("", 2, {}, Time.now.to_f)
179
+ LoadJob.perform_later("", 3, {}, Time.now.to_f)
180
+ else
181
+ LoadWorker.perform_async("", 1, {}, Time.now.to_f)
182
+ LoadWorker.perform_async("", 2, {}, Time.now.to_f)
183
+ LoadWorker.perform_async("", 3, {}, Time.now.to_f)
184
+ end
102
185
 
103
- Monitoring = Thread.new do
104
- while true
105
- sleep 0.2
106
- qsize = Sidekiq.redis do |conn|
107
- conn.llen "queue:default"
186
+ sleep 0.1
187
+ @x.stop
188
+ Process.kill("INT", $$)
189
+ break
190
+ end
191
+ end
108
192
  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)
193
+ end
194
+
195
+ def with_latency(latency, &block)
196
+ Sidekiq.logger.warn "Simulating #{latency}ms of latency between Sidekiq and redis"
197
+ if latency > 0
198
+ Toxiproxy[:redis].downstream(:latency, latency: latency).apply(&block)
199
+ else
200
+ yield
121
201
  end
122
202
  end
123
- end
124
203
 
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
204
+ def run(name)
205
+ Sidekiq.logger.warn("Starting #{name}")
206
+ monitor
207
+
208
+ if ENV["PROFILE"]
209
+ $profile = RubyProf::Profile.new(exclude_threads: [@monitor])
210
+ $profile.start
211
+ elsif ENV["GC"]
212
+ GC.start
213
+ GC.compact
214
+ GC.disable
215
+ Sidekiq.logger.error("GC Start RSS: #{Process.rss}")
216
+ end
217
+ @start = Time.now
218
+ with_latency(@latency) do
219
+ @x.run
220
+
221
+ while (readable_io = IO.select([@self_read]))
222
+ signal = readable_io.first[0].gets.strip
223
+ handle_signal(signal)
224
+ end
225
+ end
226
+ # normal
227
+ rescue Interrupt
228
+ rescue => e
229
+ raise e if $DEBUG
230
+ warn e.message
231
+ warn e.backtrace.join("\n")
232
+ exit 1
233
+ ensure
234
+ @x.stop
131
235
  end
132
- 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)
237
+ def done
238
+ Sidekiq.logger.error("GC End RSS: #{Process.rss}") if ENV["GC"]
239
+ if ENV["PROFILE"]
240
+ Sidekiq.logger.error("Profiling...")
241
+ result = $profile.stop
242
+ printer = RubyProf::GraphHtmlPrinter.new(result)
243
+ printer.print(File.new("output.html", "w"), min_percent: 1)
148
244
  end
149
245
  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
161
246
  end
247
+
248
+ ll = Loader.new
249
+ ll.configure
250
+
251
+ if ENV["WARM"]
252
+ ll.setup
253
+ ll.run("warmup")
254
+ end
255
+
256
+ ll.setup
257
+ ll.run("load")
258
+ 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
 
data/bin/webload ADDED
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler'
4
+ Bundler.setup
5
+
6
+ # This skeleton allows you to run Sidekiq::Web page rendering
7
+ # through Vernier for tuning.
8
+ require "sidekiq/web"
9
+ require "rack/test"
10
+ require "vernier"
11
+
12
+ Sidekiq::Web.configure do |config|
13
+ config.middlewares.clear # remove csrf
14
+ end
15
+
16
+ class SomeJob
17
+ include Sidekiq::Job
18
+ end
19
+
20
+ class BenchWeb
21
+ include Rack::Test::Methods
22
+
23
+ def app
24
+ Sidekiq::Web.new
25
+ end
26
+
27
+ def warmup(page = "/scheduled")
28
+ # Sidekiq.redis {|c| c.flushdb }
29
+
30
+ # 100.times do |idx|
31
+ # SomeJob.perform_at(idx, 1, 3, "mike", {"foo" => "bar"})
32
+ # end
33
+
34
+ 100.times do
35
+ get page
36
+ end
37
+ end
38
+
39
+ def load(page = "/scheduled", count = 10_000)
40
+ profile do
41
+ count.times do
42
+ get page
43
+ raise last_response.inspect unless last_response.status == 200
44
+ end
45
+ end
46
+ end
47
+
48
+ def profile(&)
49
+ if ENV["PROF"]
50
+ Vernier.profile(out: "profile.json.gz", &)
51
+ else
52
+ yield
53
+ end
54
+ end
55
+ end
56
+
57
+ def timer(name="block", count = 10_000)
58
+ a = Time.now
59
+ yield count
60
+ b = Time.now
61
+ puts "#{name} in #{b - a} sec"
62
+ end
63
+
64
+ page = "/busy"
65
+ b = BenchWeb.new
66
+ b.warmup(page)
67
+ timer(page) do |count|
68
+ b.load(page, count)
69
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ gem "activejob", ">= 7.0"
5
+ require "active_job"
6
+
7
+ module Sidekiq
8
+ module ActiveJob
9
+ # @api private
10
+ class Wrapper
11
+ include Sidekiq::Job
12
+
13
+ def perform(job_data)
14
+ ::ActiveJob::Base.execute(job_data.merge("provider_job_id" => jid))
15
+ end
16
+ end
17
+ end
18
+ end
19
+
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
34
+
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
49
+
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
+ # NB: Active Job only serializes keys it recognizes. We
65
+ # cannot set arbitrary key/values here.
66
+ options = {wrapped: job.class, queue: job.queue_name}
67
+ options[:profile] = job.profile if job.respond_to?(:profile) && !job.profile.nil?
68
+
69
+ wrapper = Sidekiq::ActiveJob::Wrapper.set(options)
70
+ job.provider_job_id = wrapper.perform_async(job.serialize)
71
+ end
72
+
73
+ # @api private
74
+ def enqueue_at(job, timestamp)
75
+ options = {wrapped: job.class, queue: job.queue_name}
76
+ options[:profile] = job.profile if job.respond_to?(:profile) && !job.profile.nil?
77
+
78
+ job.provider_job_id = Sidekiq::ActiveJob::Wrapper.set(options).perform_at(timestamp, job.serialize)
79
+ end
80
+
81
+ # @api private
82
+ def enqueue_all(jobs)
83
+ enqueued_count = 0
84
+ jobs.group_by(&:class).each do |job_class, same_class_jobs|
85
+ same_class_jobs.group_by(&:queue_name).each do |queue, same_class_and_queue_jobs|
86
+ immediate_jobs, scheduled_jobs = same_class_and_queue_jobs.partition { |job| job.scheduled_at.nil? }
87
+
88
+ if immediate_jobs.any?
89
+ jids = Sidekiq::Client.push_bulk(
90
+ "class" => Sidekiq::ActiveJob::Wrapper,
91
+ "wrapped" => job_class,
92
+ "queue" => queue,
93
+ "args" => immediate_jobs.map { |job| [job.serialize] }
94
+ )
95
+ enqueued_count += jids.compact.size
96
+ end
97
+
98
+ if scheduled_jobs.any?
99
+ jids = Sidekiq::Client.push_bulk(
100
+ "class" => Sidekiq::ActiveJob::Wrapper,
101
+ "wrapped" => job_class,
102
+ "queue" => queue,
103
+ "args" => scheduled_jobs.map { |job| [job.serialize] },
104
+ "at" => scheduled_jobs.map { |job| job.scheduled_at&.to_f }
105
+ )
106
+ enqueued_count += jids.compact.size
107
+ end
108
+ end
109
+ end
110
+ enqueued_count
111
+ end
112
+
113
+ # @api private
114
+ def stopping?(job = nil) = !!@@stopping
115
+
116
+ # Defines a class alias for backwards compatibility with enqueued Active Job jobs.
117
+ # @api private
118
+ JobWrapper = Sidekiq::ActiveJob::Wrapper
119
+ end
120
+ end
121
+ end
122
+ rescue Gem::LoadError
123
+ # ActiveJob not available or version requirement not met
124
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rails/generators/named_base"
2
4
 
3
5
  module Sidekiq
@@ -12,7 +14,10 @@ module Sidekiq
12
14
  end
13
15
 
14
16
  def create_job_file
15
- template "job.rb.erb", File.join("app/sidekiq", class_path, "#{file_name}_job.rb")
17
+ template(
18
+ "job.rb.erb",
19
+ File.join(jobs_directory, class_path, "#{file_name}_job.rb")
20
+ )
16
21
  end
17
22
 
18
23
  def create_test_file
@@ -29,7 +34,8 @@ module Sidekiq
29
34
 
30
35
  def create_job_spec
31
36
  template_file = File.join(
32
- "spec/sidekiq",
37
+ "spec",
38
+ jobs_directory.gsub("app/", ""),
33
39
  class_path,
34
40
  "#{file_name}_job_spec.rb"
35
41
  )
@@ -38,7 +44,8 @@ module Sidekiq
38
44
 
39
45
  def create_job_test
40
46
  template_file = File.join(
41
- "test/sidekiq",
47
+ "test",
48
+ jobs_directory.gsub("app/", ""),
42
49
  class_path,
43
50
  "#{file_name}_job_test.rb"
44
51
  )
@@ -52,6 +59,13 @@ module Sidekiq
52
59
  def test_framework
53
60
  ::Rails.application.config.generators.options[:rails][:test_framework]
54
61
  end
62
+
63
+ # Can be set in an initializer or in application configuration
64
+ # with Rails.application.config.generators.options[:sidekiq][:jobs_directory] = "app/jobs"
65
+ # to change the directory that the job files are generated in to.
66
+ def jobs_directory
67
+ ::Rails.application.config.generators.options[:sidekiq].fetch(:jobs_directory, "app/sidekiq")
68
+ end
55
69
  end
56
70
  end
57
71
  end
@@ -6,4 +6,4 @@ class <%= class_name %>Job
6
6
  # Do something
7
7
  end
8
8
  end
9
- <% end -%>
9
+ <% end -%>