sidekiq 6.5.7 → 7.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +154 -13
  3. data/README.md +40 -32
  4. data/bin/sidekiq +3 -8
  5. data/bin/sidekiqload +204 -118
  6. data/bin/sidekiqmon +3 -0
  7. data/lib/sidekiq/api.rb +124 -131
  8. data/lib/sidekiq/capsule.rb +127 -0
  9. data/lib/sidekiq/cli.rb +57 -63
  10. data/lib/sidekiq/client.rb +64 -37
  11. data/lib/sidekiq/component.rb +4 -1
  12. data/lib/sidekiq/config.rb +278 -0
  13. data/lib/sidekiq/deploy.rb +62 -0
  14. data/lib/sidekiq/embedded.rb +61 -0
  15. data/lib/sidekiq/fetch.rb +11 -14
  16. data/lib/sidekiq/job.rb +371 -10
  17. data/lib/sidekiq/job_logger.rb +2 -2
  18. data/lib/sidekiq/job_retry.rb +33 -15
  19. data/lib/sidekiq/job_util.rb +51 -15
  20. data/lib/sidekiq/launcher.rb +66 -62
  21. data/lib/sidekiq/logger.rb +1 -26
  22. data/lib/sidekiq/manager.rb +9 -11
  23. data/lib/sidekiq/metrics/query.rb +4 -4
  24. data/lib/sidekiq/metrics/shared.rb +8 -7
  25. data/lib/sidekiq/metrics/tracking.rb +20 -18
  26. data/lib/sidekiq/middleware/chain.rb +19 -18
  27. data/lib/sidekiq/middleware/current_attributes.rb +53 -21
  28. data/lib/sidekiq/monitor.rb +17 -4
  29. data/lib/sidekiq/paginator.rb +10 -2
  30. data/lib/sidekiq/processor.rb +46 -51
  31. data/lib/sidekiq/rails.rb +12 -7
  32. data/lib/sidekiq/redis_client_adapter.rb +11 -69
  33. data/lib/sidekiq/redis_connection.rb +11 -111
  34. data/lib/sidekiq/scheduled.rb +21 -22
  35. data/lib/sidekiq/testing.rb +5 -33
  36. data/lib/sidekiq/transaction_aware_client.rb +4 -5
  37. data/lib/sidekiq/version.rb +2 -1
  38. data/lib/sidekiq/web/action.rb +3 -3
  39. data/lib/sidekiq/web/application.rb +29 -11
  40. data/lib/sidekiq/web/csrf_protection.rb +2 -2
  41. data/lib/sidekiq/web/helpers.rb +23 -32
  42. data/lib/sidekiq/web.rb +17 -16
  43. data/lib/sidekiq/worker_compatibility_alias.rb +13 -0
  44. data/lib/sidekiq.rb +76 -274
  45. data/sidekiq.gemspec +12 -10
  46. data/web/assets/javascripts/application.js +19 -0
  47. data/web/assets/javascripts/base-charts.js +106 -0
  48. data/web/assets/javascripts/dashboard-charts.js +168 -0
  49. data/web/assets/javascripts/dashboard.js +3 -223
  50. data/web/assets/javascripts/metrics.js +117 -115
  51. data/web/assets/stylesheets/application-dark.css +4 -0
  52. data/web/assets/stylesheets/application-rtl.css +2 -91
  53. data/web/assets/stylesheets/application.css +23 -298
  54. data/web/locales/ar.yml +70 -70
  55. data/web/locales/cs.yml +62 -62
  56. data/web/locales/da.yml +60 -53
  57. data/web/locales/de.yml +65 -65
  58. data/web/locales/el.yml +2 -7
  59. data/web/locales/en.yml +76 -70
  60. data/web/locales/es.yml +68 -68
  61. data/web/locales/fa.yml +65 -65
  62. data/web/locales/fr.yml +81 -67
  63. data/web/locales/gd.yml +99 -0
  64. data/web/locales/he.yml +65 -64
  65. data/web/locales/hi.yml +59 -59
  66. data/web/locales/it.yml +53 -53
  67. data/web/locales/ja.yml +67 -69
  68. data/web/locales/ko.yml +52 -52
  69. data/web/locales/lt.yml +66 -66
  70. data/web/locales/nb.yml +61 -61
  71. data/web/locales/nl.yml +52 -52
  72. data/web/locales/pl.yml +45 -45
  73. data/web/locales/pt-br.yml +79 -69
  74. data/web/locales/pt.yml +51 -51
  75. data/web/locales/ru.yml +67 -66
  76. data/web/locales/sv.yml +53 -53
  77. data/web/locales/ta.yml +60 -60
  78. data/web/locales/uk.yml +62 -61
  79. data/web/locales/ur.yml +64 -64
  80. data/web/locales/vi.yml +67 -67
  81. data/web/locales/zh-cn.yml +20 -18
  82. data/web/locales/zh-tw.yml +10 -1
  83. data/web/views/_footer.erb +5 -2
  84. data/web/views/_job_info.erb +18 -2
  85. data/web/views/_metrics_period_select.erb +12 -0
  86. data/web/views/_paging.erb +2 -0
  87. data/web/views/_poll_link.erb +1 -1
  88. data/web/views/busy.erb +42 -26
  89. data/web/views/dashboard.erb +36 -5
  90. data/web/views/metrics.erb +33 -20
  91. data/web/views/metrics_for_job.erb +25 -44
  92. data/web/views/morgue.erb +5 -9
  93. data/web/views/queue.erb +10 -14
  94. data/web/views/queues.erb +3 -1
  95. data/web/views/retries.erb +5 -9
  96. data/web/views/scheduled.erb +12 -13
  97. metadata +43 -33
  98. data/lib/sidekiq/delay.rb +0 -43
  99. data/lib/sidekiq/extensions/action_mailer.rb +0 -48
  100. data/lib/sidekiq/extensions/active_record.rb +0 -43
  101. data/lib/sidekiq/extensions/class_methods.rb +0 -43
  102. data/lib/sidekiq/extensions/generic_proxy.rb +0 -33
  103. data/lib/sidekiq/metrics/deploy.rb +0 -47
  104. data/lib/sidekiq/worker.rb +0 -370
  105. data/web/assets/javascripts/graph.js +0 -16
  106. /data/{LICENSE → LICENSE.txt} +0 -0
