sidekiq 7.1.6 → 7.2.4

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +68 -0
  3. data/README.md +2 -2
  4. data/bin/multi_queue_bench +271 -0
  5. data/lib/sidekiq/api.rb +77 -11
  6. data/lib/sidekiq/cli.rb +3 -1
  7. data/lib/sidekiq/config.rb +4 -4
  8. data/lib/sidekiq/deploy.rb +2 -2
  9. data/lib/sidekiq/job.rb +1 -1
  10. data/lib/sidekiq/job_retry.rb +3 -3
  11. data/lib/sidekiq/launcher.rb +6 -4
  12. data/lib/sidekiq/logger.rb +1 -1
  13. data/lib/sidekiq/metrics/query.rb +4 -1
  14. data/lib/sidekiq/metrics/tracking.rb +7 -3
  15. data/lib/sidekiq/middleware/current_attributes.rb +1 -1
  16. data/lib/sidekiq/paginator.rb +2 -2
  17. data/lib/sidekiq/processor.rb +1 -1
  18. data/lib/sidekiq/rails.rb +7 -3
  19. data/lib/sidekiq/redis_client_adapter.rb +16 -0
  20. data/lib/sidekiq/redis_connection.rb +3 -6
  21. data/lib/sidekiq/scheduled.rb +2 -2
  22. data/lib/sidekiq/testing.rb +9 -3
  23. data/lib/sidekiq/transaction_aware_client.rb +7 -0
  24. data/lib/sidekiq/version.rb +1 -1
  25. data/lib/sidekiq/web/action.rb +5 -0
  26. data/lib/sidekiq/web/application.rb +32 -4
  27. data/lib/sidekiq/web/csrf_protection.rb +8 -5
  28. data/lib/sidekiq/web/helpers.rb +14 -15
  29. data/sidekiq.gemspec +1 -1
  30. data/web/assets/javascripts/application.js +21 -0
  31. data/web/assets/javascripts/dashboard-charts.js +14 -0
  32. data/web/assets/javascripts/dashboard.js +7 -9
  33. data/web/assets/javascripts/metrics.js +34 -0
  34. data/web/assets/stylesheets/application-rtl.css +10 -0
  35. data/web/assets/stylesheets/application.css +22 -0
  36. data/web/views/_footer.erb +13 -1
  37. data/web/views/_metrics_period_select.erb +1 -1
  38. data/web/views/_summary.erb +7 -7
  39. data/web/views/busy.erb +7 -7
  40. data/web/views/dashboard.erb +23 -33
  41. data/web/views/filtering.erb +4 -4
  42. data/web/views/metrics.erb +36 -27
  43. data/web/views/metrics_for_job.erb +26 -35
  44. data/web/views/queues.erb +6 -2
  45. metadata +5 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b50ab03f32263dd24ee37ce2ddadc7a4e5e226860146c1a677262dd3a417329d
4
- data.tar.gz: 0af1051781796465d5ce5dd0d0e454016fdc04b7cb5fcf0b8d62e447faff11ed
3
+ metadata.gz: 6c43e6b585c25dcfc8ef8364bb36cf74f9167b981ad03faa3a8d76e0d45ebe55
4
+ data.tar.gz: d8c65dc03008f7280b36af94db753d4c7f68267c2eb0d78cd018322887aabbb0
5
5
  SHA512:
