sidekiq 7.0.9 → 7.3.10

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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +263 -0
  3. data/README.md +5 -5
  4. data/bin/multi_queue_bench +271 -0
  5. data/bin/sidekiqload +41 -14
  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 +170 -52
  9. data/lib/sidekiq/capsule.rb +8 -3
  10. data/lib/sidekiq/cli.rb +6 -2
  11. data/lib/sidekiq/client.rb +58 -22
  12. data/lib/sidekiq/component.rb +23 -1
  13. data/lib/sidekiq/config.rb +52 -11
  14. data/lib/sidekiq/deploy.rb +4 -2
  15. data/lib/sidekiq/embedded.rb +2 -0
  16. data/lib/sidekiq/fetch.rb +2 -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 +15 -8
  24. data/lib/sidekiq/job_logger.rb +7 -6
  25. data/lib/sidekiq/job_retry.rb +30 -8
  26. data/lib/sidekiq/job_util.rb +6 -2
  27. data/lib/sidekiq/launcher.rb +8 -6
  28. data/lib/sidekiq/logger.rb +1 -1
  29. data/lib/sidekiq/metrics/query.rb +6 -1
  30. data/lib/sidekiq/metrics/shared.rb +16 -5
  31. data/lib/sidekiq/metrics/tracking.rb +20 -8
  32. data/lib/sidekiq/middleware/current_attributes.rb +88 -16
  33. data/lib/sidekiq/middleware/i18n.rb +2 -0
  34. data/lib/sidekiq/middleware/modules.rb +2 -0
  35. data/lib/sidekiq/monitor.rb +2 -1
  36. data/lib/sidekiq/paginator.rb +8 -2
  37. data/lib/sidekiq/processor.rb +44 -33
  38. data/lib/sidekiq/rails.rb +28 -7
  39. data/lib/sidekiq/redis_client_adapter.rb +30 -31
  40. data/lib/sidekiq/redis_connection.rb +49 -9
  41. data/lib/sidekiq/ring_buffer.rb +3 -0
  42. data/lib/sidekiq/scheduled.rb +3 -3
  43. data/lib/sidekiq/systemd.rb +2 -0
  44. data/lib/sidekiq/testing.rb +32 -13
  45. data/lib/sidekiq/transaction_aware_client.rb +20 -5
  46. data/lib/sidekiq/version.rb +5 -1
  47. data/lib/sidekiq/web/action.rb +29 -7
  48. data/lib/sidekiq/web/application.rb +64 -25
  49. data/lib/sidekiq/web/csrf_protection.rb +9 -6
  50. data/lib/sidekiq/web/helpers.rb +95 -35
  51. data/lib/sidekiq/web/router.rb +5 -2
  52. data/lib/sidekiq/web.rb +67 -3
  53. data/lib/sidekiq.rb +5 -3
  54. data/sidekiq.gemspec +5 -13
  55. data/web/assets/javascripts/application.js +27 -0
  56. data/web/assets/javascripts/dashboard-charts.js +40 -12
  57. data/web/assets/javascripts/dashboard.js +14 -10
  58. data/web/assets/javascripts/metrics.js +34 -0
  59. data/web/assets/stylesheets/application-rtl.css +10 -0
  60. data/web/assets/stylesheets/application.css +38 -3
  61. data/web/locales/en.yml +5 -1
  62. data/web/locales/fr.yml +13 -0
  63. data/web/locales/gd.yml +0 -1
  64. data/web/locales/it.yml +32 -1
  65. data/web/locales/ja.yml +0 -1
  66. data/web/locales/pt-br.yml +20 -1
  67. data/web/locales/tr.yml +100 -0
  68. data/web/locales/uk.yml +24 -1
  69. data/web/locales/zh-cn.yml +0 -1
  70. data/web/locales/zh-tw.yml +0 -1
  71. data/web/views/_footer.erb +12 -1
  72. data/web/views/_job_info.erb +1 -1
  73. data/web/views/_metrics_period_select.erb +1 -1
  74. data/web/views/_summary.erb +7 -7
  75. data/web/views/busy.erb +7 -7
  76. data/web/views/dashboard.erb +29 -36
  77. data/web/views/filtering.erb +6 -0
  78. data/web/views/layout.erb +6 -6
  79. data/web/views/metrics.erb +38 -30
  80. data/web/views/metrics_for_job.erb +30 -39
  81. data/web/views/morgue.erb +2 -2
  82. data/web/views/queue.erb +1 -1
  83. data/web/views/queues.erb +6 -2
  84. metadata +52 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8bdff48fe97b8b29f77dcd24b8807541fb5ddee0562efc6a836d0888a07f7429
