sidekiq 5.2.7 → 8.0.5

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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +845 -8
  3. data/LICENSE.txt +9 -0
  4. data/README.md +54 -54
  5. data/bin/multi_queue_bench +271 -0
  6. data/bin/sidekiq +22 -3
  7. data/bin/sidekiqload +219 -112
  8. data/bin/sidekiqmon +11 -0
  9. data/bin/webload +69 -0
  10. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +120 -0
  11. data/lib/generators/sidekiq/job_generator.rb +59 -0
  12. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  13. data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
  14. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  15. data/lib/sidekiq/api.rb +757 -373
  16. data/lib/sidekiq/capsule.rb +132 -0
  17. data/lib/sidekiq/cli.rb +210 -233
  18. data/lib/sidekiq/client.rb +145 -103
  19. data/lib/sidekiq/component.rb +128 -0
  20. data/lib/sidekiq/config.rb +315 -0
  21. data/lib/sidekiq/deploy.rb +64 -0
  22. data/lib/sidekiq/embedded.rb +64 -0
  23. data/lib/sidekiq/fetch.rb +49 -42
  24. data/lib/sidekiq/iterable_job.rb +56 -0
  25. data/lib/sidekiq/job/interrupt_handler.rb +24 -0
  26. data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
  27. data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
  28. data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
  29. data/lib/sidekiq/job/iterable.rb +306 -0
  30. data/lib/sidekiq/job.rb +385 -0
  31. data/lib/sidekiq/job_logger.rb +34 -7
  32. data/lib/sidekiq/job_retry.rb +164 -109
  33. data/lib/sidekiq/job_util.rb +113 -0
  34. data/lib/sidekiq/launcher.rb +208 -107
  35. data/lib/sidekiq/logger.rb +80 -0
  36. data/lib/sidekiq/manager.rb +42 -46
  37. data/lib/sidekiq/metrics/query.rb +184 -0
  38. data/lib/sidekiq/metrics/shared.rb +109 -0
  39. data/lib/sidekiq/metrics/tracking.rb +150 -0
  40. data/lib/sidekiq/middleware/chain.rb +113 -56
  41. data/lib/sidekiq/middleware/current_attributes.rb +119 -0
  42. data/lib/sidekiq/middleware/i18n.rb +7 -7
  43. data/lib/sidekiq/middleware/modules.rb +23 -0
  44. data/lib/sidekiq/monitor.rb +147 -0
  45. data/lib/sidekiq/paginator.rb +41 -16
  46. data/lib/sidekiq/processor.rb +146 -127
  47. data/lib/sidekiq/profiler.rb +72 -0
  48. data/lib/sidekiq/rails.rb +46 -43
  49. data/lib/sidekiq/redis_client_adapter.rb +113 -0
  50. data/lib/sidekiq/redis_connection.rb +79 -108
  51. data/lib/sidekiq/ring_buffer.rb +31 -0
  52. data/lib/sidekiq/scheduled.rb +112 -50
  53. data/lib/sidekiq/sd_notify.rb +149 -0
  54. data/lib/sidekiq/systemd.rb +26 -0
  55. data/lib/sidekiq/testing/inline.rb +6 -5
  56. data/lib/sidekiq/testing.rb +91 -90
  57. data/lib/sidekiq/transaction_aware_client.rb +51 -0
  58. data/lib/sidekiq/version.rb +7 -1
  59. data/lib/sidekiq/web/action.rb +125 -60
  60. data/lib/sidekiq/web/application.rb +363 -259
  61. data/lib/sidekiq/web/config.rb +120 -0
  62. data/lib/sidekiq/web/csrf_protection.rb +183 -0
  63. data/lib/sidekiq/web/helpers.rb +241 -120
  64. data/lib/sidekiq/web/router.rb +62 -71
  65. data/lib/sidekiq/web.rb +69 -161
  66. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  67. data/lib/sidekiq.rb +94 -182
  68. data/sidekiq.gemspec +26 -16
  69. data/web/assets/images/apple-touch-icon.png +0 -0
  70. data/web/assets/javascripts/application.js +150 -61
  71. data/web/assets/javascripts/base-charts.js +120 -0
  72. data/web/assets/javascripts/chart.min.js +13 -0
  73. data/web/assets/javascripts/chartjs-adapter-date-fns.min.js +7 -0
  74. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  75. data/web/assets/javascripts/dashboard-charts.js +194 -0
  76. data/web/assets/javascripts/dashboard.js +41 -293
  77. data/web/assets/javascripts/metrics.js +280 -0
  78. data/web/assets/stylesheets/style.css +766 -0
  79. data/web/locales/ar.yml +72 -65
  80. data/web/locales/cs.yml +63 -62
  81. data/web/locales/da.yml +61 -53
  82. data/web/locales/de.yml +66 -53
  83. data/web/locales/el.yml +44 -24
  84. data/web/locales/en.yml +94 -66
  85. data/web/locales/es.yml +92 -54
  86. data/web/locales/fa.yml +66 -65
  87. data/web/locales/fr.yml +83 -62
  88. data/web/locales/gd.yml +99 -0
  89. data/web/locales/he.yml +66 -64
  90. data/web/locales/hi.yml +60 -59
  91. data/web/locales/it.yml +93 -54
  92. data/web/locales/ja.yml +75 -64
  93. data/web/locales/ko.yml +53 -52
  94. data/web/locales/lt.yml +84 -0
  95. data/web/locales/nb.yml +62 -61
  96. data/web/locales/nl.yml +53 -52
  97. data/web/locales/pl.yml +46 -45
  98. data/web/locales/{pt-br.yml → pt-BR.yml} +84 -56
  99. data/web/locales/pt.yml +52 -51
  100. data/web/locales/ru.yml +69 -63
  101. data/web/locales/sv.yml +54 -53
  102. data/web/locales/ta.yml +61 -60
  103. data/web/locales/tr.yml +101 -0
  104. data/web/locales/uk.yml +86 -61
  105. data/web/locales/ur.yml +65 -64
  106. data/web/locales/vi.yml +84 -0
  107. data/web/locales/zh-CN.yml +106 -0
  108. data/web/locales/{zh-tw.yml → zh-TW.yml} +43 -9
  109. data/web/views/_footer.erb +31 -19
  110. data/web/views/_job_info.erb +94 -75
  111. data/web/views/_metrics_period_select.erb +15 -0
  112. data/web/views/_nav.erb +14 -21
  113. data/web/views/_paging.erb +23 -19
  114. data/web/views/_poll_link.erb +3 -6
  115. data/web/views/_summary.erb +23 -23
  116. data/web/views/busy.erb +139 -87
  117. data/web/views/dashboard.erb +82 -53
  118. data/web/views/dead.erb +31 -27
  119. data/web/views/filtering.erb +6 -0
  120. data/web/views/layout.erb +15 -29
  121. data/web/views/metrics.erb +84 -0
  122. data/web/views/metrics_for_job.erb +58 -0
  123. data/web/views/morgue.erb +60 -70
  124. data/web/views/profiles.erb +43 -0
  125. data/web/views/queue.erb +50 -39
  126. data/web/views/queues.erb +45 -29
  127. data/web/views/retries.erb +65 -75
  128. data/web/views/retry.erb +32 -27
  129. data/web/views/scheduled.erb +58 -52
  130. data/web/views/scheduled_job_info.erb +1 -1
  131. metadata +96 -76
  132. data/.circleci/config.yml +0 -61
  133. data/.github/contributing.md +0 -32
  134. data/.github/issue_template.md +0 -11
  135. data/.gitignore +0 -15
  136. data/.travis.yml +0 -11
  137. data/3.0-Upgrade.md +0 -70
  138. data/4.0-Upgrade.md +0 -53
  139. data/5.0-Upgrade.md +0 -56
  140. data/COMM-LICENSE +0 -97
  141. data/Ent-Changes.md +0 -238
  142. data/Gemfile +0 -23
  143. data/LICENSE +0 -9
  144. data/Pro-2.0-Upgrade.md +0 -138
  145. data/Pro-3.0-Upgrade.md +0 -44
  146. data/Pro-4.0-Upgrade.md +0 -35
  147. data/Pro-Changes.md +0 -759
  148. data/Rakefile +0 -9
  149. data/bin/sidekiqctl +0 -20
  150. data/code_of_conduct.md +0 -50
  151. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  152. data/lib/sidekiq/core_ext.rb +0 -1
  153. data/lib/sidekiq/ctl.rb +0 -221
  154. data/lib/sidekiq/delay.rb +0 -42
  155. data/lib/sidekiq/exception_handler.rb +0 -29
  156. data/lib/sidekiq/extensions/action_mailer.rb +0 -57
  157. data/lib/sidekiq/extensions/active_record.rb +0 -40
  158. data/lib/sidekiq/extensions/class_methods.rb +0 -40
  159. data/lib/sidekiq/extensions/generic_proxy.rb +0 -31
  160. data/lib/sidekiq/logging.rb +0 -122
  161. data/lib/sidekiq/middleware/server/active_record.rb +0 -23
  162. data/lib/sidekiq/util.rb +0 -66
  163. data/lib/sidekiq/worker.rb +0 -220
  164. data/web/assets/stylesheets/application-rtl.css +0 -246
  165. data/web/assets/stylesheets/application.css +0 -1144
  166. data/web/assets/stylesheets/bootstrap-rtl.min.css +0 -9
  167. data/web/assets/stylesheets/bootstrap.css +0 -5
  168. data/web/locales/zh-cn.yml +0 -68
  169. data/web/views/_status.erb +0 -4