data/lib/sidekiq/cli.rb CHANGED
@@ -9,7 +9,9 @@ require "erb"
9
9
  require "fileutils"
10
10
 
11
11
  require "sidekiq"
12
+ require "sidekiq/config"
12
13
  require "sidekiq/component"
14
+ require "sidekiq/capsule"
13
15
  require "sidekiq/launcher"
14
16
 
15
17
  module Sidekiq # :nodoc:
@@ -22,9 +24,7 @@ module Sidekiq # :nodoc:
22
24
  attr_accessor :config
23
25
 
24
26
  def parse(args = ARGV.dup)
25
- @config = Sidekiq
26
- @config[:error_handlers].clear
27
- @config[:error_handlers] << @config.method(:default_error_handler)
27
+ @config ||= Sidekiq.default_configuration
28
28
 
29
29
  setup_options(args)
30
30
  initialize_logger
@@ -41,7 +41,7 @@ module Sidekiq # :nodoc:
41
41
  def run(boot_app: true)
42
42
  boot_application if boot_app
43
43
 
44
- if environment == "development" && $stdout.tty? && @config.log_formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
44
+ if environment == "development" && $stdout.tty? && @config.logger.formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
45
45
  print_banner
46
46
  end
47
47
  logger.info "Booted Rails #{::Rails.version} application in #{environment} environment" if rails_app?
@@ -73,26 +73,27 @@ module Sidekiq # :nodoc:
73
73
  # touch the connection pool so it is created before we
74
74
  # fire startup and start multithreading.
75
75
  info = @config.redis_info
76
- ver = info["redis_version"]
77
- raise "You are connecting to Redis v#{ver}, Sidekiq requires Redis v4.0.0 or greater" if ver < "4"
76
+ ver = Gem::Version.new(info["redis_version"])
77
+ raise "You are connecting to Redis #{ver}, Sidekiq requires Redis 6.2.0 or greater" if ver < Gem::Version.new("6.2.0")
78
78
 
79
79
  maxmemory_policy = info["maxmemory_policy"]
80
- if maxmemory_policy != "noeviction"
80
+ if maxmemory_policy != "noeviction" && maxmemory_policy != ""
81
+ # Redis Enterprise Cloud returns "" for their policy 😳
81
82
  logger.warn <<~EOM