6
- metadata.gz: f2e84e49ceb024e8cb24a7213ffe81e40ed9423d646e39778ed51049bf89a04c28890a21e93eeb38bb9daefe88b94b150d71a0cdf80ecdad712a2167dd0a3421
7
- data.tar.gz: 39ffc3e4eee3e2c27a3f774f2a2120c50a52ab9e2049b0fc925123c622553bf4fd19512c16e63882031909bba6ea5b2d15cf856256a3c453d080e89253196fc6
6
+ metadata.gz: d2687692b873ab82bda2ad32e9be795150cd0a8d3d330bc19f5b509ba729bef33189e06ebac86b1906c2682187391d6cf0d532e47d03fcbea83058109c5816ef
7
+ data.tar.gz: 431a482baeb03fc4de50fbdfba8717fc332a9d6564fde98a77699a7bd174fa3194431385951cf689c64e04853039c95fcf287084f283e8d381b3b37d5bc665e0
data/Changes.md CHANGED
@@ -2,6 +2,69 @@
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.2.4
6
+ ----------
7
+
8
+ - Fix XSS in metrics filtering introduced in 7.2.0, CVE-2024-32887
9
+ Thanks to @UmerAdeemCheema for the security report.
10
+
11
+ 7.2.3
12
+ ----------
13
+
14
+ - [Support Dragonfly.io](https://www.mikeperham.com/2024/02/01/supporting-dragonfly/) as an alternative Redis implementation
15
+ - Fix error unpacking some compressed error backtraces [#6241]
16
+ - Fix potential heartbeat data leak [#6227]
17
+ - Add ability to find a currently running work by jid [#6212, fatkodima]
18
+
19
+ 7.2.2
20
+ ----------
21
+
22
+ - Add `Process.warmup` call in Ruby 3.3+
23
+ - Batch jobs now skip transactional push [#6160]
24
+
25
+ 7.2.1
26
+ ----------
27
+
28
+ - Add `Sidekiq::Work` type which replaces the raw Hash as the third parameter in
29
+ `Sidekiq::WorkSet#each { |pid, tid, hash| ... }` [#6145]
30
+ - **DEPRECATED**: direct access to the attributes within the `hash` block parameter above.
31
+ The `Sidekiq::Work` instance contains accessor methods to get at the same data, e.g.
32
+ ```ruby
33
+ work["queue"] # Old
34
+ work.queue # New
35
+ ```
36
+ - Fix Ruby 3.3 warnings around `base64` gem [#6151, earlopain]
37
+
38
+ 7.2.0
39
+ ----------
40
+
41
+ - `sidekiq_retries_exhausted` can return `:discard` to avoid the deadset
42
+ and all death handlers [#6091]
43
+ - Metrics filtering by job class in Web UI [#5974]
44
+ - Better readability and formatting for numbers within the Web UI [#6080]
45
+ - Add explicit error if user code tries to nest test modes [#6078]
46
+ ```ruby
47
+ Sidekiq::Testing.inline! # global setting
48
+ Sidekiq::Testing.fake! do # override within block
49
+ # ok
50
+ Sidekiq::Testing.inline! do # can't override the override
51
+ # not ok, nested
52
+ end
53
+ end
54
+ ```
55
+ - **SECURITY** Forbid inline JavaScript execution in Web UI [#6074]
56
+ - Adjust redis-client adapter to avoid `method_missing` [#6083]
57
+ This can result in app code breaking if your app's Redis API usage was
58
+ depending on Sidekiq's adapter to correct invalid redis-client API usage.
59
+ One example:
60
+ ```ruby
61
+ # bad, not redis-client native
62
+ # Unsupported command argument type: TrueClass (TypeError)
63
+ Sidekiq.redis { |c| c.set("key", "value", nx: true, ex: 15) }
64
+ # good
65
+ Sidekiq.redis { |c| c.set("key", "value", "nx", "ex", 15) }
66
+ ```
67
+
5
68
  7.1.6
6
69
  ----------
7
70
 
@@ -150,6 +213,11 @@ end
150
213
  - Job Execution metrics!!!
151
214
  - See `docs/7.0-Upgrade.md` for release notes
152
215
 
216
+ 6.5.{10,11,12}
217
+ ----------
218
+
219
+ - Fixes for Rails 7.1 [#6067, #6070]
220
+
153
221
  6.5.9
154
222
  ----------
155
223
 
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
  -----------------
@@ -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/lib/sidekiq/api.rb CHANGED
@@ -4,7 +4,6 @@ require "sidekiq"
4
4
 
5
5
  require "zlib"
6
6
  require "set"
7
- require "base64"
8
7
 
9
8
  require "sidekiq/metrics/query"
10
9
 
@@ -491,8 +490,8 @@ module Sidekiq
491
490
  end
492
491
 
493
492
  def uncompress_backtrace(backtrace)
494
- decoded = Base64.decode64(backtrace)
495
- uncompressed = Zlib::Inflate.inflate(decoded)
493
+ strict_base64_decoded = backtrace.unpack1("m")
494
+ uncompressed = Zlib::Inflate.inflate(strict_base64_decoded)
496
495
  Sidekiq.load_json(uncompressed)
497
496
  end
498
497
  end
@@ -679,7 +678,7 @@ module Sidekiq
679
678
  range_start = page * page_size + offset_size
680
679
  range_end = range_start + page_size - 1
681
680
  elements = Sidekiq.redis { |conn|
682
- conn.zrange name, range_start, range_end, withscores: true
681
+ conn.zrange name, range_start, range_end, "withscores"
683
682
  }
684
683
  break if elements.empty?
685
684
  page -= 1
@@ -706,7 +705,7 @@ module Sidekiq
706
705
  end
707
706
 
708
707
  elements = Sidekiq.redis { |conn|
709
- conn.zrange(name, begin_score, end_score, "BYSCORE", withscores: true)
708
+ conn.zrange(name, begin_score, end_score, "BYSCORE", "withscores")
710
709
  }
711
710
 
712
711
  elements.each_with_object([]) do |element, result|
@@ -774,7 +773,7 @@ module Sidekiq
774
773
  #
775
774
  class ScheduledSet < JobSet
776
775
  def initialize
777
- super "schedule"
776
+ super("schedule")
778
777
  end
779
778
  end
780
779
 
@@ -788,7 +787,7 @@ module Sidekiq
788
787
  #
789
788
  class RetrySet < JobSet
790
789
  def initialize
791
- super "retry"
790
+ super("retry")
792
791
  end
793
792
 
794
793
  # Enqueues all jobs pending within the retry set.
@@ -809,7 +808,7 @@ module Sidekiq
809
808
  #
810
809
  class DeadSet < JobSet
811
810
  def initialize
812
- super "dead"
811
+ super("dead")
813
812
  end
814
813
 
815
814
  # Add the given job to the Dead set.
@@ -881,7 +880,7 @@ module Sidekiq
881
880
  # @api private
882
881
  def cleanup
883
882
  # dont run cleanup more than once per minute
884
- return 0 unless Sidekiq.redis { |conn| conn.set("process_cleanup", "1", nx: true, ex: 60) }
883
+ return 0 unless Sidekiq.redis { |conn| conn.set("process_cleanup", "1", "NX", "EX", "60") }
885
884
 
886
885
  count = 0
887
886
  Sidekiq.redis do |conn|
@@ -1110,11 +1109,11 @@ module Sidekiq
1110
1109
 
1111
1110
  procs.zip(all_works).each do |key, workers|
1112
1111
  workers.each_pair do |tid, json|
1113
- results << [key, tid, Sidekiq.load_json(json)] unless json.empty?
1112
+ results << [key, tid, Sidekiq::Work.new(key, tid, Sidekiq.load_json(json))] unless json.empty?
1114
1113
  end
1115
1114
  end
1116
1115
 
1117
- results.sort_by { |(_, _, hsh)| hsh["run_at"] }.each(&block)
1116
+ results.sort_by { |(_, _, hsh)| hsh.raw("run_at") }.each(&block)
1118
1117
  end
1119
1118
 
1120
1119
  # Note that #size is only as accurate as Sidekiq's heartbeat,
@@ -1137,7 +1136,74 @@ module Sidekiq
1137
1136
  end
1138
1137
  end
1139
1138
  end
1139
+
1140
+ ##
1141
+ # Find the work which represents a job with the given JID.
1142
+ # *This is a slow O(n) operation*. Do not use for app logic.
1143
+ #
1144
+ # @param jid [String] the job identifier
1145
+ # @return [Sidekiq::Work] the work or nil
1146
+ def find_work_by_jid(jid)
1147
+ each do |_process_id, _thread_id, work|
1148
+ job = work.job
1149
+ return work if job.jid == jid
1150
+ end
1151
+ nil
1152
+ end
1140
1153
  end
1154
+
1155
+ # Sidekiq::Work represents a job which is currently executing.
1156
+ class Work
1157
+ attr_reader :process_id
1158
+ attr_reader :thread_id
1159
+
1160
+ def initialize(pid, tid, hsh)
1161
+ @process_id = pid
1162
+ @thread_id = tid
1163
+ @hsh = hsh
1164
+ @job = nil
1165
+ end
1166
+
1167
+ def queue
1168
+ @hsh["queue"]
1169
+ end
1170
+
1171
+ def run_at
1172
+ Time.at(@hsh["run_at"])
1173
+ end
1174
+
1175
+ def job
1176
+ @job ||= Sidekiq::JobRecord.new(@hsh["payload"])
1177
+ end
1178
+
1179
+ def payload
1180
+ @hsh["payload"]
1181
+ end
1182
+
1183
+ # deprecated
1184
+ def [](key)
1185
+ kwargs = {uplevel: 1}
1186
+ kwargs[:category] = :deprecated if RUBY_VERSION > "3.0" # TODO
1187
+ warn("Direct access to `Sidekiq::Work` attributes is deprecated, please use `#payload`, `#queue`, `#run_at` or `#job` instead", **kwargs)
1188
+
1189
+ @hsh[key]
1190
+ end
1191
+
1192
+ # :nodoc:
1193
+ # @api private
1194
+ def raw(name)
1195
+ @hsh[name]
1196
+ end
1197
+
1198
+ def method_missing(*all)
1199
+ @hsh.send(*all)
1200
+ end
1201
+
1202
+ def respond_to_missing?(name)
1203
+ @hsh.respond_to?(name)
1204
+ end
1205
+ end
1206
+
1141
1207
  # Since "worker" is a nebulous term, we've deprecated the use of this class name.
1142
1208
  # Is "worker" a process, a type of job, a thread? Undefined!
1143
1209
  # WorkSet better describes the data.
data/lib/sidekiq/cli.rb CHANGED
@@ -38,7 +38,7 @@ module Sidekiq # :nodoc:
38
38
  # Code within this method is not tested because it alters
39
39
  # global process state irreversibly. PRs which improve the
40
40
  # test coverage of Sidekiq::CLI are welcomed.
41
- def run(boot_app: true)
41
+ def run(boot_app: true, warmup: true)
42
42
  boot_application if boot_app
43
43
 
44
44
  if environment == "development" && $stdout.tty? && @config.logger.formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
@@ -101,6 +101,8 @@ module Sidekiq # :nodoc:
101
101
  # Touch middleware so it isn't lazy loaded by multiple threads, #3043
102
102
  @config.server_middleware
103
103
 
104
+ ::Process.warmup if warmup && ::Process.respond_to?(:warmup)
105
+
104
106
  # Before this point, the process is initializing with just the main thread.
105
107
  # Starting here the process will now have multiple threads running.
106
108
  fire_event(:startup, reverse: false, reraise: true)
@@ -258,9 +258,9 @@ module Sidekiq
258
258
  @logger = logger
259
259
  end
260
260
 
261
- private def arity(handler)
262
- return handler.arity if handler.is_a?(Proc)
263
- handler.method(:call).arity
261
+ private def parameter_size(handler)
262
+ target = handler.is_a?(Proc) ? handler : handler.method(:call)
263
+ target.parameters.size
264
264
  end
265
265
 
266
266
  # INTERNAL USE ONLY
@@ -269,7 +269,7 @@ module Sidekiq
269
269
  p ["!!!!!", ex]
270
270
  end
271
271
  @options[:error_handlers].each do |handler|
272
- if arity(handler) == 2
272
+ if parameter_size(handler) == 2
273
273
  # TODO Remove in 8.0
274
274
  logger.info { "DEPRECATION: Sidekiq exception handlers now take three arguments, see #{handler}" }
275
275
  handler.call(ex, {_config: self}.merge(ctx))
@@ -34,7 +34,7 @@ module Sidekiq
34
34
  # handle an very common error in marking deploys:
35
35
  # having every process mark its deploy, leading
36
36
  # to N marks for each deploy. Instead we round the time
37
- # to the minute so that multple marks within that minute
37
+ # to the minute so that multiple marks within that minute
38
38
  # will all naturally rollup into one mark per minute.
39
39
  whence = at.utc
40
40
  floor = Time.utc(whence.year, whence.month, whence.mday, whence.hour, whence.min, 0)
@@ -44,7 +44,7 @@ module Sidekiq
44
44
 
45
45
  @pool.with do |c|
46
46
  # only allow one deploy mark for a given label for the next minute
47
- lock = c.set("deploylock-#{label}", stamp, nx: true, ex: 60)
47
+ lock = c.set("deploylock-#{label}", stamp, "nx", "ex", "60")
48
48
  if lock
49
49
  c.multi do |pipe|
50
50
  pipe.hsetnx(key, stamp, label)
data/lib/sidekiq/job.rb CHANGED
@@ -109,7 +109,7 @@ module Sidekiq
109
109
  m = "#{name}="
110
110
  undef_method(m) if method_defined?(m) || private_method_defined?(m)
111
111
  end
112
- define_singleton_method("#{name}=") do |val|
112
+ define_singleton_method(:"#{name}=") do |val|
113
113
  singleton_class.class_eval do
114
114
  ACCESSOR_MUTEX.synchronize do
115
115
  undef_method(synchronized_getter) if method_defined?(synchronized_getter) || private_method_defined?(synchronized_getter)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "zlib"
4
- require "base64"
5
4
  require "sidekiq/component"
6
5
 
7
6
  module Sidekiq
@@ -226,7 +225,7 @@ module Sidekiq
226
225
  end
227
226
 
228
227
  def retries_exhausted(jobinst, msg, exception)
229
- begin
228
+ rv = begin
230
229
  block = jobinst&.sidekiq_retries_exhausted_block
231
230
 
232
231
  # the sidekiq_retries_exhausted_block can be defined in a wrapped class (ActiveJob for instance)
@@ -239,6 +238,7 @@ module Sidekiq
239
238
  handle_exception(e, {context: "Error calling retries_exhausted", job: msg})
240
239
  end
241
240
 
241
+ return if rv == :discard # poof!
242
242
  send_to_morgue(msg) unless msg["dead"] == false
243
243
 
244
244
  @capsule.config.death_handlers.each do |handler|
@@ -294,7 +294,7 @@ module Sidekiq
294
294
  def compress_backtrace(backtrace)
295
295
  serialized = Sidekiq.dump_json(backtrace)
296
296
  compressed = Zlib::Deflate.deflate(serialized)
297
- Base64.encode64(compressed)
297
+ [compressed].pack("m0") # Base64.strict_encode64
298
298
  end
299
299
  end
300
300
  end
@@ -145,15 +145,17 @@ module Sidekiq
145
145
  flush_stats
146
146
 
147
147
  curstate = Processor::WORK_STATE.dup
148
+ curstate.transform_values! { |val| Sidekiq.dump_json(val) }
149
+
148
150
  redis do |conn|
149
151
  # work is the current set of executing jobs
150
152
  work_key = "#{key}:work"
151
- conn.pipelined do |transaction|
153
+ conn.multi do |transaction|
152
154
  transaction.unlink(work_key)
153
- curstate.each_pair do |tid, hash|
154
- transaction.hset(work_key, tid, Sidekiq.dump_json(hash))
155
+ if curstate.size > 0
156
+ transaction.hset(work_key, curstate)
157
+ transaction.expire(work_key, 60)
155
158
  end
156
- transaction.expire(work_key, 60)
157
159
  end
158
160
  end
159
161
 
@@ -36,7 +36,7 @@ module Sidekiq
36
36
  end
37
37
 
38
38
  LEVELS.each do |level, numeric_level|
39
- define_method("#{level}?") do
39
+ define_method(:"#{level}?") do
40
40
  local_level.nil? ? super() : local_level <= numeric_level
41
41
  end
42
42
  end