data/bin/sidekiqload CHANGED
@@ -1,84 +1,80 @@
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'
8
- Bundler.require(:default)
27
+ require "ruby-prof" if ENV["PROFILE"]
28
+ require "bundler/setup"
29
+ Bundler.require(:default, :load_test)
9
30
 
10
- require_relative '../lib/sidekiq/cli'
11
- require_relative '../lib/sidekiq/launcher'
31
+ latency = Integer(ENV["LATENCY"] || 0)
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
+ }])
44
+ end
12
45
 
13
- include Sidekiq::Util
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
14
51
 
15
- Sidekiq.configure_server do |config|
16
- #config.options[:concurrency] = 1
17
- config.redis = { db: 13 }
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)
52
+ class LoadJob < ActiveJob::Base
53
+ def perform(string, idx, hash, ts = nil)
54
+ puts(Time.now.to_f - ts) if !ts.nil?
55
+ end
56
+ end
22
57
  end
23
58
 
24
59
  class LoadWorker
25
- include Sidekiq::Worker
60
+ include Sidekiq::Job
61
+ $count = 0
62
+ $lock = Mutex.new
63
+
26
64
  sidekiq_options retry: 1
27
65
  sidekiq_retry_in do |x|
28
66
  1
29
67
  end
30
68
 