82
83
 
83
84
 
84
85
  WARNING: Your Redis instance will evict Sidekiq data under heavy load.
85
86
  The 'noeviction' maxmemory policy is recommended (current policy: '#{maxmemory_policy}').
86
- See: https://github.com/mperham/sidekiq/wiki/Using-Redis#memory
87
+ See: https://github.com/sidekiq/sidekiq/wiki/Using-Redis#memory
87
88
 
88
89
  EOM
89
90
  end
90
91
 
91
92
  # Since the user can pass us a connection pool explicitly in the initializer, we
92
93
  # need to verify the size is large enough or else Sidekiq's performance is dramatically slowed.
93
- cursize = @config.redis_pool.size
94
- needed = @config[:concurrency] + 2
95
- raise "Your pool of #{cursize} Redis connections is too small, please increase the size to at least #{needed}" if cursize < needed
94
+ @config.capsules.each_pair do |name, cap|
95
+ raise ArgumentError, "Pool size too small for #{name}" if cap.redis_pool.size < cap.concurrency
96
+ end
96
97
 
97
98
  # cache process identity
98
99
  @config[:identity] = identity
@@ -104,8 +105,8 @@ module Sidekiq # :nodoc:
104
105
  # Starting here the process will now have multiple threads running.
105
106
  fire_event(:startup, reverse: false, reraise: true)
106
107
 
107
- logger.debug { "Client Middleware: #{@config.client_middleware.map(&:klass).join(", ")}" }
108
- logger.debug { "Server Middleware: #{@config.server_middleware.map(&:klass).join(", ")}" }
108
+ logger.debug { "Client Middleware: #{@config.default_capsule.client_middleware.map(&:klass).join(", ")}" }
109
+ logger.debug { "Server Middleware: #{@config.default_capsule.server_middleware.map(&:klass).join(", ")}" }
109
110
 
110
111
  launch(self_read)
111
112
  end
@@ -138,19 +139,34 @@ module Sidekiq # :nodoc:
138
139
  end
139
140
  end
140
141
 
141
- def self.w
142
- "\e[37m"
142
+ HOLIDAY_COLORS = {
143
+ # got other color-specific holidays from around the world?
144
+ # https://developer-book.com/post/definitive-guide-for-colored-text-in-terminal/#256-color-escape-codes
145
+ "3-17" => "\e[1;32m", # St. Patrick's Day green
146
+ "10-31" => "\e[38;5;208m" # Halloween orange
147
+ }
148
+
149
+ def self.day
150
+ @@day ||= begin
151
+ t = Date.today
152
+ "#{t.month}-#{t.day}"
153
+ end
143
154
  end
144
155
 
145
156
  def self.r
146
- "\e[31m"
157
+ @@r ||= HOLIDAY_COLORS[day] || "\e[1;31m"
147
158
  end
148
159
 
149
160
  def self.b
150
- "\e[30m"
161
+ @@b ||= HOLIDAY_COLORS[day] || "\e[30m"
162
+ end
163
+
164
+ def self.w
165
+ "\e[1;37m"
151
166
  end
152
167
 
153
168
  def self.reset
169
+ @@b = @@r = @@day = nil
154
170
  "\e[0m"
155
171
  end
156
172
 
@@ -163,7 +179,7 @@ module Sidekiq # :nodoc:
163
179
  #{w} ,$$$$$b#{b}/#{w}md$$$P^'
164
180
  #{w} .d$$$$$$#{b}/#{w}$$$P'
165
181
  #{w} $$^' `"#{b}/#{w}$$$' #{r}____ _ _ _ _
166
- #{w} $: ,$$: #{r} / ___|(_) __| | ___| | _(_) __ _
182
+ #{w} $: #{b}'#{w},$$: #{r} / ___|(_) __| | ___| | _(_) __ _
167
183
  #{w} `b :$$ #{r} \\___ \\| |/ _` |/ _ \\ |/ / |/ _` |
168
184
  #{w} $$: #{r} ___) | | (_| | __/ <| | (_| |
169
185
  #{w} $$ #{r}|____/|_|\\__,_|\\___|_|\\_\\_|\\__, |
@@ -214,6 +230,7 @@ module Sidekiq # :nodoc:
214
230
  # Both Sinatra 2.0+ and Sidekiq support this term.
