sidekiq 7.1.6 → 7.3.9

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +184 -0
  3. data/README.md +3 -3
  4. data/bin/multi_queue_bench +271 -0
  5. data/bin/sidekiqload +21 -12
  6. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +75 -0
  7. data/lib/generators/sidekiq/job_generator.rb +2 -0
  8. data/lib/sidekiq/api.rb +139 -44
  9. data/lib/sidekiq/capsule.rb +8 -3
  10. data/lib/sidekiq/cli.rb +4 -1
  11. data/lib/sidekiq/client.rb +21 -1
  12. data/lib/sidekiq/component.rb +22 -0
  13. data/lib/sidekiq/config.rb +31 -7
  14. data/lib/sidekiq/deploy.rb +4 -2
  15. data/lib/sidekiq/embedded.rb +2 -0
  16. data/lib/sidekiq/fetch.rb +1 -1
  17. data/lib/sidekiq/iterable_job.rb +55 -0
  18. data/lib/sidekiq/job/interrupt_handler.rb +24 -0
  19. data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
  20. data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
  21. data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
  22. data/lib/sidekiq/job/iterable.rb +294 -0
  23. data/lib/sidekiq/job.rb +14 -3
  24. data/lib/sidekiq/job_logger.rb +7 -6
  25. data/lib/sidekiq/job_retry.rb +9 -4
  26. data/lib/sidekiq/job_util.rb +2 -0
  27. data/lib/sidekiq/launcher.rb +7 -5
  28. data/lib/sidekiq/logger.rb +1 -1
  29. data/lib/sidekiq/metrics/query.rb +6 -1
  30. data/lib/sidekiq/metrics/shared.rb +15 -4
  31. data/lib/sidekiq/metrics/tracking.rb +20 -8
  32. data/lib/sidekiq/middleware/current_attributes.rb +46 -13
  33. data/lib/sidekiq/middleware/modules.rb +2 -0
  34. data/lib/sidekiq/monitor.rb +2 -1
  35. data/lib/sidekiq/paginator.rb +8 -2
  36. data/lib/sidekiq/processor.rb +21 -11
  37. data/lib/sidekiq/rails.rb +19 -3
  38. data/lib/sidekiq/redis_client_adapter.rb +24 -5
  39. data/lib/sidekiq/redis_connection.rb +36 -8
  40. data/lib/sidekiq/ring_buffer.rb +2 -0
  41. data/lib/sidekiq/scheduled.rb +2 -2
  42. data/lib/sidekiq/systemd.rb +2 -0
  43. data/lib/sidekiq/testing.rb +14 -8
  44. data/lib/sidekiq/transaction_aware_client.rb +7 -0
  45. data/lib/sidekiq/version.rb +5 -1
  46. data/lib/sidekiq/web/action.rb +26 -4
  47. data/lib/sidekiq/web/application.rb +53 -64
  48. data/lib/sidekiq/web/csrf_protection.rb +8 -5
  49. data/lib/sidekiq/web/helpers.rb +73 -27
  50. data/lib/sidekiq/web/router.rb +5 -2
  51. data/lib/sidekiq/web.rb +54 -2
  52. data/lib/sidekiq.rb +5 -3
  53. data/sidekiq.gemspec +3 -2
  54. data/web/assets/javascripts/application.js +26 -0
  55. data/web/assets/javascripts/dashboard-charts.js +37 -11
  56. data/web/assets/javascripts/dashboard.js +14 -10
  57. data/web/assets/javascripts/metrics.js +34 -0
  58. data/web/assets/stylesheets/application-rtl.css +10 -0
  59. data/web/assets/stylesheets/application.css +38 -3
  60. data/web/locales/en.yml +3 -1
  61. data/web/locales/fr.yml +0 -1
  62. data/web/locales/gd.yml +0 -1
  63. data/web/locales/it.yml +32 -1
  64. data/web/locales/ja.yml +0 -1
  65. data/web/locales/pt-br.yml +1 -2
  66. data/web/locales/tr.yml +100 -0
  67. data/web/locales/uk.yml +24 -1
  68. data/web/locales/zh-cn.yml +0 -1
  69. data/web/locales/zh-tw.yml +0 -1
  70. data/web/views/_footer.erb +12 -1
  71. data/web/views/_metrics_period_select.erb +1 -1
  72. data/web/views/_summary.erb +7 -7
  73. data/web/views/busy.erb +7 -7
  74. data/web/views/dashboard.erb +29 -36
  75. data/web/views/filtering.erb +4 -5
  76. data/web/views/layout.erb +6 -6
  77. data/web/views/metrics.erb +38 -30
  78. data/web/views/metrics_for_job.erb +29 -38
  79. data/web/views/morgue.erb +2 -2
  80. data/web/views/queue.erb +1 -1
  81. data/web/views/queues.erb +6 -2
  82. metadata +33 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b50ab03f32263dd24ee37ce2ddadc7a4e5e226860146c1a677262dd3a417329d