31
- def perform(idx)
32
- #raise idx.to_s if idx % 100 == 1
33
- end
34
- end
35
-
36
- # brew tap shopify/shopify
37
- # brew install toxiproxy
38
- # gem install toxiproxy
39
- #require 'toxiproxy'
40
- # simulate a non-localhost network for realer-world conditions.
41
- # adding 1ms of network latency has an ENORMOUS impact on benchmarks
42
- #Toxiproxy.populate([{
43
- #"name": "redis",
44
- #"listen": "127.0.0.1:6380",
45
- #"upstream": "127.0.0.1:6379"
46
- #}])
47
-
48
- self_read, self_write = IO.pipe
49
- %w(INT TERM TSTP TTIN).each do |sig|
50
- begin
51
- trap sig do
52
- self_write.puts(sig)
53
- end
54
- rescue ArgumentError
55
- puts "Signal #{sig} not supported"
56
- end
57
- end
58
-
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>"
69
+ def perform(string, idx, hash, ts = nil)
70
+ $lock.synchronize do
71
+ $count += 1
72
+ if $count % 100_000 == 0
73
+ logger.warn("#{Time.now} Done #{$count}")
80
74
  end
81
75
  end
76
+ puts(Time.now.to_f - ts) if !ts.nil?
77
+ # raise idx.to_s if idx % 100 == 1
82
78
  end