215
231
  # RAILS_ENV and RACK_ENV are there for legacy support.
216
232
  @environment = cli_env || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
233
+ config[:environment] = @environment
217
234
  end
218
235
 
219
236
  def symbolize_keys_deep!(hash)
@@ -260,6 +277,18 @@ module Sidekiq # :nodoc:
260
277
 
261
278
  # merge with defaults
262
279
  @config.merge!(opts)
280
+
281
+ @config.default_capsule.tap do |cap|
282
+ cap.queues = opts[:queues]
283
+ cap.concurrency = opts[:concurrency] || @config[:concurrency]
284
+ end
285
+
286
+ opts[:capsules]&.each do |name, cap_config|
287
+ @config.capsule(name.to_s) do |cap|
288
+ cap.queues = cap_config[:queues]
289
+ cap.concurrency = cap_config[:concurrency]
290
+ end
291
+ end
263
292
  end
264
293
 
265
294
  def boot_application
@@ -267,12 +296,11 @@ module Sidekiq # :nodoc:
267
296
 
268
297
  if File.directory?(@config[:require])
269
298
  require "rails"
270
- if ::Rails::VERSION::MAJOR < 5
271
- raise "Sidekiq no longer supports this version of Rails"
272
- else
273
- require "sidekiq/rails"
274
- require File.expand_path("#{@config[:require]}/config/environment.rb")
299
+ if ::Rails::VERSION::MAJOR < 6
300
+ warn "Sidekiq #{Sidekiq::VERSION} only supports Rails 6+"
275
301
  end
302
+ require "sidekiq/rails"
303
+ require File.expand_path("#{@config[:require]}/config/environment.rb")
276
304
  @config[:tag] ||= default_tag
277
305
  else
278
306
  require @config[:require]
@@ -320,10 +348,6 @@ module Sidekiq # :nodoc:
320
348
  opts[:concurrency] = Integer(arg)
321
349
  end
322
350
 
323
- o.on "-d", "--daemon", "Daemonize process" do |arg|
324
- puts "ERROR: Daemonization mode was removed in Sidekiq 6.0, please use a proper process supervisor to start and manage your services"
325
- end
326
-
327
351
  o.on "-e", "--environment ENV", "Application environment" do |arg|
328
352
  opts[:environment] = arg
329
353
  end
@@ -333,8 +357,8 @@ module Sidekiq # :nodoc:
333
357
  end
334
358
 
335
359
  o.on "-q", "--queue QUEUE[,WEIGHT]", "Queues to process with optional weights" do |arg|
336
- queue, weight = arg.split(",")
337
- parse_queue opts, queue, weight
360
+ opts[:queues] ||= []
361
+ opts[:queues] << arg
338
362
  end
339
363
 
340
364
  o.on "-r", "--require [PATH|DIR]", "Location of Rails application with jobs or file to require" do |arg|
@@ -353,15 +377,7 @@ module Sidekiq # :nodoc:
353
377
  opts[:config_file] = arg
354
378
  end
355
379
 
356
- o.on "-L", "--logfile PATH", "path to writable logfile" do |arg|
357
- puts "ERROR: Logfile redirection was removed in Sidekiq 6.0, Sidekiq will only log to STDOUT"
358
- end
359
-
360
- o.on "-P", "--pidfile PATH", "path to pidfile" do |arg|
361
- puts "ERROR: PID file creation was removed in Sidekiq 6.0, please use a proper process supervisor to start and manage your services"
362
- end
363
-
364
- o.on "-V", "--version", "Print version and exit" do |arg|
380
+ o.on "-V", "--version", "Print version and exit" do
365
381
  puts "Sidekiq #{Sidekiq::VERSION}"
366
382
  die(0)
367
383
  end
@@ -381,9 +397,9 @@ module Sidekiq # :nodoc:
381
397
  end
382
398
 
383
399
  def parse_config(path)
384
- erb = ERB.new(File.read(path))
400
+ erb = ERB.new(File.read(path), trim_mode: "-")
385
401
  erb.filename = File.expand_path(path)
386
- opts = load_yaml(erb.result) || {}
402
+ opts = YAML.safe_load(erb.result, permitted_classes: [Symbol], aliases: true) || {}
387
403
 