4
- data.tar.gz: 0af1051781796465d5ce5dd0d0e454016fdc04b7cb5fcf0b8d62e447faff11ed
3
+ metadata.gz: dccc402e8f4e309a47a2adb62418fa29989012e8c94f34aa11200b098dc375f2
4
+ data.tar.gz: 71b64a582d5971b045d60bae38dc26da265b60ff9a099ca3b2f9c7df9035e805
5
5
  SHA512:
6
- metadata.gz: f2e84e49ceb024e8cb24a7213ffe81e40ed9423d646e39778ed51049bf89a04c28890a21e93eeb38bb9daefe88b94b150d71a0cdf80ecdad712a2167dd0a3421
7
- data.tar.gz: 39ffc3e4eee3e2c27a3f774f2a2120c50a52ab9e2049b0fc925123c622553bf4fd19512c16e63882031909bba6ea5b2d15cf856256a3c453d080e89253196fc6
6
+ metadata.gz: d9148b613a222ca9617ceebe25bb04a5d086edacdafad4a426b6232662d3b583db462dc5e0491297f7859037c5166bcb541bddcd3949f6d3b5a1c2e3fc572b65
7
+ data.tar.gz: 93a797cefd1a68adb236c7538c28571467c328d6965af0bc5ce505a1391d6f5b6e2ae2c87b42110e837dcfa8d788aff55309806bbb1bcd181e205c3bc66adc29
data/Changes.md CHANGED
@@ -2,6 +2,185 @@
2
2
 