83
79
  end
84
80
 
@@ -86,64 +82,175 @@ def Process.rss
86
82
  `ps -o rss= -p #{Process.pid}`.chomp.to_i
87
83
  end
88
84
 
89
- iter = 10
90
- count = 10_000
85
+ class Loader
86
+ def initialize
87
+ @iter = ENV["GC"] ? 10 : 500
88
+ @count = Integer(ENV["COUNT"] || 1_000)
89
+ @latency = Integer(ENV["LATENCY"] || 0)
90
+ end
91
91
 
92
- iter.times do
93
- arr = Array.new(count) do
94
- []
92
+ def configure
93
+ @x = Sidekiq.configure_embed do |config|
94
+ config.redis = {db: 13, port: ((@latency > 0) ? 6380 : 6379)}
95
+ config.concurrency = Integer(ENV.fetch("THREADS", "10"))
96
+ # config.redis = { db: 13, port: 6380, driver: :hiredis}
97
+ config.queues = %w[default]
98
+ config.logger.level = Logger::WARN
99
+ config.average_scheduled_poll_interval = 2
100
+ config.reliable! if defined?(Sidekiq::Pro)
101
+ end
102
+
103
+ @self_read, @self_write = IO.pipe
104
+ %w[INT TERM TSTP TTIN].each do |sig|
105
+ trap sig do
106
+ @self_write.puts(sig)
107
+ end
108
+ rescue ArgumentError
109
+ puts "Signal #{sig} not supported"
110
+ end
95
111
  end
96
- count.times do |idx|
97
- arr[idx][0] = idx
112
+
113
+ def handle_signal(sig)
114
+ launcher = @x
115
+ Sidekiq.logger.debug "Got #{sig} signal"
116
+ case sig
117
+ when "INT"
118
+ # Handle Ctrl-C in JRuby like MRI
119
+ # http://jira.codehaus.org/browse/JRUBY-4637
120
+ raise Interrupt
121
+ when "TERM"
122
+ # Heroku sends TERM and then waits 30 seconds for process to exit.
123
+ raise Interrupt
124
+ when "TSTP"
125
+ Sidekiq.logger.info "Received TSTP, no longer accepting new work"
126
+ launcher.quiet
127
+ when "TTIN"
128
+ Thread.list.each do |thread|
129
+ Sidekiq.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread["label"]}"
130
+ if thread.backtrace
131
+ Sidekiq.logger.warn thread.backtrace.join("\n")
132
+ else
133
+ Sidekiq.logger.warn "<no backtrace available>"
134
+ end
135
+ end
136
+ end
98
137
  end