388
404
  if opts.respond_to? :deep_symbolize_keys!
389
405
  opts.deep_symbolize_keys!
@@ -394,31 +410,9 @@ module Sidekiq # :nodoc:
394
410
  opts = opts.merge(opts.delete(environment.to_sym) || {})
395
411
  opts.delete(:strict)
396
412
 
397
- parse_queues(opts, opts.delete(:queues) || [])
398
-
399
413
  opts
400
414
  end
401
415
 
402
- def load_yaml(src)
403
- if Psych::VERSION > "4.0"
404
- YAML.safe_load(src, permitted_classes: [Symbol], aliases: true)
405
- else
406
- YAML.load(src)
407
- end
408
- end
409
-
410
- def parse_queues(opts, queues_and_weights)
411
- queues_and_weights.each { |queue_and_weight| parse_queue(opts, *queue_and_weight) }
412
- end
413
-
414
- def parse_queue(opts, queue, weight = nil)
415
- opts[:queues] ||= []
416
- opts[:strict] = true if opts[:strict].nil?
417
- raise ArgumentError, "queues: #{queue} cannot be defined twice" if opts[:queues].include?(queue)
418
- [weight.to_i, 1].max.times { opts[:queues] << queue.to_s }
419
- opts[:strict] = false if weight.to_i > 0
420
- end
421
-
422
416
  def rails_app?
423
417
  defined?(::Rails) && ::Rails.respond_to?(:application)
424
418
  end
@@ -426,4 +420,4 @@ module Sidekiq # :nodoc:
426
420
  end
427
421
 
428
422
  require "sidekiq/systemd"
429
- require "sidekiq/metrics/tracking" if ENV["SIDEKIQ_METRICS_BETA"]
423
+ require "sidekiq/metrics/tracking"
@@ -21,7 +21,6 @@ module Sidekiq
21
21
  # Sidekiq.client_middleware but you can change as necessary.
22
22
  #
23
23
  def middleware(&block)
24
- @chain ||= Sidekiq.client_middleware
25
24
  if block
26
25
  @chain = @chain.dup
27
26
  yield @chain
@@ -31,18 +30,32 @@ module Sidekiq
31
30
 
32
31
  attr_accessor :redis_pool
33
32
 
34
- # Sidekiq::Client normally uses the default Redis pool but you may
35
- # pass a custom ConnectionPool if you want to shard your
36
- # Sidekiq jobs across several Redis instances (for scalability
37
- # reasons, e.g.)
33
+ # Sidekiq::Client is responsible for pushing job payloads to Redis.
34
+ # Requires the :pool or :config keyword argument.
38
35
  #
39
- # Sidekiq::Client.new(ConnectionPool.new { Redis.new })
36
+ # Sidekiq::Client.new(pool: Sidekiq::RedisConnection.create)
40
37
  #
41
- # Generally this is only needed for very large Sidekiq installs processing
42
- # thousands of jobs per second. I don't recommend sharding unless you
43
- # cannot scale any other way (e.g. splitting your app into smaller apps).
44
- def initialize(redis_pool = nil)
45
- @redis_pool = redis_pool || Thread.current[:sidekiq_via_pool] || Sidekiq.redis_pool
38
+ # Inside the Sidekiq process, you can reuse the configured resources:
39
+ #
40
+ # Sidekiq::Client.new(config: config)
41
+ #
42
+ # @param pool [ConnectionPool] explicit Redis pool to use
43
+ # @param config [Sidekiq::Config] use the pool and middleware from the given Sidekiq container
44
+ # @param chain [Sidekiq::Middleware::Chain] use the given middleware chain
45
+ def initialize(*args, **kwargs)
46
+ if args.size == 1 && kwargs.size == 0
47
+ warn "Sidekiq::Client.new(pool) is deprecated, please use Sidekiq::Client.new(pool: pool), #{caller(0..3)}"
48
+ # old calling method, accept 1 pool argument
49
+ @redis_pool = args[0]
50
+ @chain = Sidekiq.default_configuration.client_middleware
51
+ @config = Sidekiq.default_configuration
52
+ else
53
+ # new calling method: keyword arguments
54
+ @config = kwargs[:config] || Sidekiq.default_configuration
55
+ @redis_pool = kwargs[:pool] || Thread.current[:sidekiq_redis_pool] || @config&.redis_pool
56
+ @chain = kwargs[:chain] || @config&.client_middleware
57
+ raise ArgumentError, "No Redis pool available for Sidekiq::Client" unless @redis_pool
58
+ end
46
59
  end