3
3
  [Sidekiq Changes](https://github.com/sidekiq/sidekiq/blob/main/Changes.md) | [Sidekiq Pro Changes](https://github.com/sidekiq/sidekiq/blob/main/Pro-Changes.md) | [Sidekiq Enterprise Changes](https://github.com/sidekiq/sidekiq/blob/main/Ent-Changes.md)
4
4
 
5
+ 7.3.9
6
+ ----------
7
+
8
+ - Only require activejob if necessary [#6584]
9
+ - Fix iterable job cancellation [#6589]
10
+ - Web UI accessibility improvements [#6604]
11
+
12
+ 7.3.8
13
+ ----------
14
+
15
+ - Fix dead tag links [#6554]
16
+ - Massive Web UI performance improvement, some pages up to 15x faster [#6555]
17
+
18
+ 7.3.7
19
+ ----------
20
+
21
+ - Backport `Sidekiq::Web.configure` for compatibility with 8.0 [#6532]
22
+ - Backport `url_params(key)` and `route_params(key)` for compatibility with 8.0 [#6532]
23
+ - Various fixes for UI filtering [#6508]
24
+ - Tune `inspect` for internal S::Components to keep size managable [#6553]
25
+
26
+ 7.3.6
27
+ ----------
28
+
29
+ - Forward compatibility fixes for Ruby 3.4
30
+ - Filtering in the Web UI now works via GET so you can bookmark a filtered view. [#6497]
31
+
32
+ 7.3.5
33
+ ----------
34
+
35
+ - Reimplement `retry_all` and `kill_all` API methods to use ZPOPMIN,
36
+ approximately 30-60% faster. [#6481]
37
+ - Add preload testing binary at `examples/testing/sidekiq_boot` to verify your Rails app boots correctly with Sidekiq Enterprise's app preloading.
38
+ - Fix circular require with ActiveJob adapter [#6477]
39
+ - Fix potential race condition leading to incorrect serialized values for CurrentAttributes [#6475]
40
+ - Restore missing elapsed time when default job logging is disabled
41
+
42
+ 7.3.4
43
+ ----------
44
+
45
+ - Fix FrozenError when starting Sidekiq [#6470]
46
+
47
+ 7.3.3
48
+ ----------
49
+
50
+ - Freeze global configuration once boot is complete, to avoid configuration race conditions [#6466, #6465]
51
+ - Sidekiq now warns if a job iteration takes longer than the `-t` timeout setting (defaults to 25 seconds)
52
+ - Iteration callbacks now have easy access to job arguments via the `arguments` method:
53
+ ```ruby
54
+ def on_stop
55
+ p arguments # => `[123, "string", {"key" => "value"}]`
56
+ id, str, hash = arguments
57
+ end
58
+ ```
59
+ - Iterable jobs can be cancelled via `Sidekiq::Client#cancel!`:
60
+ ```ruby
61
+ c = Sidekiq::Client.new
62
+ jid = c.push("class" => SomeJob, "args" => [123])
63
+ c.cancel!(jid) # => true
64
+ ```
65
+ - Take over support for ActiveJob's `:sidekiq` adapter [#6430, fatkodima]
66
+ - Ensure CurrentAttributes are in scope when creating batch callbacks [#6455]
67
+ - Add `Sidekiq.gem_version` API.
68
+ - Update Ukranian translations
69
+
70
+ 7.3.2
71
+ ----------
72
+
73
+ - Adjust ActiveRecord batch iteration to restart an interrupted batch from the beginning.
74
+ Each batch should be processed as a single transaction in order to be idempotent. [#6405]
75
+ - Fix typo in Sidekiq::DeadSet#kill [#6397]
76
+ - Fix CSS issue with bottom bar in Web UI [#6414]
77
+
78
+ 7.3.1
79
+ ----------
80
+
81
+ - Don't count job interruptions as failures in metrics [#6386]
82
+ - Add frozen string literal to a number of .rb files.
83
+ - Fix frozen string error with style_tag and script_tag [#6371]
84
+ - Fix an error on Ruby 2.7 because of usage of `Hash#except` [#6376]
85
+
86
+ 7.3.0
87
+ ----------
88
+
89
+ - **NEW FEATURE** Add `Sidekiq::IterableJob`, iteration support for long-running jobs. [#6286, fatkodima]
90
+ Iterable jobs are interruptible and can restart quickly if
91
+ running during a deploy. You must ensure that `each_iteration`
92
+ doesn't take more than Sidekiq's `-t` timeout (default: 25 seconds). Iterable jobs must not implement `perform`.
93
+ ```ruby
94
+ class ProcessArrayJob
95
+ include Sidekiq::IterableJob
96
+ def build_enumerator(*args, **kwargs)
97
+ array_enumerator(args, **kwargs)
98
+ end
99
+ def each_iteration(arg)
100
+ puts arg
101
+ end
102
+ end
103
+ ProcessArrayJob.perform_async(1, 2, 3)
104
+ ```
105
+ See the [Iteration](//github.com/sidekiq/sidekiq/wiki/Iteration) wiki page and the RDoc in `Sidekiq::IterableJob`.
106
+ This feature should be considered BETA until the next minor release.
107
+ - **SECURITY** The Web UI no longer allows extensions to use `<script>`.
108
+ Adjust CSP to disallow inline scripts within the Web UI. Please see
109
+ `examples/webui-ext` for how to register Web UI extensions and use
110
+ dynamic CSS and JS. This will make Sidekiq immune to XSS attacks. [#6270]
111
+ - Add config option, `:skip_default_job_logging` to disable Sidekiq's default
112
+ start/finish job logging. [#6200]
113
+ - Allow `Sidekiq::Limiter.redis` to use Redis Cluster [#6288]
114
+ - Retain CurrentAttributeѕ after inline execution [#6307]
115
+ - Ignore non-existent CurrentAttributes attributes when restoring [#6341]
116
+ - Raise default Redis {read,write,connect} timeouts from 1 to 3 seconds
117
+ to minimize ReadTimeoutErrors [#6162]
118
+ - Add `logger` as a dependency since it will become bundled in Ruby 3.5 [#6320]
119
+ - Ignore unsupported locales in the Web UI [#6313]
120
+
121
+ 7.2.4
122
+ ----------
123
+
124
+ - Fix XSS in metrics filtering introduced in 7.2.0, CVE-2024-32887
125
+ Thanks to @UmerAdeemCheema for the security report.
126
+
127
+ 7.2.3
128
+ ----------
129
+
130
+ - [Support Dragonfly.io](https://www.mikeperham.com/2024/02/01/supporting-dragonfly/) as an alternative Redis implementation
131
+ - Fix error unpacking some compressed error backtraces [#6241]
132
+ - Fix potential heartbeat data leak [#6227]
133
+ - Add ability to find a currently running work by jid [#6212, fatkodima]
134
+
135
+ 7.2.2
136
+ ----------
137
+
138
+ - Add `Process.warmup` call in Ruby 3.3+
139
+ - Batch jobs now skip transactional push [#6160]
140
+
141
+ 7.2.1
142
+ ----------
143
+
144
+ - Add `Sidekiq::Work` type which replaces the raw Hash as the third parameter in
145
+ `Sidekiq::WorkSet#each { |pid, tid, hash| ... }` [#6145]
146
+ - **DEPRECATED**: direct access to the attributes within the `hash` block parameter above.
147
+ The `Sidekiq::Work` instance contains accessor methods to get at the same data, e.g.
148
+ ```ruby
149
+ work["queue"] # Old
150
+ work.queue # New
151
+ ```
152
+ - Fix Ruby 3.3 warnings around `base64` gem [#6151, earlopain]
153
+
154
+ 7.2.0
155
+ ----------
156
+
157
+ - `sidekiq_retries_exhausted` can return `:discard` to avoid the deadset
158
+ and all death handlers [#6091]
159
+ - Metrics filtering by job class in Web UI [#5974]
160
+ - Better readability and formatting for numbers within the Web UI [#6080]
161
+ - Add explicit error if user code tries to nest test modes [#6078]
162
+ ```ruby
163
+ Sidekiq::Testing.inline! # global setting
164
+ Sidekiq::Testing.fake! do # override within block
165
+ # ok
166
+ Sidekiq::Testing.inline! do # can't override the override
167
+ # not ok, nested
168
+ end
169
+ end
170
+ ```
171
+ - **SECURITY** Forbid inline JavaScript execution in Web UI [#6074]
172
+ - Adjust redis-client adapter to avoid `method_missing` [#6083]
173
+ This can result in app code breaking if your app's Redis API usage was
174
+ depending on Sidekiq's adapter to correct invalid redis-client API usage.
175
+ One example:
176
+ ```ruby
177
+ # bad, not redis-client native
178
+ # Unsupported command argument type: TrueClass (TypeError)
179
+ Sidekiq.redis { |c| c.set("key", "value", nx: true, ex: 15) }
180
+ # good
181
+ Sidekiq.redis { |c| c.set("key", "value", "nx", "ex", 15) }
182
+ ```
183
+
5
184
  7.1.6
6
185
  ----------
7
186
 
@@ -150,6 +329,11 @@ end
150
329
  - Job Execution metrics!!!
151
330
  - See `docs/7.0-Upgrade.md` for release notes
152
331
 
332
+ 6.5.{10,11,12}
333
+ ----------
334
+
335
+ - Fixes for Rails 7.1 [#6067, #6070]
336
+
153
337
  6.5.9
154
338
  ----------
155
339
 
data/README.md CHANGED
@@ -14,11 +14,11 @@ Rails to make background processing dead simple.
14
14
  Requirements
15
15
  -----------------
16
16
 
17
- - Redis: 6.2+
17
+ - Redis: Redis 6.2+ or Dragonfly 1.13+
18
18
  - Ruby: MRI 2.7+ or JRuby 9.3+.
19
19
 
20
20
  Sidekiq 7.0 supports Rails 6.0+ but does not require it.
21
-
21
+ As of 7.2, Sidekiq supports Dragonfly as an alternative to Redis for data storage.
22
22
 
23
23
  Installation
24
24
  -----------------
@@ -86,7 +86,7 @@ Useful resources:
86
86
  * Occasional announcements are made to the [@sidekiq](https://ruby.social/@sidekiq) Mastodon account.
87
87
  * The [Sidekiq tag](https://stackoverflow.com/questions/tagged/sidekiq) on Stack Overflow has lots of useful Q &amp; A.
88
88
 
89
- Every Friday morning is Sidekiq office hour: I video chat and answer questions.
89
+ Every Thursday morning is Sidekiq office hour: I video chat and answer questions.
90
90
  See the [Sidekiq support page](https://sidekiq.org/support.html) for details.
91
91
 
92
92
  Contributing
@@ -0,0 +1,271 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # bin/bench is a helpful script to load test and
5
+ # performance tune Sidekiq's core. It's a configurable script,
6
+ # which accepts the following parameters as ENV variables.
7
+ #
8
+ # QUEUES
9
+ # Number of queues to consume from. Default is 8
10
+ #
11
+ # PROCESSES
12
+ # The number of processes this benchmark will create. Each process, consumes
13
+ # from one of the available queues. When processes are more than the number of
14
+ # queues, they are distributed to processes in round robin. Default is 8
15
+ #
16
+ # ELEMENTS
17
+ # Number of jobs to push to each queue. Default is 1000
18
+ #
19
+ # ITERATIONS
20
+ # Each queue pushes ITERATIONS times ELEMENTS jobs. Default is 1000
21
+ #
22
+ # PORT
23
+ # The port of the Dragonfly instance. Default is 6379
24
+ #
25
+ # IP
26
+ # The ip of the Dragonfly instance. Default is 127.0.0.1
27
+ #
28
+ # Example Usage:
29
+ #
30
+ # > RUBY_YJIT_ENABLE=1 THREADS=10 PROCESSES=8 QUEUES=8 bin/multi_queue_bench
31
+ #
32
+ # None of this script is considered a public API and may change over time.
33
+ #
34
+
35
+ # Quiet some warnings we see when running in warning mode:
36
+ # RUBYOPT=-w bundle exec sidekiq
37
+ $TESTING = false
38
+ puts RUBY_DESCRIPTION
39
+
40
+ require "bundler/setup"
41
+ Bundler.require(:default, :load_test)
42
+
43
+ class LoadWorker
44
+ include Sidekiq::Job
45
+ sidekiq_options retry: 1
46
+ sidekiq_retry_in do |x|
47
+ 1
48
+ end
49
+
50
+ def perform(idx, ts = nil)
51
+ puts(Time.now.to_f - ts) if !ts.nil?
52
+ # raise idx.to_s if idx % 100 == 1
53
+ end
54
+ end
55
+
56
+ def Process.rss
57
+ `ps -o rss= -p #{Process.pid}`.chomp.to_i
58
+ end
59
+
60
+ $iterations = ENV["ITERATIONS"] ? Integer(ENV["ITERATIONS"]) : 1_000
61
+ $elements = ENV["ELEMENTS"] ? Integer(ENV["ELEMENTS"]) : 1_000
62
+ $port = ENV["PORT"] ? Integer(ENV["PORT"]) : 6379
63
+ $ip = ENV["IP"] ? String(ENV["IP"]) : "127.0.0.1"
64
+
65
+ class Loader
66
+ def initialize
67
+ @iter = $iterations
68
+ @count = $elements
69
+ end
70
+
71
+ def configure(queue)
72
+ @x = Sidekiq.configure_embed do |config|
73
+ config.redis = {db: 0, host: $ip, port: $port}
74
+ config.concurrency = Integer(ENV.fetch("THREADS", "30"))
75
+ config.queues = queue
76
+ config.logger.level = Logger::WARN
77
+ config.average_scheduled_poll_interval = 2
78
+ config.reliable! if defined?(Sidekiq::Pro)
79
+ end
80
+
81
+ @self_read, @self_write = IO.pipe
82
+ %w[INT TERM TSTP TTIN].each do |sig|
83
+ trap sig do
84
+ @self_write.puts(sig)
85
+ end
86
+ rescue ArgumentError
87
+ puts "Signal #{sig} not supported"
88
+ end
89
+ end
90
+
91
+ def handle_signal(sig)
92
+ launcher = @x
93
+ Sidekiq.logger.debug "Got #{sig} signal"
94
+ case sig
95
+ when "INT"
96
+ # Handle Ctrl-C in JRuby like MRI
97
+ # http://jira.codehaus.org/browse/JRUBY-4637
98
+ raise Interrupt
99
+ when "TERM"
100
+ # Heroku sends TERM and then waits 30 seconds for process to exit.
101
+ raise Interrupt
102
+ when "TSTP"
103
+ Sidekiq.logger.info "Received TSTP, no longer accepting new work"
104
+ launcher.quiet
105
+ when "TTIN"
106
+ Thread.list.each do |thread|
107
+ Sidekiq.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)} #{thread["label"]}"
108
+ if thread.backtrace
109
+ Sidekiq.logger.warn thread.backtrace.join("\n")
110
+ else
111
+ Sidekiq.logger.warn "<no backtrace available>"
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ def setup(queue)
118
+ Sidekiq.logger.error("Setup RSS: #{Process.rss}")
119
+ Sidekiq.logger.error("Pushing work to queue: #{queue}")
120
+ start = Time.now
121
+ @iter.times do
122
+ arr = Array.new(@count) { |idx| [idx] }
123
+ # Sidekiq always prepends "queue:" to the queue name,
124
+ # that's why we pass 'q1', 'q2', etc instead of 'queue:q1'
125
+ Sidekiq::Client.push_bulk("class" => LoadWorker, "args" => arr, "queue" => queue)
126
+ $stdout.write "."
127
+ end
128
+ puts "Done"
129
+ end
130
+
131
+ def monitor_single(queue)
132
+ q = "queue:#{queue}"
133
+ @monitor_single = Thread.new do
134
+ GC.start
135
+ loop do
136
+ sleep 0.2
137
+ total = Sidekiq.redis do |conn|
138
+ conn.llen q
139
+ end
140
+
141
+ if total == 0
142
+ sleep 0.1
143
+ @x.stop
144
+ Process.kill("INT", $$)
145
+ break
146
+ end
147
+
148
+ end
149
+ end
150
+ end
151
+
152
+ def monitor_all(queues)
153
+ @monitor_all = Thread.new do
154
+ GC.start
155
+ loop do
156
+ sleep 0.2
157
+ qsize = 0
158
+ queues.each do |q|
159
+ tmp = Sidekiq.redis do |conn|
160
+ conn.llen q
161
+ end
162
+ qsize = qsize + tmp
163
+ end
164
+ total = qsize
165
+
166
+ if total == 0
167
+ ending = Time.now - @start
168
+ size = @iter * @count * queues.length()
169
+ Sidekiq.logger.error("Done, #{size} jobs in #{ending} sec, #{(size / ending).to_i} jobs/sec")
170
+ Sidekiq.logger.error("Ending RSS: #{Process.rss}")
171
+
172
+ sleep 0.1
173
+ @x.stop
174
+ Process.kill("INT", $$)
175
+ break
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ def run(queues, queue, monitor_all_queues)
182
+ Sidekiq.logger.warn("Consuming from #{queue}")
183
+ if monitor_all_queues
184
+ monitor_all(queues)
185
+ else
186
+ monitor_single(queue)
187
+ end
188
+
189
+ @start = Time.now
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
196
+ # normal
197
+ rescue Interrupt
198
+ rescue => e
199
+ raise e if $DEBUG
200
+ warn e.message
201
+ warn e.backtrace.join("\n")
202
+ exit 1
203
+ ensure
204
+ @x.stop
205
+ end
206
+ end
207
+
208
+ def setup(queue)
209
+ ll = Loader.new
210
+ ll.configure(queue)
211
+ ll.setup(queue)
212
+ end
213
+
214
+ def consume(queues, queue, monitor_all_queues)
215
+ ll = Loader.new
216
+ ll.configure(queue)
217
+ ll.run(queues, queue, monitor_all_queues)
218
+ end
219
+
220
+ # We assign one queue to each sidekiq process
221
+ def run(number_of_processes, total_queues)
222
+ read_stream, write_stream = IO.pipe
223
+
224
+ queues = []
225
+ (0..total_queues-1).each do |idx|
226
+ queues.push("queue:q#{idx}")
227
+ end
228
+
229
+ Sidekiq.logger.info("Queues are: #{queues}")
230
+
231
+ # Produce
232
+ start = Time.now
233
+ (0..total_queues-1).each do |idx|
234
+ Process.fork do
235
+ queue_num = "q#{idx}"
236
+ setup(queue_num)
237
+ end
238
+ end
239
+
240
+ queue_sz = $iterations * $elements * total_queues
241
+ Process.waitall
242
+
243
+ ending = Time.now - start
244
+ #Sidekiq.logger.info("Pushed #{queue_sz} in #{ending} secs")
245
+
246
+ # Consume
247
+ (0..number_of_processes-1).each do |idx|
248
+ Process.fork do
249
+ # First process only consumes from it's own queue but monitors all queues.
250
+ # It works as a synchronization point. Once all processes finish
251
+ # (that is, when all queues are emptied) it prints the the stats.
252
+ if idx == 0
253
+ queue = "q#{idx}"
254
+ consume(queues, queue, true)
255
+ else
256
+ queue = "q#{idx % total_queues}"
257
+ consume(queues, queue, false)
258
+ end
259
+ end
260
+ end
261
+
262
+ Process.waitall
263
+ write_stream.close
264
+ results = read_stream.read
265
+ read_stream.close
266
+ end
267
+
268
+ $total_processes = ENV["PROCESSES"] ? Integer(ENV["PROCESSES"]) : 8;
269
+ $total_queues = ENV["QUEUES"] ? Integer(ENV["QUEUES"]) : 8;
270
+
271
+ run($total_processes, $total_queues)
data/bin/sidekiqload CHANGED
@@ -50,7 +50,7 @@ if ENV["AJ"]
50
50
  ActiveJob::Base.logger.level = Logger::WARN
51
51
 
52
52
  class LoadJob < ActiveJob::Base
53
- def perform(idx, ts = nil)
53
+ def perform(string, idx, hash, ts = nil)
54
54
  puts(Time.now.to_f - ts) if !ts.nil?
55
55
  end
56
56
  end
@@ -58,12 +58,21 @@ end
58
58
 
59
59
  class LoadWorker
60
60
  include Sidekiq::Job
61
+ $count = 0
62
+ $lock = Mutex.new
63
+
61
64
  sidekiq_options retry: 1
62
65
  sidekiq_retry_in do |x|
63
66
  1
64
67
  end
65
68
 
66
- def perform(idx, ts = nil)
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}")
74
+ end
75
+ end
67
76
  puts(Time.now.to_f - ts) if !ts.nil?
68
77
  # raise idx.to_s if idx % 100 == 1
69
78
  end
@@ -133,13 +142,13 @@ class Loader
133
142
  start = Time.now
134
143
  if ENV["AJ"]
135
144
  @iter.times do
136
- @count.times do |idx|
137
- LoadJob.perform_later(idx)
138
- end
145
+ ActiveJob.perform_all_later(@count.times.map do |idx|
146
+ LoadJob.new("mike", idx, {mike: "bob"})
147
+ end)
139
148
  end
140
149
  else
141
150
  @iter.times do
142
- arr = Array.new(@count) { |idx| [idx] }
151
+ arr = Array.new(@count) { |idx| ["string", idx, {"mike" => "bob"}] }
143
152
  Sidekiq::Client.push_bulk("class" => LoadWorker, "args" => arr)
144
153
  end
145
154
  end
@@ -163,13 +172,13 @@ class Loader
163
172
  Sidekiq.logger.error("Now here's the latency for three jobs")
164
173
 
165
174
  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)
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)
169
178
  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)
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)
173
182
  end
174
183
 
175
184
  sleep 0.1
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJob
4
+ module QueueAdapters
5
+ # Explicitly remove the implementation existing in older rails'.
6
+ remove_const(:SidekiqAdapter) if const_defined?(:SidekiqAdapter)
7
+
8
+ # Sidekiq adapter for Active Job
9
+ #
10
+ # To use Sidekiq set the queue_adapter config to +:sidekiq+.
11
+ #
12
+ # Rails.application.config.active_job.queue_adapter = :sidekiq
13
+ class SidekiqAdapter
14
+ # Defines whether enqueuing should happen implicitly to after commit when called
15
+ # from inside a transaction.
16
+ # @api private
17
+ def enqueue_after_transaction_commit?
18
+ true
19
+ end
20
+
21
+ # @api private
22
+ def enqueue(job)
23
+ job.provider_job_id = JobWrapper.set(
24
+ wrapped: job.class,
25
+ queue: job.queue_name
26
+ ).perform_async(job.serialize)
27
+ end
28
+
29
+ # @api private
30
+ def enqueue_at(job, timestamp)
31
+ job.provider_job_id = JobWrapper.set(
32
+ wrapped: job.class,
33
+ queue: job.queue_name
34
+ ).perform_at(timestamp, job.serialize)
35
+ end
36
+
37
+ # @api private
38
+ def enqueue_all(jobs)
39
+ enqueued_count = 0
40
+ jobs.group_by(&:class).each do |job_class, same_class_jobs|
41
+ same_class_jobs.group_by(&:queue_name).each do |queue, same_class_and_queue_jobs|
42
+ immediate_jobs, scheduled_jobs = same_class_and_queue_jobs.partition { |job| job.scheduled_at.nil? }
43
+
44
+ if immediate_jobs.any?
45
+ jids = Sidekiq::Client.push_bulk(
46
+ "class" => JobWrapper,
47
+ "wrapped" => job_class,
48
+ "queue" => queue,
49
+ "args" => immediate_jobs.map { |job| [job.serialize] }
50
+ )
51
+ enqueued_count += jids.compact.size
52
+ end
53
+
54
+ if scheduled_jobs.any?
55
+ jids = Sidekiq::Client.push_bulk(
56
+ "class" => JobWrapper,
57
+ "wrapped" => job_class,
58
+ "queue" => queue,
59
+ "args" => scheduled_jobs.map { |job| [job.serialize] },
60
+ "at" => scheduled_jobs.map { |job| job.scheduled_at&.to_f }
61
+ )
62
+ enqueued_count += jids.compact.size
63
+ end
64
+ end
65
+ end
66
+ enqueued_count
67
+ end
68
+
69
+ # Defines a class alias for backwards compatibility with enqueued Active Job jobs.
70
+ # @api private
71
+ class JobWrapper < Sidekiq::ActiveJob::Wrapper
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "rails/generators/named_base"
2
4
 
3
5
  module Sidekiq