99
- Sidekiq::Client.push_bulk('class' => LoadWorker, 'args' => arr)
100
- end
101
- Sidekiq.logger.error "Created #{count*iter} jobs"
102
-
103
- Monitoring = Thread.new do
104
- watchdog("monitor thread") do
105
- while true
106
- sleep 1
107
- qsize, retries = Sidekiq.redis do |conn|
108
- conn.pipelined do
138
+
139
+ def setup
140
+ Sidekiq.logger.warn("Setup RSS: #{Process.rss}")
141
+ Sidekiq.redis { |c| c.flushdb }
142
+ start = Time.now
143
+ if ENV["AJ"]
144
+ @iter.times do
145
+ ActiveJob.perform_all_later(@count.times.map do |idx|
146
+ LoadJob.new("mike", idx, {mike: "bob"})
147
+ end)
148
+ end
149
+ else
150
+ @iter.times do
151
+ arr = Array.new(@count) { |idx| ["string", idx, {"mike" => "bob"}] }
152
+ Sidekiq::Client.push_bulk("class" => LoadWorker, "args" => arr)
153
+ end
154
+ end
155
+ Sidekiq.logger.warn "Created #{@count * @iter} jobs in #{Time.now - start} sec"
156
+ end
157
+
158
+ def monitor
159
+ @monitor = Thread.new do
160
+ GC.start
161
+ loop do
162
+ sleep 0.2
163
+ qsize = Sidekiq.redis do |conn|
109
164
  conn.llen "queue:default"
110
- conn.zcard "retry"
111
165
  end
112
- end.map(&:to_i)
113
- total = qsize + retries
114
- #GC.start
115
- Sidekiq.logger.error("RSS: #{Process.rss} Pending: #{total}")
116
- if total == 0
117
- Sidekiq.logger.error("Done")
118
- exit(0)
166
+ total = qsize
167
+ if total == 0
168
+ ending = Time.now - @start
169
+ size = @iter * @count
170
+ Sidekiq.logger.warn("Done, #{size} jobs in #{ending} sec, #{(size / ending).to_i} jobs/sec")
171
+ Sidekiq.logger.warn("Ending RSS: #{Process.rss}")
172
+ Sidekiq.logger.warn("Now here's the latency for three jobs")
173
+
174
+ if ENV["AJ"]
175
+ LoadJob.perform_later("", 1, {}, Time.now.to_f)
176
+ LoadJob.perform_later("", 2, {}, Time.now.to_f)
177
+ LoadJob.perform_later("", 3, {}, Time.now.to_f)
178
+ else
179
+ LoadWorker.perform_async("", 1, {}, Time.now.to_f)
180
+ LoadWorker.perform_async("", 2, {}, Time.now.to_f)
181
+ LoadWorker.perform_async("", 3, {}, Time.now.to_f)
182
+ end
183
+
184
+ sleep 0.1
185
+ @x.stop
186
+ Process.kill("INT", $$)
187
+ break
188
+ end
119
189
  end
120
190
  end
121
191
  end
122
- end
123
192
 
124
- begin
125
- #RubyProf::exclude_threads = [ Monitoring ]
126
- #RubyProf.start
127
- fire_event(:startup)
128
- #Sidekiq.logger.error "Simulating 1ms of latency between Sidekiq and redis"
129
- #Toxiproxy[:redis].downstream(:latency, latency: 1).apply do
130
- launcher = Sidekiq::Launcher.new(Sidekiq.options)
131
- launcher.run
132
-
133
- while readable_io = IO.select([self_read])
134
- signal = readable_io.first[0].gets.strip
135
- handle_signal(launcher, signal)
193
+ def with_latency(latency, &block)
194
+ Sidekiq.logger.warn "Simulating #{latency}ms of latency between Sidekiq and redis"
195
+ if latency > 0
196
+ Toxiproxy[:redis].downstream(:latency, latency: latency).apply(&block)
197
+ else
198
+ yield
199
+ end
200
+ end
201
+
202
+ def run(name)
203
+ Sidekiq.logger.warn("Starting #{name}")
204
+ monitor
205
+
206
+ if ENV["PROFILE"]
207
+ $profile = RubyProf::Profile.new(exclude_threads: [@monitor])
208
+ $profile.start
209
+ elsif ENV["GC"]
210
+ GC.start
211
+ GC.compact
212
+ GC.disable
213
+ Sidekiq.logger.error("GC Start RSS: #{Process.rss}")
136
214
  end
