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.
- checksums.yaml +4 -4
- data/Changes.md +184 -0
- data/README.md +3 -3
- data/bin/multi_queue_bench +271 -0
- data/bin/sidekiqload +21 -12
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +75 -0
- data/lib/generators/sidekiq/job_generator.rb +2 -0
- data/lib/sidekiq/api.rb +139 -44
- data/lib/sidekiq/capsule.rb +8 -3
- data/lib/sidekiq/cli.rb +4 -1
- data/lib/sidekiq/client.rb +21 -1
- data/lib/sidekiq/component.rb +22 -0
- data/lib/sidekiq/config.rb +31 -7
- data/lib/sidekiq/deploy.rb +4 -2
- data/lib/sidekiq/embedded.rb +2 -0
- data/lib/sidekiq/fetch.rb +1 -1
- data/lib/sidekiq/iterable_job.rb +55 -0
- data/lib/sidekiq/job/interrupt_handler.rb +24 -0
- data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
- data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
- data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
- data/lib/sidekiq/job/iterable.rb +294 -0
- data/lib/sidekiq/job.rb +14 -3
- data/lib/sidekiq/job_logger.rb +7 -6
- data/lib/sidekiq/job_retry.rb +9 -4
- data/lib/sidekiq/job_util.rb +2 -0
- data/lib/sidekiq/launcher.rb +7 -5
- data/lib/sidekiq/logger.rb +1 -1
- data/lib/sidekiq/metrics/query.rb +6 -1
- data/lib/sidekiq/metrics/shared.rb +15 -4
- data/lib/sidekiq/metrics/tracking.rb +20 -8
- data/lib/sidekiq/middleware/current_attributes.rb +46 -13
- data/lib/sidekiq/middleware/modules.rb +2 -0
- data/lib/sidekiq/monitor.rb +2 -1
- data/lib/sidekiq/paginator.rb +8 -2
- data/lib/sidekiq/processor.rb +21 -11
- data/lib/sidekiq/rails.rb +19 -3
- data/lib/sidekiq/redis_client_adapter.rb +24 -5
- data/lib/sidekiq/redis_connection.rb +36 -8
- data/lib/sidekiq/ring_buffer.rb +2 -0
- data/lib/sidekiq/scheduled.rb +2 -2
- data/lib/sidekiq/systemd.rb +2 -0
- data/lib/sidekiq/testing.rb +14 -8
- data/lib/sidekiq/transaction_aware_client.rb +7 -0
- data/lib/sidekiq/version.rb +5 -1
- data/lib/sidekiq/web/action.rb +26 -4
- data/lib/sidekiq/web/application.rb +53 -64
- data/lib/sidekiq/web/csrf_protection.rb +8 -5
- data/lib/sidekiq/web/helpers.rb +73 -27
- data/lib/sidekiq/web/router.rb +5 -2
- data/lib/sidekiq/web.rb +54 -2
- data/lib/sidekiq.rb +5 -3
- data/sidekiq.gemspec +3 -2
- data/web/assets/javascripts/application.js +26 -0
- data/web/assets/javascripts/dashboard-charts.js +37 -11
- data/web/assets/javascripts/dashboard.js +14 -10
- data/web/assets/javascripts/metrics.js +34 -0
- data/web/assets/stylesheets/application-rtl.css +10 -0
- data/web/assets/stylesheets/application.css +38 -3
- data/web/locales/en.yml +3 -1
- data/web/locales/fr.yml +0 -1
- data/web/locales/gd.yml +0 -1
- data/web/locales/it.yml +32 -1
- data/web/locales/ja.yml +0 -1
- data/web/locales/pt-br.yml +1 -2
- data/web/locales/tr.yml +100 -0
- data/web/locales/uk.yml +24 -1
- data/web/locales/zh-cn.yml +0 -1
- data/web/locales/zh-tw.yml +0 -1
- data/web/views/_footer.erb +12 -1
- data/web/views/_metrics_period_select.erb +1 -1
- data/web/views/_summary.erb +7 -7
- data/web/views/busy.erb +7 -7
- data/web/views/dashboard.erb +29 -36
- data/web/views/filtering.erb +4 -5
- data/web/views/layout.erb +6 -6
- data/web/views/metrics.erb +38 -30
- data/web/views/metrics_for_job.erb +29 -38
- data/web/views/morgue.erb +2 -2
- data/web/views/queue.erb +1 -1
- data/web/views/queues.erb +6 -2
- metadata +33 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dccc402e8f4e309a47a2adb62418fa29989012e8c94f34aa11200b098dc375f2
|
4
|
+
data.tar.gz: 71b64a582d5971b045d60bae38dc26da265b60ff9a099ca3b2f9c7df9035e805
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 & A.
|
88
88
|
|
89
|
-
Every
|
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.
|
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
|