4
- data.tar.gz: a106c157bbe402f5ebc1ce15ffea50a1a82e5aefed49943f7dfdafa412fb4961
3
+ metadata.gz: 8c8347f3f2281d531c67cac5b8d00a38baa08c2fa144d111e0d380c9520e1d88
4
+ data.tar.gz: 70f09f133c258fa1239d98f294e66ba9fba21dbb55573a76510d128b795b0180
5
5
  SHA512:
6
- metadata.gz: 7e63f9d1bbb4f685f59a6c65c1dc820ddc3d31f1dea8c87828072f5eb89dc8a99491d43517d60d917feb8f912822fefd38f694199f31fccb36da8cab0e931ea0
7
- data.tar.gz: e43c57aae746ae4e77b64f34bd9927e5b3b8fb2c9e5f257784a1aa767fca7aff2a978d6a7924fd5d3b659a401fc90d6ee3b9971acee547c358cc4066ac1c2a7d
6
+ metadata.gz: 308446f60faf0b6c180c62562f8e787e68f7654d9339e4c43b0219013b952a945e728117c8b8121d57a91fc1cb71eeecb560779f29579801d76f9a33fdaac963
7
+ data.tar.gz: '0392876234c063d8d9a2ecc10c6e2a79bd8dfea10fa4d547fb9046c134917d6aa958b9c7946c95797c6dc75ebfc694c52cd8e30602a5598d093e75281ce0f03f'
data/Changes.md CHANGED
@@ -2,6 +2,259 @@
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.10
6
+ ----------
7
+
8
+ - Allow Redis :password as a Proc [#6625]
9
+ `config.redis = { password: ->(username) { "password" } }`
10
+ - Bump required redis-client version to 0.23
11
+ - Lock dependencies to known good major versions
12
+
13
+ 7.3.9
14
+ ----------
15
+
16
+ - Only require activejob if necessary [#6584]
17
+ - Fix iterable job cancellation [#6589]
18
+ - Web UI accessibility improvements [#6604]
19
+
20
+ 7.3.8
21
+ ----------
22
+
23
+ - Fix dead tag links [#6554]
24
+ - Massive Web UI performance improvement, some pages up to 15x faster [#6555]
25
+
26
+ 7.3.7
27
+ ----------
28
+
29
+ - Backport `Sidekiq::Web.configure` for compatibility with 8.0 [#6532]
30
+ - Backport `url_params(key)` and `route_params(key)` for compatibility with 8.0 [#6532]
31
+ - Various fixes for UI filtering [#6508]
32
+ - Tune `inspect` for internal S::Components to keep size managable [#6553]
33
+
34
+ 7.3.6
35
+ ----------
36
+
37
+ - Forward compatibility fixes for Ruby 3.4
38
+ - Filtering in the Web UI now works via GET so you can bookmark a filtered view. [#6497]
39
+
40
+ 7.3.5
41
+ ----------
42
+
43
+ - Reimplement `retry_all` and `kill_all` API methods to use ZPOPMIN,
44
+ approximately 30-60% faster. [#6481]
45
+ - Add preload testing binary at `examples/testing/sidekiq_boot` to verify your Rails app boots correctly with Sidekiq Enterprise's app preloading.
46
+ - Fix circular require with ActiveJob adapter [#6477]
47
+ - Fix potential race condition leading to incorrect serialized values for CurrentAttributes [#6475]
48
+ - Restore missing elapsed time when default job logging is disabled
49
+
50
+ 7.3.4
51
+ ----------
52
+
53
+ - Fix FrozenError when starting Sidekiq [#6470]
54
+
55
+ 7.3.3
56
+ ----------
57
+
58
+ - Freeze global configuration once boot is complete, to avoid configuration race conditions [#6466, #6465]
59
+ - Sidekiq now warns if a job iteration takes longer than the `-t` timeout setting (defaults to 25 seconds)
60
+ - Iteration callbacks now have easy access to job arguments via the `arguments` method:
61
+ ```ruby
62
+ def on_stop
63
+ p arguments # => `[123, "string", {"key" => "value"}]`
64
+ id, str, hash = arguments
65
+ end
66
+ ```
67
+ - Iterable jobs can be cancelled via `Sidekiq::Client#cancel!`:
68
+ ```ruby
69
+ c = Sidekiq::Client.new
70
+ jid = c.push("class" => SomeJob, "args" => [123])
71
+ c.cancel!(jid) # => true
72
+ ```
73
+ - Take over support for ActiveJob's `:sidekiq` adapter [#6430, fatkodima]
74
+ - Ensure CurrentAttributes are in scope when creating batch callbacks [#6455]
75
+ - Add `Sidekiq.gem_version` API.
76
+ - Update Ukranian translations
77
+
78
+ 7.3.2
79
+ ----------
80
+
81
+ - Adjust ActiveRecord batch iteration to restart an interrupted batch from the beginning.
82
+ Each batch should be processed as a single transaction in order to be idempotent. [#6405]
83
+ - Fix typo in Sidekiq::DeadSet#kill [#6397]
84
+ - Fix CSS issue with bottom bar in Web UI [#6414]
85
+
86
+ 7.3.1
87
+ ----------
88
+
89
+ - Don't count job interruptions as failures in metrics [#6386]
90
+ - Add frozen string literal to a number of .rb files.
91
+ - Fix frozen string error with style_tag and script_tag [#6371]
92
+ - Fix an error on Ruby 2.7 because of usage of `Hash#except` [#6376]
93
+
94
+ 7.3.0
95
+ ----------
96
+
97
+ - **NEW FEATURE** Add `Sidekiq::IterableJob`, iteration support for long-running jobs. [#6286, fatkodima]
98
+ Iterable jobs are interruptible and can restart quickly if
99
+ running during a deploy. You must ensure that `each_iteration`
100
+ doesn't take more than Sidekiq's `-t` timeout (default: 25 seconds). Iterable jobs must not implement `perform`.
101
+ ```ruby
102
+ class ProcessArrayJob
103
+ include Sidekiq::IterableJob
104
+ def build_enumerator(*args, **kwargs)
105
+ array_enumerator(args, **kwargs)
106
+ end
107
+ def each_iteration(arg)
108
+ puts arg
109
+ end
110
+ end
111
+ ProcessArrayJob.perform_async(1, 2, 3)
112
+ ```
113
+ See the [Iteration](//github.com/sidekiq/sidekiq/wiki/Iteration) wiki page and the RDoc in `Sidekiq::IterableJob`.
114
+ This feature should be considered BETA until the next minor release.
115
+ - **SECURITY** The Web UI no longer allows extensions to use `<script>`.
116
+ Adjust CSP to disallow inline scripts within the Web UI. Please see
117
+ `examples/webui-ext` for how to register Web UI extensions and use
118
+ dynamic CSS and JS. This will make Sidekiq immune to XSS attacks. [#6270]
119
+ - Add config option, `:skip_default_job_logging` to disable Sidekiq's default
120
+ start/finish job logging. [#6200]
121
+ - Allow `Sidekiq::Limiter.redis` to use Redis Cluster [#6288]
122
+ - Retain CurrentAttributeѕ after inline execution [#6307]
123
+ - Ignore non-existent CurrentAttributes attributes when restoring [#6341]
124
+ - Raise default Redis {read,write,connect} timeouts from 1 to 3 seconds
125
+ to minimize ReadTimeoutErrors [#6162]
126
+ - Add `logger` as a dependency since it will become bundled in Ruby 3.5 [#6320]
127
+ - Ignore unsupported locales in the Web UI [#6313]
128
+
129
+ 7.2.4
130
+ ----------
131
+
132
+ - Fix XSS in metrics filtering introduced in 7.2.0, CVE-2024-32887
133
+ Thanks to @UmerAdeemCheema for the security report.
134
+
135
+ 7.2.3
136
+ ----------
137
+
138
+ - [Support Dragonfly.io](https://www.mikeperham.com/2024/02/01/supporting-dragonfly/) as an alternative Redis implementation
139
+ - Fix error unpacking some compressed error backtraces [#6241]
140
+ - Fix potential heartbeat data leak [#6227]
141
+ - Add ability to find a currently running work by jid [#6212, fatkodima]
142
+
143
+ 7.2.2
144
+ ----------
145
+
146
+ - Add `Process.warmup` call in Ruby 3.3+
147
+ - Batch jobs now skip transactional push [#6160]
148
+
149
+ 7.2.1
150
+ ----------
151
+
152
+ - Add `Sidekiq::Work` type which replaces the raw Hash as the third parameter in
153
+ `Sidekiq::WorkSet#each { |pid, tid, hash| ... }` [#6145]
154
+ - **DEPRECATED**: direct access to the attributes within the `hash` block parameter above.
155
+ The `Sidekiq::Work` instance contains accessor methods to get at the same data, e.g.
156
+ ```ruby
157
+ work["queue"] # Old
158
+ work.queue # New
159
+ ```
160
+ - Fix Ruby 3.3 warnings around `base64` gem [#6151, earlopain]
161
+
162
+ 7.2.0
163
+ ----------
164
+
165
+ - `sidekiq_retries_exhausted` can return `:discard` to avoid the deadset
166
+ and all death handlers [#6091]
167
+ - Metrics filtering by job class in Web UI [#5974]
168
+ - Better readability and formatting for numbers within the Web UI [#6080]
169
+ - Add explicit error if user code tries to nest test modes [#6078]
170
+ ```ruby
171
+ Sidekiq::Testing.inline! # global setting
172
+ Sidekiq::Testing.fake! do # override within block
173
+ # ok
174
+ Sidekiq::Testing.inline! do # can't override the override
175
+ # not ok, nested
176
+ end
177
+ end
178
+ ```
179
+ - **SECURITY** Forbid inline JavaScript execution in Web UI [#6074]
180
+ - Adjust redis-client adapter to avoid `method_missing` [#6083]
181
+ This can result in app code breaking if your app's Redis API usage was
182
+ depending on Sidekiq's adapter to correct invalid redis-client API usage.
183
+ One example:
184
+ ```ruby
185
+ # bad, not redis-client native
186
+ # Unsupported command argument type: TrueClass (TypeError)
187
+ Sidekiq.redis { |c| c.set("key", "value", nx: true, ex: 15) }
188
+ # good
189
+ Sidekiq.redis { |c| c.set("key", "value", "nx", "ex", 15) }
190
+ ```
191
+
192
+ 7.1.6
193
+ ----------
194
+
195
+ - The block forms of testing modes (inline, fake) are now thread-safe so you can have
196
+ a multithreaded test suite which uses different modes for different tests. [#6069]
197
+ - Fix breakage with non-Proc error handlers [#6065]
198
+
199
+ 7.1.5
200
+ ----------
201
+
202
+ - **FEATURE**: Job filtering within the Web UI. This feature has been open
203
+ sourced from Sidekiq Pro. [#6052]
204
+ - **API CHANGE** Error handlers now take three arguments `->(ex, context, config)`.
205
+ The previous calling convention will work until Sidekiq 8.0 but will print
206
+ out a deprecation warning. [#6051]
207
+ - Fix issue with the `batch_size` and `at` options in `S::Client.push_bulk` [#6040]
208
+ - Fix inline testing firing batch callbacks early [#6057]
209
+ - Use new log broadcast API in Rails 7.1 [#6054]
210
+ - Crash if user tries to use RESP2 `protocol: 2` [#6061]
211
+
212
+ 7.1.4
213
+ ----------
214
+
215
+ - Fix empty `retry_for` logic [#6035]
216
+
217
+ 7.1.3
218
+ ----------
219
+
220
+ - Add `sidekiq_options retry_for: 48.hours` to allow time-based retry windows [#6029]
221
+ - Support sidekiq_retry_in and sidekiq_retries_exhausted_block in ActiveJobs (#5994)
222
+ - Lowercase all Rack headers for Rack 3.0 [#5951]
223
+ - Validate Sidekiq::Web page refresh delay to avoid potential DoS,
224
+ CVE-2023-26141, thanks for reporting Keegan!
225
+
226
+ 7.1.2
227
+ ----------
228
+
229
+ - Mark Web UI assets as private so CDNs won't cache them [#5936]
230
+ - Fix stackoverflow when using Oj and the JSON log formatter [#5920]
231
+ - Remove spurious `enqueued_at` from scheduled ActiveJobs [#5937]
232
+
233
+ 7.1.1
234
+ ----------
235
+
236
+ - Support multiple CurrentAttributes [#5904]
237
+ - Speed up latency fetch with large queues on Redis <7 [#5910]
238
+ - Allow a larger default client pool [#5886]
239
+ - Ensure Sidekiq.options[:environment] == RAILS_ENV [#5932]
240
+
241
+ 7.1.0
242
+ ----------
243
+
244
+ - Improve display of ActiveJob arguments in Web UI [#5825, cover]
245
+ - Update `push_bulk` to push `batch_size` jobs at a time and allow laziness [#5827, fatkodima]
246
+ This allows Sidekiq::Client to push unlimited jobs as long as it has enough memory for the batch_size.
247
+ - Update `perform_bulk` to use `push_bulk` internally.
248
+ - Change return value of `push_bulk` to map 1-to-1 with arguments.
249
+ If you call `push_bulk(args: [[1], [2], [3]])`, you will now always get
250
+ an array of 3 values as the result: `["jid1", nil, "jid3"]` where nil means
251
+ that particular job did not push successfully (possibly due to middleware
252
+ stopping it). Previously nil values were removed so it was impossible to tell
253
+ which jobs pushed successfully and which did not.
254
+ - Migrate away from all deprecated Redis commands [#5788]
255
+ Sidekiq will now print a warning if you use one of those deprecated commands.
256
+ - Prefix all Sidekiq thread names [#5872]
257
+
5
258
  7.0.9
6
259
  ----------
7
260
 
@@ -84,6 +337,16 @@ end
84
337
  - Job Execution metrics!!!
85
338
  - See `docs/7.0-Upgrade.md` for release notes
86
339
 
340
+ 6.5.{10,11,12}
341
+ ----------
342
+
343
+ - Fixes for Rails 7.1 [#6067, #6070]
344
+
345
+ 6.5.9
346
+ ----------
347
+
348
+ - Ensure Sidekiq.options[:environment] == RAILS_ENV [#5932]
349
+
87
350
  6.5.8
88
351
  ----------
89
352
 
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
  -----------------
@@ -83,10 +83,10 @@ You can purchase at https://sidekiq.org; email support@contribsys.com for help.
83
83
  Useful resources:
84
84
 
85
85
  * Product documentation is in the [wiki](https://github.com/sidekiq/sidekiq/wiki).
86
- * Occasional announcements are made to the [@sidekiq](https://twitter.com/sidekiq) Twitter account.
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
@@ -103,4 +103,4 @@ The license for Sidekiq Pro and Sidekiq Enterprise can be found in [COMM-LICENSE
103
103
  Author
104
104
  -----------------
105
105
 
106
- Mike Perham, [@getajobmike](https://twitter.com/getajobmike) / [@sidekiq](https://twitter.com/sidekiq), [https://www.mikeperham.com](https://www.mikeperham.com) / [https://www.contribsys.com](https://www.contribsys.com)
106
+ Mike Perham, [@getajobmike](https://ruby.social/@getajobmike) / [@sidekiq](https://ruby.social/@sidekiq), [https://www.mikeperham.com](https://www.mikeperham.com) / [https://www.contribsys.com](https://www.contribsys.com)
@@ -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
@@ -1,5 +1,23 @@
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
@@ -32,7 +50,7 @@ if ENV["AJ"]
32
50
  ActiveJob::Base.logger.level = Logger::WARN
33
51
 
34
52
  class LoadJob < ActiveJob::Base
35
- def perform(idx, ts=nil)
53
+ def perform(string, idx, hash, ts = nil)
36
54
  puts(Time.now.to_f - ts) if !ts.nil?
37
55
  end
38
56
  end
@@ -40,12 +58,21 @@ end
40
58
 
41
59
  class LoadWorker
42
60
  include Sidekiq::Job
61
+ $count = 0
62
+ $lock = Mutex.new
63
+
43
64
  sidekiq_options retry: 1
44
65
  sidekiq_retry_in do |x|
45
66
  1
46
67
  end
47
68
 
48
- 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
49
76
  puts(Time.now.to_f - ts) if !ts.nil?
50
77
  # raise idx.to_s if idx % 100 == 1
51
78
  end
@@ -115,13 +142,13 @@ class Loader
115
142
  start = Time.now
116
143
  if ENV["AJ"]
117
144
  @iter.times do
118
- @count.times do |idx|
119
- LoadJob.perform_later(idx)
120
- end
145
+ ActiveJob.perform_all_later(@count.times.map do |idx|
146
+ LoadJob.new("mike", idx, {mike: "bob"})
147
+ end)
121
148
  end
122
149
  else
123
150
  @iter.times do
124
- arr = Array.new(@count) { |idx| [idx] }
151
+ arr = Array.new(@count) { |idx| ["string", idx, {"mike" => "bob"}] }
125
152
  Sidekiq::Client.push_bulk("class" => LoadWorker, "args" => arr)
126
153
  end
127
154
  end
@@ -145,13 +172,13 @@ class Loader
145
172
  Sidekiq.logger.error("Now here's the latency for three jobs")
146
173
 
147
174
  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)
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)
151
178
  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)
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)
155
182
  end
156
183
 
157
184
  sleep 0.1
@@ -219,11 +246,11 @@ end
219
246
  ll = Loader.new
220
247
  ll.configure
221
248
 
222
- unless ENV["GC"] || ENV["PROFILE"]
249
+ if ENV["WARM"]
223
250
  ll.setup
224
251
  ll.run("warmup")
225
252
  end
226
253
 
227
254
  ll.setup
228
- ll.run("ideal")
255
+ ll.run("load")
229
256
  ll.done