137
- #end
138
- rescue SystemExit => e
139
- #Sidekiq.logger.error("Profiling...")
140
- #result = RubyProf.stop
141
- #printer = RubyProf::GraphHtmlPrinter.new(result)
142
- #printer.print(File.new("output.html", "w"), :min_percent => 1)
143
- # normal
144
- rescue => e
145
- raise e if $DEBUG
146
- STDERR.puts e.message
147
- STDERR.puts e.backtrace.join("\n")
148
- exit 1
215
+ @start = Time.now
216
+ with_latency(@latency) do
217
+ @x.run
218
+
219
+ while (readable_io = IO.select([@self_read]))
220
+ signal = readable_io.first[0].gets.strip
221
+ handle_signal(signal)
222
+ end
223
+ end
224
+ # normal
225
+ rescue Interrupt
226
+ rescue => e
227
+ raise e if $DEBUG
228
+ warn e.message
229
+ warn e.backtrace.join("\n")
230
+ exit 1
231
+ ensure
232
+ @x.stop
233
+ end
234
+
235
+ def done
236
+ Sidekiq.logger.error("GC End RSS: #{Process.rss}") if ENV["GC"]
237
+ if ENV["PROFILE"]
238
+ Sidekiq.logger.error("Profiling...")
239
+ result = $profile.stop
240
+ printer = RubyProf::GraphHtmlPrinter.new(result)
241
+ printer.print(File.new("output.html", "w"), min_percent: 1)
242
+ end
243
+ end
149
244
  end