47
60
 
48
61
  ##
@@ -53,6 +66,7 @@ module Sidekiq
53
66
  # args - an array of simple arguments to the perform method, must be JSON-serializable
54
67
  # at - timestamp to schedule the job (optional), must be Numeric (e.g. Time.now.to_f)
55
68
  # retry - whether to retry this job if it fails, default true or an integer number of retries
69
+ # retry_for - relative amount of time to retry this job if it fails, default nil
56
70
  # backtrace - whether to save any error backtrace, default false
57
71
  #
58
72
  # If class is set to the class name, the jobs' options will be based on Sidekiq's default
@@ -83,8 +97,9 @@ module Sidekiq
83
97
 
84
98
  ##
85
99
  # Push a large number of jobs to Redis. This method cuts out the redis
86
- # network round trip latency. I wouldn't recommend pushing more than
87
- # 1000 per call but YMMV based on network quality, size of job args, etc.
100
+ # network round trip latency. It pushes jobs in batches if more than
101
+ # `:batch_size` (1000 by default) of jobs are passed. I wouldn't recommend making `:batch_size`
102
+ # larger than 1000 but YMMV based on network quality, size of job args, etc.
88
103
  # A large number of jobs can cause a bit of Redis command processing latency.
89
104
  #
90
105
  # Takes the same arguments as #push except that args is expected to be
@@ -92,13 +107,15 @@ module Sidekiq
92
107
  # is run through the client middleware pipeline and each job gets its own Job ID
93
108
  # as normal.
94
109
  #
95
- # Returns an array of the of pushed jobs' jids. The number of jobs pushed can be less
96
- # than the number given if the middleware stopped processing for one or more jobs.
110
+ # Returns an array of the of pushed jobs' jids, may contain nils if any client middleware
111
+ # prevented a job push.
112
+ #
113
+ # Example (pushing jobs in batches):
114
+ # push_bulk('class' => 'MyJob', 'args' => (1..100_000).to_a, batch_size: 1_000)
115
+ #
97
116
  def push_bulk(items)
117
+ batch_size = items.delete(:batch_size) || items.delete("batch_size") || 1_000
98
118
  args = items["args"]
99
- raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" unless args.is_a?(Array) && args.all?(Array)
100
- return [] if args.empty? # no jobs to push
101
-
102
119
  at = items.delete("at")
103
120
  raise ArgumentError, "Job 'at' must be a Numeric or an Array of Numeric timestamps" if at && (Array(at).empty? || !Array(at).all? { |entry| entry.is_a?(Numeric) })
104
121
  raise ArgumentError, "Job 'at' Array must have same size as 'args' Array" if at.is_a?(Array) && at.size != args.size
@@ -107,18 +124,26 @@ module Sidekiq
107
124
  raise ArgumentError, "Explicitly passing 'jid' when pushing more than one job is not supported" if jid && args.size > 1
108
125
 
109
126
  normed = normalize_item(items)
110
- payloads = args.map.with_index { |job_args, index|
111
- copy = normed.merge("args" => job_args, "jid" => SecureRandom.hex(12))
112
- copy["at"] = (at.is_a?(Array) ? at[index] : at) if at
113
- result = middleware.invoke(items["class"], copy, copy["queue"], @redis_pool) do
114
- verify_json(copy)
115
- copy
116
- end
117
- result || nil
118
- }.compact
127
+ result = args.each_slice(batch_size).flat_map do |slice|
128
+ raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" unless slice.is_a?(Array) && slice.all?(Array)
129
+ break [] if slice.empty? # no jobs to push
130
+
131
+ payloads = slice.map.with_index { |job_args, index|
132
+ copy = normed.merge("args" => job_args, "jid" => SecureRandom.hex(12))
133
+ copy["at"] = (at.is_a?(Array) ? at[index] : at) if at
134
+ result = middleware.invoke(items["class"], copy, copy["queue"], @redis_pool) do
135
+ verify_json(copy)
136
+ copy
137
+ end
138
+ result || nil
139
+ }
140
+
141
+ to_push = payloads.compact
142
+ raw_push(to_push) unless to_push.empty?
143
+ payloads.map { |payload| payload&.[]("jid") }
144
+ end
119
145
 
