sidekiq 7.1.6 → 7.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +184 -0
  3. data/README.md +3 -3
  4. data/bin/multi_queue_bench +271 -0
  5. data/bin/sidekiqload +21 -12
  6. data/lib/active_job/queue_adapters/sidekiq_adapter.rb +75 -0
  7. data/lib/generators/sidekiq/job_generator.rb +2 -0
  8. data/lib/sidekiq/api.rb +139 -44
  9. data/lib/sidekiq/capsule.rb +8 -3
  10. data/lib/sidekiq/cli.rb +4 -1
  11. data/lib/sidekiq/client.rb +21 -1
  12. data/lib/sidekiq/component.rb +22 -0
  13. data/lib/sidekiq/config.rb +31 -7
  14. data/lib/sidekiq/deploy.rb +4 -2
  15. data/lib/sidekiq/embedded.rb +2 -0
  16. data/lib/sidekiq/fetch.rb +1 -1
  17. data/lib/sidekiq/iterable_job.rb +55 -0
  18. data/lib/sidekiq/job/interrupt_handler.rb +24 -0
  19. data/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
  20. data/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
  21. data/lib/sidekiq/job/iterable/enumerators.rb +135 -0
  22. data/lib/sidekiq/job/iterable.rb +294 -0
  23. data/lib/sidekiq/job.rb +14 -3
  24. data/lib/sidekiq/job_logger.rb +7 -6
  25. data/lib/sidekiq/job_retry.rb +9 -4
  26. data/lib/sidekiq/job_util.rb +2 -0
  27. data/lib/sidekiq/launcher.rb +7 -5
  28. data/lib/sidekiq/logger.rb +1 -1
  29. data/lib/sidekiq/metrics/query.rb +6 -1
  30. data/lib/sidekiq/metrics/shared.rb +15 -4
  31. data/lib/sidekiq/metrics/tracking.rb +20 -8
  32. data/lib/sidekiq/middleware/current_attributes.rb +46 -13
  33. data/lib/sidekiq/middleware/modules.rb +2 -0
  34. data/lib/sidekiq/monitor.rb +2 -1
  35. data/lib/sidekiq/paginator.rb +8 -2
  36. data/lib/sidekiq/processor.rb +21 -11
  37. data/lib/sidekiq/rails.rb +19 -3
  38. data/lib/sidekiq/redis_client_adapter.rb +24 -5
  39. data/lib/sidekiq/redis_connection.rb +36 -8
  40. data/lib/sidekiq/ring_buffer.rb +2 -0
  41. data/lib/sidekiq/scheduled.rb +2 -2
  42. data/lib/sidekiq/systemd.rb +2 -0
  43. data/lib/sidekiq/testing.rb +14 -8
  44. data/lib/sidekiq/transaction_aware_client.rb +7 -0
  45. data/lib/sidekiq/version.rb +5 -1
  46. data/lib/sidekiq/web/action.rb +26 -4
  47. data/lib/sidekiq/web/application.rb +53 -64
  48. data/lib/sidekiq/web/csrf_protection.rb +8 -5
  49. data/lib/sidekiq/web/helpers.rb +73 -27
  50. data/lib/sidekiq/web/router.rb +5 -2
  51. data/lib/sidekiq/web.rb +54 -2
  52. data/lib/sidekiq.rb +5 -3
  53. data/sidekiq.gemspec +3 -2
  54. data/web/assets/javascripts/application.js +26 -0
  55. data/web/assets/javascripts/dashboard-charts.js +37 -11
  56. data/web/assets/javascripts/dashboard.js +14 -10
  57. data/web/assets/javascripts/metrics.js +34 -0
  58. data/web/assets/stylesheets/application-rtl.css +10 -0
  59. data/web/assets/stylesheets/application.css +38 -3
  60. data/web/locales/en.yml +3 -1
  61. data/web/locales/fr.yml +0 -1
  62. data/web/locales/gd.yml +0 -1
  63. data/web/locales/it.yml +32 -1
  64. data/web/locales/ja.yml +0 -1
  65. data/web/locales/pt-br.yml +1 -2
  66. data/web/locales/tr.yml +100 -0
  67. data/web/locales/uk.yml +24 -1
  68. data/web/locales/zh-cn.yml +0 -1
  69. data/web/locales/zh-tw.yml +0 -1
  70. data/web/views/_footer.erb +12 -1
  71. data/web/views/_metrics_period_select.erb +1 -1
  72. data/web/views/_summary.erb +7 -7
  73. data/web/views/busy.erb +7 -7
  74. data/web/views/dashboard.erb +29 -36
  75. data/web/views/filtering.erb +4 -5
  76. data/web/views/layout.erb +6 -6
  77. data/web/views/metrics.erb +38 -30
  78. data/web/views/metrics_for_job.erb +29 -38
  79. data/web/views/morgue.erb +2 -2
  80. data/web/views/queue.erb +1 -1
  81. data/web/views/queues.erb +6 -2
  82. metadata +33 -13