245
+
246
+ ll = Loader.new
247
+ ll.configure
248
+
249
+ if ENV["WARM"]
250
+ ll.setup
251
+ ll.run("warmup")
252
+ end
253
+
254
+ ll.setup
255
+ ll.run("load")
256
+ ll.done
data/bin/sidekiqmon ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "sidekiq/monitor"
4
+
5
+ # disable the Redis connection pool logging
6
+ Sidekiq.default_configuration.logger.level = :warn
7
+
8
+ section = "all"
9
+ section = ARGV[0] if ARGV.size == 1
10
+
11
+ Sidekiq::Monitor::Status.new.display(section)
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,120 @@
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
+ class SidekiqAdapter < AbstractAdapter
47
+ @@stopping = false
48
+
49
+ callback = -> { @@stopping = true }
50
+
51
+ Sidekiq.configure_client { |config| config.on(:quiet, &callback) }
52
+ Sidekiq.configure_server { |config| config.on(:quiet, &callback) }
53
+
54
+ # Defines whether enqueuing should happen implicitly to after commit when called
55
+ # from inside a transaction.
56
+ # @api private
57
+ def enqueue_after_transaction_commit?
58
+ true
59
+ end
60
+
61
+ # @api private
62
+ def enqueue(job)
63
+ job.provider_job_id = Sidekiq::ActiveJob::Wrapper.set(
64
+ wrapped: job.class,
65
+ queue: job.queue_name
66
+ ).perform_async(job.serialize)
67
+ end
68
+
69
+ # @api private
70
+ def enqueue_at(job, timestamp)
71
+ job.provider_job_id = Sidekiq::ActiveJob::Wrapper.set(
72
+ wrapped: job.class,
73
+ queue: job.queue_name
74
+ ).perform_at(timestamp, job.serialize)
75
+ end
76
+
77
+ # @api private
78
+ def enqueue_all(jobs)
79
+ enqueued_count = 0
80
+ jobs.group_by(&:class).each do |job_class, same_class_jobs|
81
+ same_class_jobs.group_by(&:queue_name).each do |queue, same_class_and_queue_jobs|
82
+ immediate_jobs, scheduled_jobs = same_class_and_queue_jobs.partition { |job| job.scheduled_at.nil? }
83
+
84
+ if immediate_jobs.any?
85
+ jids = Sidekiq::Client.push_bulk(
86
+ "class" => Sidekiq::ActiveJob::Wrapper,
87
+ "wrapped" => job_class,
88
+ "queue" => queue,
89
+ "args" => immediate_jobs.map { |job| [job.serialize] }
90
+ )
91
+ enqueued_count += jids.compact.size
92
+ end
93
+
94
+ if scheduled_jobs.any?
95
+ jids = Sidekiq::Client.push_bulk(
96
+ "class" => Sidekiq::ActiveJob::Wrapper,
97
+ "wrapped" => job_class,
98
+ "queue" => queue,
99
+ "args" => scheduled_jobs.map { |job| [job.serialize] },
100
+ "at" => scheduled_jobs.map { |job| job.scheduled_at&.to_f }
101
+ )
102
+ enqueued_count += jids.compact.size
103
+ end
104
+ end
105
+ end
106
+ enqueued_count
107
+ end
108
+
109
+ # @api private
110
+ def stopping? = !!@@stopping
111
+
112
+ # Defines a class alias for backwards compatibility with enqueued Active Job jobs.
113
+ # @api private
114
+ JobWrapper = Sidekiq::ActiveJob::Wrapper
115
+ end
116
+ end
117
+ end
118
+ rescue Gem::LoadError
119
+ # ActiveJob not available or version requirement not met
120
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/named_base"
4
+
5
+ module Sidekiq
6
+ module Generators # :nodoc:
7
+ class JobGenerator < ::Rails::Generators::NamedBase # :nodoc:
8
+ desc "This generator creates a Sidekiq Job in app/sidekiq and a corresponding test"
9
+
10
+ check_class_collision suffix: "Job"
11
+
12
+ def self.default_generator_root
13
+ File.dirname(__FILE__)
14
+ end
15
+
16
+ def create_job_file
17
+ template "job.rb.erb", File.join("app/sidekiq", class_path, "#{file_name}_job.rb")
18
+ end
19
+
20
+ def create_test_file
21
+ return unless test_framework
22
+
23
+ if test_framework == :rspec
24
+ create_job_spec
25
+ else
26
+ create_job_test
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def create_job_spec
33
+ template_file = File.join(
34
+ "spec/sidekiq",
35
+ class_path,
36
+ "#{file_name}_job_spec.rb"
37
+ )
38
+ template "job_spec.rb.erb", template_file
39
+ end
40
+
41
+ def create_job_test
42
+ template_file = File.join(
43
+ "test/sidekiq",
44
+ class_path,
45
+ "#{file_name}_job_test.rb"
46
+ )
47
+ template "job_test.rb.erb", template_file
48
+ end
49
+
50
+ def file_name
51
+ @_file_name ||= super.sub(/_?job\z/i, "")
52
+ end
53
+
54
+ def test_framework
55
+ ::Rails.application.config.generators.options[:rails][:test_framework]
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,6 +1,6 @@
1
1
  <% module_namespacing do -%>
2
- class <%= class_name %>Worker
3
- include Sidekiq::Worker
2
+ class <%= class_name %>Job
3
+ include Sidekiq::Job
4
4
 
5
5
  def perform(*args)
6
6
  # Do something
@@ -1,6 +1,6 @@
1
1
  require 'rails_helper'
2
2
  <% module_namespacing do -%>
3
- RSpec.describe <%= class_name %>Worker, type: :worker do
3
+ RSpec.describe <%= class_name %>Job, type: :job do
4
4
  pending "add some examples to (or delete) #{__FILE__}"
5
5
  end
6
6
  <% end -%>
@@ -1,6 +1,6 @@
1
1
  require 'test_helper'
2
2
  <% module_namespacing do -%>
3
- class <%= class_name %>WorkerTest < <% if defined? Minitest::Test %>Minitest::Test<% else %>MiniTest::Unit::TestCase<% end %>
3
+ class <%= class_name %>JobTest < Minitest::Test
4
4
  def test_example
5
5
  skip "add some examples to (or delete) #{__FILE__}"
6
6
  end