120
- raw_push(payloads) unless payloads.empty?
121
- payloads.collect { |payload| payload["jid"] }
146
+ result.is_a?(Enumerator::Lazy) ? result.force : result
122
147
  end
123
148
 
124
149
  # Allows sharding of jobs across any number of Redis instances. All jobs
@@ -135,11 +160,11 @@ module Sidekiq
135
160
  # you cannot scale any other way (e.g. splitting your app into smaller apps).
136
161
  def self.via(pool)
137
162
  raise ArgumentError, "No pool given" if pool.nil?
138
- current_sidekiq_pool = Thread.current[:sidekiq_via_pool]
139
- Thread.current[:sidekiq_via_pool] = pool
163
+ current_sidekiq_pool = Thread.current[:sidekiq_redis_pool]
164
+ Thread.current[:sidekiq_redis_pool] = pool
140
165
  yield
141
166
  ensure
142
- Thread.current[:sidekiq_via_pool] = current_sidekiq_pool
167
+ Thread.current[:sidekiq_redis_pool] = current_sidekiq_pool
143
168
  end
144
169
 
145
170
  class << self
@@ -147,8 +172,8 @@ module Sidekiq
147
172
  new.push(item)
148
173
  end
149
174
 
150
- def push_bulk(items)
151
- new.push_bulk(items)
175
+ def push_bulk(...)
176
+ new.push_bulk(...)
152
177
  end
153
178
 
154
179
  # Resque compatibility helpers. Note all helpers
@@ -176,7 +201,7 @@ module Sidekiq
176
201
  def enqueue_to_in(queue, interval, klass, *args)
177
202
  int = interval.to_f
178
203
  now = Time.now.to_f
179
- ts = (int < 1_000_000_000 ? now + int : int)
204
+ ts = ((int < 1_000_000_000) ? now + int : int)
180
205
 
181
206
  item = {"class" => klass, "args" => args, "at" => ts, "queue" => queue}
182
207
  item.delete("at") if ts <= now
@@ -201,14 +226,14 @@ module Sidekiq
201
226
  conn.pipelined do |pipeline|
202
227
  atomic_push(pipeline, payloads)
203
228
  end
204
- rescue RedisConnection.adapter::BaseError => ex
229
+ rescue RedisClient::Error => ex
205
230
  # 2550 Failover can cause the server to become a replica, need
206
231
  # to disconnect and reopen the socket to get back to the primary.
207
232
  # 4495 Use the same logic if we have a "Not enough replicas" error from the primary
208
233
  # 4985 Use the same logic when a blocking command is force-unblocked
209
234
  # The retry logic is copied from sidekiq.rb
210
235
  if retryable && ex.message =~ /READONLY|NOREPLICAS|UNBLOCKED/
211
- conn.disconnect!
236
+ conn.close
212
237
  retryable = false
213
238
  retry
214
239
  end
@@ -222,6 +247,8 @@ module Sidekiq
222
247
  if payloads.first.key?("at")
223
248
  conn.zadd("schedule", payloads.flat_map { |hash|
224
249
  at = hash.delete("at").to_s
250
+ # ActiveJob sets this but the job has not been enqueued yet
251
+ hash.delete("enqueued_at")
225
252
  [at, Sidekiq.dump_json(hash)]
226
253
  })
227
254
  else
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sidekiq
2
4
  ##
3
5
  # Sidekiq::Component assumes a config instance is available at @config
@@ -13,7 +15,7 @@ module Sidekiq
13
15
 
14
16
  def safe_thread(name, &block)
15
17
  Thread.new do
16
- Thread.current.name = name
18
+ Thread.current.name = "sidekiq.#{name}"
17
19
  watchdog(name, &block)
18
20
  end
19
21
  end
@@ -50,6 +52,7 @@ module Sidekiq
50
52
  oneshot = options.fetch(:oneshot, true)
51
53
  reverse = options[:reverse]
52
54
  reraise = options[:reraise]
55
+ logger.debug("Firing #{event} event") if oneshot
53
56
 
54
57
  arr = config[:lifecycle_events][event]
55
58
  arr.reverse! if reverse