@@ -1,10 +1,21 @@
1
- require "concurrent"
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
4
  module Metrics
5
- # This is the only dependency on concurrent-ruby in Sidekiq but it's
6
- # mandatory for thread-safety until MRI supports atomic operations on values.
7
- Counter = ::Concurrent::AtomicFixnum
5
+ class Counter
6
+ def initialize
7
+ @value = 0
8
+ @lock = Mutex.new
9
+ end
10
+
11
+ def increment
12
+ @lock.synchronize { @value += 1 }
13
+ end
14
+
15
+ def value
16
+ @lock.synchronize { @value }
17
+ end
18
+ end
8
19
 
9
20
  # Implements space-efficient but statistically useful histogram storage.
10
21
  # A precise time histogram stores every time. Instead we break times into a set of
@@ -31,11 +31,11 @@ module Sidekiq
31
31
  # We don't track time for failed jobs as they can have very unpredictable
32
32
  # execution times. more important to know average time for successful jobs so we
33
33
  # can better recognize when a perf regression is introduced.
34
- @lock.synchronize {
35
- @grams[klass].record_time(time_ms)
36
- @jobs["#{klass}|ms"] += time_ms
37
- @totals["ms"] += time_ms
38
- }
34
+ track_time(klass, time_ms)
35
+ rescue JobRetry::Skip
36
+ # This is raised when iterable job is interrupted.
37
+ track_time(klass, time_ms)
38
+ raise
39
39
  rescue Exception
40
40
  @lock.synchronize {
41
41
  @jobs["#{klass}|f"] += 1
@@ -100,15 +100,27 @@ module Sidekiq
100
100
 
101
101
  private
102
102
 
103
+ def track_time(klass, time_ms)
104
+ @lock.synchronize {
105
+ @grams[klass].record_time(time_ms)
106
+ @jobs["#{klass}|ms"] += time_ms
107
+ @totals["ms"] += time_ms
108
+ }
109
+ end
110
+
103
111
  def reset
104
112
  @lock.synchronize {
105
113
  array = [@totals, @jobs, @grams]
106
- @totals = Hash.new(0)
107
- @jobs = Hash.new(0)
108
- @grams = Hash.new { |hash, key| hash[key] = Histogram.new(key) }
114
+ reset_instance_variables
109
115
  array
110
116
  }
111
117
  end
118
+
119
+ def reset_instance_variables
120
+ @totals = Hash.new(0)
121
+ @jobs = Hash.new(0)
122
+ @grams = Hash.new { |hash, key| hash[key] = Histogram.new(key) }
123
+ end
112
124
  end
113
125
 
114
126
  class Middleware
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/current_attributes"
2
4
 
3
5
  module Sidekiq
@@ -31,11 +33,26 @@ module Sidekiq
31
33
  attrs = strklass.constantize.attributes
32
34
  # Retries can push the job N times, we don't
33
35
  # want retries to reset cattr. #5692, #5090
34
- job[key] = attrs if attrs.any?
36
+ if attrs.any?
37
+ # Older rails has a bug that `CurrentAttributes#attributes` always returns
38
+ # the same hash instance. We need to dup it to avoid being accidentally mutated.
39
+ job[key] = if returns_same_object?
40
+ attrs.dup
41
+ else
42
+ attrs
43
+ end
44
+ end
35
45
  end
36
46
  end
37
47
  yield
38
48
  end
49
+
50
+ private
51
+
52
+ def returns_same_object?
53
+ ActiveSupport::VERSION::MAJOR < 8 ||
54
+ (ActiveSupport::VERSION::MAJOR == 8 && ActiveSupport::VERSION::MINOR == 0)
55
+ end
39
56
  end
40
57
 
41
58
  class Load
@@ -46,22 +63,38 @@ module Sidekiq
46
63
  end
47
64
 
48
65
  def call(_, job, _, &block)
49
- cattrs_to_reset = []
66
+ klass_attrs = {}
50
67
 
51
68
  @cattrs.each do |(key, strklass)|
52
- if job.has_key?(key)
53
- constklass = strklass.constantize
54
- cattrs_to_reset << constklass
69
+ next unless job.has_key?(key)
55
70
 
56
- job[key].each do |(attribute, value)|
57
- constklass.public_send("#{attribute}=", value)
58
- end
59
- end
71
+ klass_attrs[strklass.constantize] = job[key]
60
72
  end
61
73
 
62
- yield
63
- ensure
64
- cattrs_to_reset.each(&:reset)
74
+ wrap(klass_attrs.to_a, &block)
75
+ end
76
+
77
+ private
78
+
79
+ def wrap(klass_attrs, &block)
80
+ klass, attrs = klass_attrs.shift
81
+ return block.call unless klass
82
+
83
+ retried = false
84
+
85
+ begin
86
+ klass.set(attrs) do
87
+ wrap(klass_attrs, &block)
88
+ end
89
+ rescue NoMethodError
90
+ raise if retried
91
+
92
+ # It is possible that the `CurrentAttributes` definition
93
+ # was changed before the job started processing.
94
+ attrs = attrs.select { |attr| klass.respond_to?(attr) }
95
+ retried = true
96
+ retry
97
+ end
65
98
  end
66
99
  end
67
100
 
@@ -70,7 +103,7 @@ module Sidekiq
70
103
  cattrs = build_cattrs_hash(klass_or_array)
71
104
 
72
105
  config.client_middleware.add Save, cattrs
73
- config.server_middleware.add Load, cattrs
106
+ config.server_middleware.prepend Load, cattrs
74
107
  end
75
108
 
76
109
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sidekiq
2
4
  # Server-side middleware must import this Module in order
3
5
  # to get access to server resources during `call`.
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "fileutils"
4
5
  require "sidekiq/api"
@@ -98,7 +99,7 @@ class Sidekiq::Monitor
98
99
  pad = opts[:pad] || 0
99
100
  max_length = opts[:max_length] || (80 - pad)
100
101
  out = []
101
- line = ""
102
+ line = +""
102
103
  values.each do |value|
103
104
  if (line.length + value.length) > max_length
104
105
  out << line
@@ -2,6 +2,12 @@
2
2
 
3
3
  module Sidekiq
4
4
  module Paginator
5
+ TYPE_CACHE = {
6
+ "dead" => "zset",
7
+ "retry" => "zset",
8
+ "schedule" => "zset"
9
+ }
10
+
5
11
  def page(key, pageidx = 1, page_size = 25, opts = nil)
6
12
  current_page = (pageidx.to_i < 1) ? 1 : pageidx.to_i
7
13
  pageidx = current_page - 1
@@ -19,9 +25,9 @@ module Sidekiq
19
25
  total_size, items = conn.multi { |transaction|
20
26
  transaction.zcard(key)
21
27
  if rev
22
- transaction.zrange(key, starting, ending, "REV", withscores: true)
28
+ transaction.zrange(key, starting, ending, "REV", "withscores")
23
29
  else
24
- transaction.zrange(key, starting, ending, withscores: true)
30
+ transaction.zrange(key, starting, ending, "withscores")
25
31
  end
26
32
  }
27
33
  [current_page, total_size, items]
@@ -36,7 +36,7 @@ module Sidekiq
36
36
  @job = nil
37
37
  @thread = nil
38
38
  @reloader = Sidekiq.default_configuration[:reloader]
39
- @job_logger = (capsule.config[:job_logger] || Sidekiq::JobLogger).new(logger)
39
+ @job_logger = (capsule.config[:job_logger] || Sidekiq::JobLogger).new(capsule.config)
40
40
  @retrier = Sidekiq::JobRetry.new(capsule)
41
41
  end
42
42
 
@@ -58,6 +58,10 @@ module Sidekiq
58
58
  @thread.value if wait
59
59
  end
60
60
 
61
+ def stopping?
62
+ @done
63
+ end
64
+
61
65
  def start
62
66
  @thread ||= safe_thread("#{config.name}/processor", &method(:run))
63
67
  end
@@ -134,10 +138,11 @@ module Sidekiq
134
138
  # Effectively this block denotes a "unit of work" to Rails.
135
139
  @reloader.call do
136
140
  klass = Object.const_get(job_hash["class"])
137
- inst = klass.new
138
- inst.jid = job_hash["jid"]
139
- @retrier.local(inst, jobstr, queue) do
140
- yield inst
141
+ instance = klass.new
142
+ instance.jid = job_hash["jid"]
143
+ instance._context = self
144
+ @retrier.local(instance, jobstr, queue) do
145
+ yield instance
141
146
  end
142
147
  end
143
148
  end
@@ -175,9 +180,9 @@ module Sidekiq
175
180
  ack = false
176
181
  Thread.handle_interrupt(IGNORE_SHUTDOWN_INTERRUPTS) do
177
182
  Thread.handle_interrupt(ALLOW_SHUTDOWN_INTERRUPTS) do
178
- dispatch(job_hash, queue, jobstr) do |inst|
179
- config.server_middleware.invoke(inst, job_hash, queue) do
180
- execute_job(inst, job_hash["args"])
183
+ dispatch(job_hash, queue, jobstr) do |instance|
184
+ config.server_middleware.invoke(instance, job_hash, queue) do
185
+ execute_job(instance, job_hash["args"])
181
186
  end
182
187
  end
183
188
  ack = true
@@ -185,9 +190,14 @@ module Sidekiq
185
190
  # Had to force kill this job because it didn't finish
186
191
  # within the timeout. Don't acknowledge the work since
187
192
  # we didn't properly finish it.
193
+ rescue Sidekiq::JobRetry::Skip => s
194
+ # Skip means we handled this error elsewhere. We don't
195
+ # need to log or report the error.
196
+ ack = true
197
+ raise s
188
198
  rescue Sidekiq::JobRetry::Handled => h
189
199
  # this is the common case: job raised error and Sidekiq::JobRetry::Handled
190
- # signals that we created a retry successfully. We can acknowlege the job.
200
+ # signals that we created a retry successfully. We can acknowledge the job.
191
201
  ack = true
192
202
  e = h.cause || h
193
203
  handle_exception(e, {context: "Job raised exception", job: job_hash})
@@ -206,8 +216,8 @@ module Sidekiq
206
216
  end
207
217
  end
208
218
 
209
- def execute_job(inst, cloned_args)
210
- inst.perform(*cloned_args)
219
+ def execute_job(instance, cloned_args)
220
+ instance.perform(*cloned_args)
211
221
  end
212
222
 
213
223
  # Ruby doesn't provide atomic counters out of the box so we'll
data/lib/sidekiq/rails.rb CHANGED
@@ -4,6 +4,17 @@ require "sidekiq/job"
4
4
  require "rails"
5
5
 
6
6
  module Sidekiq
7
+ module ActiveJob
8
+ # @api private
9
+ class Wrapper
10
+ include Sidekiq::Job
11
+
12
+ def perform(job_data)
13
+ ::ActiveJob::Base.execute(job_data.merge("provider_job_id" => jid))
14
+ end
15
+ end
16
+ end
17
+
7
18
  class Rails < ::Rails::Engine
8
19
  class Reloader
9
20
  def initialize(app = ::Rails.application)
@@ -20,6 +31,10 @@ module Sidekiq
20
31
  def inspect
21
32
  "#<Sidekiq::Rails::Reloader @app=#{@app.class.name}>"
22
33
  end
34
+
35
+ def to_hash
36
+ {app: @app.class.name}
37
+ end
23
38
  end
24
39
 
25
40
  # By including the Options module, we allow AJs to directly control sidekiq features
@@ -35,6 +50,7 @@ module Sidekiq
35
50
  # end
36
51
  initializer "sidekiq.active_job_integration" do
37
52
  ActiveSupport.on_load(:active_job) do
53
+ require_relative "../active_job/queue_adapters/sidekiq_adapter"
38
54
  include ::Sidekiq::Job::Options unless respond_to?(:sidekiq_options)
39
55
  end
40
56
  end
@@ -56,10 +72,10 @@ module Sidekiq
56
72
  # This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
57
73
  # it will appear in the Sidekiq console with all of the job context.
58
74
  unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
59
- if ::Rails::VERSION::STRING < "7.1"
60
- ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
61
- else
75
+ if ::Rails.logger.respond_to?(:broadcast_to)
62
76
  ::Rails.logger.broadcast_to(config.logger)
77
+ else
78
+ ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
63
79
  end
64
80
  end
65
81
  end
@@ -21,6 +21,22 @@ module Sidekiq
21
21
  @client.call("EVALSHA", sha, keys.size, *keys, *argv)
22
22
  end
23
23
 
24
+ # this is the set of Redis commands used by Sidekiq. Not guaranteed
25
+ # to be comprehensive, we use this as a performance enhancement to
26
+ # avoid calling method_missing on most commands
27
+ USED_COMMANDS = %w[bitfield bitfield_ro del exists expire flushdb
28
+ get hdel hget hgetall hincrby hlen hmget hset hsetnx incr incrby
29
+ lindex llen lmove lpop lpush lrange lrem mget mset ping pttl
30
+ publish rpop rpush sadd scard script set sismember smembers
31
+ srem ttl type unlink zadd zcard zincrby zrange zrem
32
+ zremrangebyrank zremrangebyscore]
33
+
34
+ USED_COMMANDS.each do |name|
35
+ define_method(name) do |*args, **kwargs|
36
+ @client.call(name, *args, **kwargs)
37
+ end
38
+ end
39
+
24
40
  private
25
41
 
26
42
  # this allows us to use methods like `conn.hmset(...)` instead of having to use
@@ -48,6 +64,13 @@ module Sidekiq
48
64
  opts = client_opts(options)
49
65
  @config = if opts.key?(:sentinels)
50
66
  RedisClient.sentinel(**opts)
67
+ elsif opts.key?(:nodes)
68
+ # Sidekiq does not support Redis clustering but Sidekiq Enterprise's
69
+ # rate limiters are cluster-safe so we can scale to millions
70
+ # of rate limiters using a Redis cluster. This requires the
71
+ # `redis-cluster-client` gem.
72
+ # Sidekiq::Limiter.redis = { nodes: [...] }
73
+ RedisClient.cluster(**opts)
51
74
  else
52
75
  RedisClient.config(**opts)
53
76
  end
@@ -74,13 +97,9 @@ module Sidekiq
74
97
  opts.delete(:network_timeout)
75
98
  end
76
99
 
77
- if opts[:driver]
78
- opts[:driver] = opts[:driver].to_sym
79
- end
80
-
81
100
  opts[:name] = opts.delete(:master_name) if opts.key?(:master_name)
82
101
  opts[:role] = opts[:role].to_sym if opts.key?(:role)
83
- opts.delete(:url) if opts.key?(:sentinels)
102
+ opts[:driver] = opts[:driver].to_sym if opts.key?(:driver)
84
103
 
85
104
  # Issue #3303, redis-rb will silently retry an operation.
86
105
  # This can lead to duplicate jobs if Sidekiq::Client's LPUSH
@@ -8,17 +8,28 @@ module Sidekiq
8
8
  module RedisConnection
9
9
  class << self
10
10
  def create(options = {})
11
- symbolized_options = options.transform_keys(&:to_sym)
11
+ symbolized_options = deep_symbolize_keys(options)
12
12
  symbolized_options[:url] ||= determine_redis_provider
13
13
 
14
14
  logger = symbolized_options.delete(:logger)
15
15
  logger&.info { "Sidekiq #{Sidekiq::VERSION} connecting to Redis with options #{scrub(symbolized_options)}" }
16
16
 
17
17
  raise "Sidekiq 7+ does not support Redis protocol 2" if symbolized_options[:protocol] == 2
18
+
19
+ safe = !!symbolized_options.delete(:cluster_safe)
20
+ raise ":nodes not allowed, Sidekiq is not safe to run on Redis Cluster" if !safe && symbolized_options.key?(:nodes)
21
+
18
22
  size = symbolized_options.delete(:size) || 5
19
23
  pool_timeout = symbolized_options.delete(:pool_timeout) || 1
20
24
  pool_name = symbolized_options.delete(:pool_name)
21
25
 
26
+ # Default timeout in redis-client is 1 second, which can be too aggressive
27
+ # if the Sidekiq process is CPU-bound. With 10-15 threads and a thread quantum of 100ms,
28
+ # it can be easy to get the occasional ReadTimeoutError. You can still provide
29
+ # a smaller timeout explicitly:
30
+ # config.redis = { url: "...", timeout: 1 }
31
+ symbolized_options[:timeout] ||= 3
32
+
22
33
  redis_config = Sidekiq::RedisClientAdapter.new(symbolized_options)
23
34
  ConnectionPool.new(timeout: pool_timeout, size: size, name: pool_name) do
24
35
  redis_config.new_client
@@ -27,6 +38,19 @@ module Sidekiq
27
38
 
28
39
  private
29
40
 
41
+ def deep_symbolize_keys(object)
42
+ case object
43
+ when Hash
44
+ object.each_with_object({}) do |(key, value), result|
45
+ result[key.to_sym] = deep_symbolize_keys(value)
46
+ end
47
+ when Array
48
+ object.map { |e| deep_symbolize_keys(e) }
49
+ else
50
+ object
51
+ end
52
+ end
53
+
30
54
  def scrub(options)
31
55
  redacted = "REDACTED"
32
56
 
@@ -39,11 +63,17 @@ module Sidekiq
39
63
  uri.password = redacted
40
64
  scrubbed_options[:url] = uri.to_s
41
65
  end
42
- if scrubbed_options[:password]
43
- scrubbed_options[:password] = redacted
44
- end
66
+ scrubbed_options[:password] = redacted if scrubbed_options[:password]
67
+ scrubbed_options[:sentinel_password] = redacted if scrubbed_options[:sentinel_password]
45
68
  scrubbed_options[:sentinels]&.each do |sentinel|
46
- sentinel[:password] = redacted if sentinel[:password]
69
+ if sentinel.is_a?(String)
70
+ if (uri = URI(sentinel)) && uri.password
71
+ uri.password = redacted
72
+ sentinel.replace(uri.to_s)
73
+ end
74
+ elsif sentinel[:password]
75
+ sentinel[:password] = redacted
76
+ end
47
77
  end
48
78
  scrubbed_options
49
79
  end
@@ -67,9 +97,7 @@ module Sidekiq
67
97
  EOM
68
98
  end
69
99
 
70
- ENV[
71
- p || "REDIS_URL"
72
- ]
100
+ ENV[p.to_s] || ENV["REDIS_URL"]
73
101
  end
74
102
  end
75
103
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "forwardable"
2
4
 
3
5
  module Sidekiq
@@ -144,7 +144,7 @@ module Sidekiq
144
144
  # In the example above, each process should schedule every 10 seconds on average. We special
145
145
  # case smaller clusters to add 50% so they would sleep somewhere between 5 and 15 seconds.
146
146
  # As we run more processes, the scheduling interval average will approach an even spread
147
- # between 0 and poll interval so we don't need this artifical boost.
147
+ # between 0 and poll interval so we don't need this artificial boost.
148
148
  #
149
149
  count = process_count
150
150
  interval = poll_interval_average(count)
@@ -193,7 +193,7 @@ module Sidekiq
193
193
  # should never depend on sidekiq/api.
194
194
  def cleanup
195
195
  # dont run cleanup more than once per minute
196
- return 0 unless redis { |conn| conn.set("process_cleanup", "1", nx: true, ex: 60) }
196
+ return 0 unless redis { |conn| conn.set("process_cleanup", "1", "NX", "EX", "60") }
197
197
 
198
198
  count = 0
199
199
  redis do |conn|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  #
2
4
  # Sidekiq's systemd integration allows Sidekiq to inform systemd:
3
5
  # 1. when it has successfully started
@@ -5,6 +5,7 @@ require "sidekiq"
5
5
 
6
6
  module Sidekiq
7
7
  class Testing
8
+ class TestModeAlreadySetError < RuntimeError; end
8
9
  class << self
9
10
  attr_accessor :__global_test_mode
10
11
 
@@ -12,8 +13,13 @@ module Sidekiq
12
13
  # all threads. Calling with a block only affects the current Thread.
13
14
  def __set_test_mode(mode)
14
15
  if block_given?
16
+ # Reentrant testing modes will lead to a rat's nest of code which is
17
+ # hard to reason about. You can set the testing mode once globally and
18
+ # you can override that global setting once per-thread.
19
+ raise TestModeAlreadySetError, "Nesting test modes is not supported" if __local_test_mode
20
+
21
+ self.__local_test_mode = mode
15
22
  begin
16
- self.__local_test_mode = mode
17
23
  yield
18
24
  ensure
19
25
  self.__local_test_mode = nil
@@ -106,7 +112,7 @@ module Sidekiq
106
112
  # The Queues class is only for testing the fake queue implementation.
107
113
  # There are 2 data structures involved in tandem. This is due to the
108
114
  # Rspec syntax of change(HardJob.jobs, :size). It keeps a reference
109
- # to the array. Because the array was dervied from a filter of the total
115
+ # to the array. Because the array was derived from a filter of the total
110
116
  # jobs enqueued, it appeared as though the array didn't change.
111
117
  #
112
118
  # To solve this, we'll keep 2 hashes containing the jobs. One with keys based
@@ -272,16 +278,16 @@ module Sidekiq
272
278
  def perform_one
273
279
  raise(EmptyQueueError, "perform_one called with empty job queue") if jobs.empty?
274
280
  next_job = jobs.first
275
- Queues.delete_for(next_job["jid"], queue, to_s)
281
+ Queues.delete_for(next_job["jid"], next_job["queue"], to_s)
276
282
  process_job(next_job)
277
283
  end
278
284
 
279
285
  def process_job(job)
280
- inst = new
281
- inst.jid = job["jid"]
282
- inst.bid = job["bid"] if inst.respond_to?(:bid=)
283
- Sidekiq::Testing.server_middleware.invoke(inst, job, job["queue"]) do
284
- execute_job(inst, job["args"])
286
+ instance = new
287
+ instance.jid = job["jid"]
288
+ instance.bid = job["bid"] if instance.respond_to?(:bid=)
289
+ Sidekiq::Testing.server_middleware.invoke(instance, job, job["queue"]) do
290
+ execute_job(instance, job["args"])
285
291
  end
286
292
  end
287
293
 
@@ -9,7 +9,14 @@ module Sidekiq
9
9
  @redis_client = Client.new(pool: pool, config: config)
10
10
  end
11
11
 
12
+ def batching?
13
+ Thread.current[:sidekiq_batch]
14
+ end
15
+
12
16
  def push(item)
17
+ # 6160 we can't support both Sidekiq::Batch and transactions.
18
+ return @redis_client.push(item) if batching?
19
+
13
20
  # pre-allocate the JID so we can return it immediately and
14
21
  # save it to the database as part of the transaction.
15
22
  item["jid"] ||= SecureRandom.hex(12)
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "7.1.6"
4
+ VERSION = "7.3.9"
5
5
  MAJOR = 7
6
+
7
+ def self.gem_version
8
+ Gem::Version.new(VERSION)
9
+ end
6
10
  end
@@ -22,6 +22,12 @@ module Sidekiq
22
22
  throw :halt, [302, {Web::LOCATION => "#{request.base_url}#{location}"}, []]
23
23
  end
24
24
 
25
+ def reload_page
26
+ current_location = request.referer.gsub(request.base_url, "")
27
+ redirect current_location
28
+ end
29
+
30
+ # deprecated, will warn in 8.0
25
31
  def params
26
32
  indifferent_hash = Hash.new { |hash, key| hash[key.to_s] if Symbol === key }
27
33
 
@@ -31,8 +37,19 @@ module Sidekiq
31
37
  indifferent_hash
32
38
  end
33
39
 
34
- def route_params
35
- env[WebRouter::ROUTE_PARAMS]
40
+ # Use like `url_params("page")` within your action blocks
41
+ def url_params(key)
42
+ request.params[key]
43
+ end
44
+
45
+ # Use like `route_params(:name)` within your action blocks
46
+ # key is required in 8.0, nil is only used for backwards compatibility
47
+ def route_params(key = nil)
48
+ if key
49
+ env[WebRouter::ROUTE_PARAMS][key]
50
+ else
51
+ env[WebRouter::ROUTE_PARAMS]
52
+ end
36
53
  end
37
54
 
38
55
  def session
@@ -42,8 +59,13 @@ module Sidekiq
42
59
  def erb(content, options = {})
43
60
  if content.is_a? Symbol
44
61
  unless respond_to?(:"_erb_#{content}")
45
- src = ERB.new(File.read("#{Web.settings.views}/#{content}.erb")).src
46
- WebAction.class_eval <<-RUBY, __FILE__, __LINE__ + 1
62
+ views = options[:views] || Web.settings.views
63
+ filename = "#{views}/#{content}.erb"
64
+ src = ERB.new(File.read(filename)).src
65
+
66
+ # Need to use lineno less by 1 because erb generates a
67
+ # comment before the source code.
68
+ WebAction.class_eval <<-RUBY, filename, -1 # standard:disable Style/EvalWithLocation
47
69
  def _erb_#{content}
48
70
  #{src}
49
71
  end