sidekiq 4.2.10 → 6.1.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

Files changed (106) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
  3. data/.github/workflows/ci.yml +41 -0
  4. data/.gitignore +2 -1
  5. data/.standard.yml +20 -0
  6. data/5.0-Upgrade.md +56 -0
  7. data/6.0-Upgrade.md +72 -0
  8. data/COMM-LICENSE +12 -10
  9. data/Changes.md +354 -1
  10. data/Ent-2.0-Upgrade.md +37 -0
  11. data/Ent-Changes.md +111 -3
  12. data/Gemfile +16 -21
  13. data/Gemfile.lock +192 -0
  14. data/LICENSE +1 -1
  15. data/Pro-4.0-Upgrade.md +35 -0
  16. data/Pro-5.0-Upgrade.md +25 -0
  17. data/Pro-Changes.md +181 -4
  18. data/README.md +19 -33
  19. data/Rakefile +6 -8
  20. data/bin/sidekiq +26 -2
  21. data/bin/sidekiqload +37 -34
  22. data/bin/sidekiqmon +8 -0
  23. data/lib/generators/sidekiq/templates/worker_spec.rb.erb +1 -1
  24. data/lib/generators/sidekiq/templates/worker_test.rb.erb +1 -1
  25. data/lib/generators/sidekiq/worker_generator.rb +21 -13
  26. data/lib/sidekiq.rb +86 -61
  27. data/lib/sidekiq/api.rb +320 -209
  28. data/lib/sidekiq/cli.rb +207 -217
  29. data/lib/sidekiq/client.rb +78 -51
  30. data/lib/sidekiq/delay.rb +41 -0
  31. data/lib/sidekiq/exception_handler.rb +12 -16
  32. data/lib/sidekiq/extensions/action_mailer.rb +13 -22
  33. data/lib/sidekiq/extensions/active_record.rb +13 -10
  34. data/lib/sidekiq/extensions/class_methods.rb +14 -11
  35. data/lib/sidekiq/extensions/generic_proxy.rb +10 -4
  36. data/lib/sidekiq/fetch.rb +29 -30
  37. data/lib/sidekiq/job_logger.rb +63 -0
  38. data/lib/sidekiq/job_retry.rb +262 -0
  39. data/lib/sidekiq/launcher.rb +102 -69
  40. data/lib/sidekiq/logger.rb +165 -0
  41. data/lib/sidekiq/manager.rb +16 -19
  42. data/lib/sidekiq/middleware/chain.rb +15 -5
  43. data/lib/sidekiq/middleware/i18n.rb +5 -7
  44. data/lib/sidekiq/monitor.rb +133 -0
  45. data/lib/sidekiq/paginator.rb +18 -14
  46. data/lib/sidekiq/processor.rb +161 -82
  47. data/lib/sidekiq/rails.rb +27 -100
  48. data/lib/sidekiq/redis_connection.rb +60 -20
  49. data/lib/sidekiq/scheduled.rb +61 -35
  50. data/lib/sidekiq/sd_notify.rb +149 -0
  51. data/lib/sidekiq/systemd.rb +24 -0
  52. data/lib/sidekiq/testing.rb +48 -28
  53. data/lib/sidekiq/testing/inline.rb +2 -1
  54. data/lib/sidekiq/util.rb +20 -16
  55. data/lib/sidekiq/version.rb +2 -1
  56. data/lib/sidekiq/web.rb +57 -57
  57. data/lib/sidekiq/web/action.rb +14 -14
  58. data/lib/sidekiq/web/application.rb +103 -84
  59. data/lib/sidekiq/web/csrf_protection.rb +158 -0
  60. data/lib/sidekiq/web/helpers.rb +126 -71
  61. data/lib/sidekiq/web/router.rb +18 -17
  62. data/lib/sidekiq/worker.rb +164 -41
  63. data/sidekiq.gemspec +15 -27
  64. data/web/assets/javascripts/application.js +25 -27
  65. data/web/assets/javascripts/dashboard.js +33 -37
  66. data/web/assets/stylesheets/application-dark.css +143 -0
  67. data/web/assets/stylesheets/application-rtl.css +246 -0
  68. data/web/assets/stylesheets/application.css +385 -10
  69. data/web/assets/stylesheets/bootstrap-rtl.min.css +9 -0
  70. data/web/assets/stylesheets/bootstrap.css +2 -2
  71. data/web/locales/ar.yml +81 -0
  72. data/web/locales/de.yml +14 -2
  73. data/web/locales/en.yml +4 -0
  74. data/web/locales/es.yml +4 -3
  75. data/web/locales/fa.yml +1 -0
  76. data/web/locales/fr.yml +2 -2
  77. data/web/locales/he.yml +79 -0
  78. data/web/locales/ja.yml +9 -4
  79. data/web/locales/lt.yml +83 -0
  80. data/web/locales/pl.yml +4 -4
  81. data/web/locales/ru.yml +4 -0
  82. data/web/locales/ur.yml +80 -0
  83. data/web/locales/vi.yml +83 -0
  84. data/web/views/_footer.erb +5 -2
  85. data/web/views/_job_info.erb +2 -1
  86. data/web/views/_nav.erb +4 -18
  87. data/web/views/_paging.erb +1 -1
  88. data/web/views/busy.erb +15 -8
  89. data/web/views/dashboard.erb +1 -1
  90. data/web/views/dead.erb +2 -2
  91. data/web/views/layout.erb +12 -2
  92. data/web/views/morgue.erb +9 -6
  93. data/web/views/queue.erb +18 -8
  94. data/web/views/queues.erb +11 -1
  95. data/web/views/retries.erb +14 -7
  96. data/web/views/retry.erb +2 -2
  97. data/web/views/scheduled.erb +7 -4
  98. metadata +41 -188
  99. data/.github/issue_template.md +0 -9
  100. data/.travis.yml +0 -18
  101. data/bin/sidekiqctl +0 -99
  102. data/lib/sidekiq/core_ext.rb +0 -119
  103. data/lib/sidekiq/logging.rb +0 -106
  104. data/lib/sidekiq/middleware/server/active_record.rb +0 -13
  105. data/lib/sidekiq/middleware/server/logging.rb +0 -31
  106. data/lib/sidekiq/middleware/server/retry_jobs.rb +0 -205
@@ -1,20 +1,28 @@
1
- # encoding: utf-8
2
1
  # frozen_string_literal: true
3
- require 'sidekiq/manager'
4
- require 'sidekiq/fetch'
5
- require 'sidekiq/scheduled'
2
+
3
+ require "sidekiq/manager"
4
+ require "sidekiq/fetch"
5
+ require "sidekiq/scheduled"
6
6
 
7
7
  module Sidekiq
8
- # The Launcher is a very simple Actor whose job is to
9
- # start, monitor and stop the core Actors in Sidekiq.
10
- # If any of these actors die, the Sidekiq process exits
11
- # immediately.
8
+ # The Launcher starts the Manager and Poller threads and provides the process heartbeat.
12
9
  class Launcher
13
10
  include Util
14
11
 
12
+ STATS_TTL = 5 * 365 * 24 * 60 * 60 # 5 years
13
+
14
+ PROCTITLES = [
15
+ proc { "sidekiq" },
16
+ proc { Sidekiq::VERSION },
17
+ proc { |me, data| data["tag"] },
18
+ proc { |me, data| "[#{Processor::WORKER_STATE.size} of #{data["concurrency"]} busy]" },
19
+ proc { |me, data| "stopping" if me.stopping? }
20
+ ]
21
+
15
22
  attr_accessor :manager, :poller, :fetcher
16
23
 
17
24
  def initialize(options)
25
+ options[:fetch] ||= BasicFetch.new(options)
18
26
  @manager = Sidekiq::Manager.new(options)
19
27
  @poller = Sidekiq::Scheduled::Poller.new
20
28
  @done = false
@@ -39,7 +47,7 @@ module Sidekiq
39
47
  # return until all work is complete and cleaned up.
40
48
  # It can take up to the timeout to complete.
41
49
  def stop
42
- deadline = Time.now + @options[:timeout]
50
+ deadline = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + @options[:timeout]
43
51
 
44
52
  @done = true
45
53
  @manager.quiet
@@ -49,7 +57,7 @@ module Sidekiq
49
57
 
50
58
  # Requeue everything in case there was a worker who grabbed work while stopped
51
59
  # This call is a no-op in Sidekiq but necessary for Sidekiq Pro.
52
- strategy = (@options[:fetch] || Sidekiq::BasicFetch)
60
+ strategy = @options[:fetch]
53
61
  strategy.bulk_requeue([], @options)
54
62
 
55
63
  clear_heartbeat
@@ -61,88 +69,128 @@ module Sidekiq
61
69
 
62
70
  private unless $TESTING
63
71
 
64
- JVM_RESERVED_SIGNALS = ['USR1', 'USR2'] # Don't Process#kill if we get these signals via the API
72
+ def start_heartbeat
73
+ loop do
74
+ heartbeat
75
+ sleep 5
76
+ end
77
+ Sidekiq.logger.info("Heartbeat stopping...")
78
+ end
79
+
80
+ def clear_heartbeat
81
+ # Remove record from Redis since we are shutting down.
82
+ # Note we don't stop the heartbeat thread; if the process
83
+ # doesn't actually exit, it'll reappear in the Web UI.
84
+ Sidekiq.redis do |conn|
85
+ conn.pipelined do
86
+ conn.srem("processes", identity)
87
+ conn.unlink("#{identity}:workers")
88
+ end
89
+ end
90
+ rescue
91
+ # best effort, ignore network errors
92
+ end
65
93
 
66
94
  def heartbeat
67
- results = Sidekiq::CLI::PROCTITLES.map {|x| x.(self, to_data) }
68
- results.compact!
69
- $0 = results.join(' ')
95
+ $0 = PROCTITLES.map { |proc| proc.call(self, to_data) }.compact.join(" ")
70
96
 
71
97
 
72
98
  end
73
99
 
100
+ def self.flush_stats
101
+ fails = Processor::FAILURE.reset
102
+ procd = Processor::PROCESSED.reset
103
+ return if fails + procd == 0
104
+
105
+ nowdate = Time.now.utc.strftime("%Y-%m-%d")
106
+ begin
107
+ Sidekiq.redis do |conn|
108
+ conn.pipelined do
109
+ conn.incrby("stat:processed", procd)
110
+ conn.incrby("stat:processed:#{nowdate}", procd)
111
+ conn.expire("stat:processed:#{nowdate}", STATS_TTL)
112
+
113
+ conn.incrby("stat:failed", fails)
114
+ conn.incrby("stat:failed:#{nowdate}", fails)
115
+ conn.expire("stat:failed:#{nowdate}", STATS_TTL)
116
+ end
117
+ end
118
+ rescue => ex
119
+ # we're exiting the process, things might be shut down so don't
120
+ # try to handle the exception
121
+ Sidekiq.logger.warn("Unable to flush stats: #{ex}")
122
+ end
123
+ end
124
+ at_exit(&method(:flush_stats))
125
+
74
126
  def ❤
75
127
  key = identity
76
128
  fails = procd = 0
129
+
77
130
  begin
78
- Processor::FAILURE.update {|curr| fails = curr; 0 }
79
- Processor::PROCESSED.update {|curr| procd = curr; 0 }
131
+ fails = Processor::FAILURE.reset
132
+ procd = Processor::PROCESSED.reset
133
+ curstate = Processor::WORKER_STATE.dup
134
+
135
+ workers_key = "#{key}:workers"
136
+ nowdate = Time.now.utc.strftime("%Y-%m-%d")
80
137
 
81
- workers_key = "#{key}:workers".freeze
82
- nowdate = Time.now.utc.strftime("%Y-%m-%d".freeze)
83
138
  Sidekiq.redis do |conn|
84
139
  conn.multi do
85
- conn.incrby("stat:processed".freeze, procd)
140
+ conn.incrby("stat:processed", procd)
86
141
  conn.incrby("stat:processed:#{nowdate}", procd)
87
- conn.incrby("stat:failed".freeze, fails)
142
+ conn.expire("stat:processed:#{nowdate}", STATS_TTL)
143
+
144
+ conn.incrby("stat:failed", fails)
88
145
  conn.incrby("stat:failed:#{nowdate}", fails)
89
- conn.del(workers_key)
90
- Processor::WORKER_STATE.each_pair do |tid, hash|
146
+ conn.expire("stat:failed:#{nowdate}", STATS_TTL)
147
+
148
+ conn.unlink(workers_key)
149
+ curstate.each_pair do |tid, hash|
91
150
  conn.hset(workers_key, tid, Sidekiq.dump_json(hash))
92
151
  end
93
152
  conn.expire(workers_key, 60)
94
153
  end
95
154
  end
155
+
96
156
  fails = procd = 0
97
157
 
98
- _, exists, _, _, msg = Sidekiq.redis do |conn|
99
- conn.multi do
100
- conn.sadd('processes', key)
101
- conn.exists(key)
102
- conn.hmset(key, 'info', to_json, 'busy', Processor::WORKER_STATE.size, 'beat', Time.now.to_f, 'quiet', @done)
158
+ _, exists, _, _, msg = Sidekiq.redis { |conn|
159
+ conn.multi {
160
+ conn.sadd("processes", key)
161
+ conn.exists?(key)
162
+ conn.hmset(key, "info", to_json, "busy", curstate.size, "beat", Time.now.to_f, "quiet", @done)
103
163
  conn.expire(key, 60)
104
164
  conn.rpop("#{key}-signals")
105
- end
106
- end
165
+ }
166
+ }
107
167
 
108
168
  # first heartbeat or recovering from an outage and need to reestablish our heartbeat
109
- fire_event(:heartbeat) if !exists
169
+ fire_event(:heartbeat) unless exists
110
170
 
111
171
  return unless msg
112
172
 
113
- if JVM_RESERVED_SIGNALS.include?(msg)
114
- Sidekiq::CLI.instance.handle_signal(msg)
115
- else
116
- ::Process.kill(msg, $$)
117
- end
173
+ ::Process.kill(msg, ::Process.pid)
118
174
  rescue => e
119
175
  # ignore all redis/network issues
120
- logger.error("heartbeat: #{e.message}")
176
+ logger.error("heartbeat: #{e}")
121
177
  # don't lose the counts if there was a network issue
122
- Processor::PROCESSED.increment(procd)
123
- Processor::FAILURE.increment(fails)
178
+ Processor::PROCESSED.incr(procd)
179
+ Processor::FAILURE.incr(fails)
124
180
  end
125
181
  end
126
182
 
127
- def start_heartbeat
128
- while true
129
- heartbeat
130
- sleep 5
131
- end
132
- Sidekiq.logger.info("Heartbeat stopping...")
133
- end
134
-
135
183
  def to_data
136
184
  @data ||= begin
137
185
  {
138
- 'hostname' => hostname,
139
- 'started_at' => Time.now.to_f,
140
- 'pid' => $$,
141
- 'tag' => @options[:tag] || '',
142
- 'concurrency' => @options[:concurrency],
143
- 'queues' => @options[:queues].uniq,
144
- 'labels' => @options[:labels],
145
- 'identity' => identity,
186
+ "hostname" => hostname,
187
+ "started_at" => Time.now.to_f,
188
+ "pid" => ::Process.pid,
189
+ "tag" => @options[:tag] || "",
190
+ "concurrency" => @options[:concurrency],
191
+ "queues" => @options[:queues].uniq,
192
+ "labels" => @options[:labels],
193
+ "identity" => identity
146
194
  }
147
195
  end
148
196
  end
@@ -154,20 +202,5 @@ module Sidekiq
154
202
  Sidekiq.dump_json(to_data)
155
203
  end
156
204
  end
157
-
158
- def clear_heartbeat
159
- # Remove record from Redis since we are shutting down.
160
- # Note we don't stop the heartbeat thread; if the process
161
- # doesn't actually exit, it'll reappear in the Web UI.
162
- Sidekiq.redis do |conn|
163
- conn.pipelined do
164
- conn.srem('processes', identity)
165
- conn.del("#{identity}:workers")
166
- end
167
- end
168
- rescue
169
- # best effort, ignore network errors
170
- end
171
-
172
205
  end
173
206
  end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+ require "time"
5
+
6
+ module Sidekiq
7
+ module Context
8
+ def self.with(hash)
9
+ current.merge!(hash)
10
+ yield
11
+ ensure
12
+ hash.each_key { |key| current.delete(key) }
13
+ end
14
+
15
+ def self.current
16
+ Thread.current[:sidekiq_context] ||= {}
17
+ end
18
+ end
19
+
20
+ module LoggingUtils
21
+ LEVELS = {
22
+ "debug" => 0,
23
+ "info" => 1,
24
+ "warn" => 2,
25
+ "error" => 3,
26
+ "fatal" => 4
27
+ }
28
+ LEVELS.default_proc = proc do |_, level|
29
+ Sidekiq.logger.warn("Invalid log level: #{level.inspect}")
30
+ nil
31
+ end
32
+
33
+ def debug?
34
+ level <= 0
35
+ end
36
+
37
+ def info?
38
+ level <= 1
39
+ end
40
+
41
+ def warn?
42
+ level <= 2
43
+ end
44
+
45
+ def error?
46
+ level <= 3
47
+ end
48
+
49
+ def fatal?
50
+ level <= 4
51
+ end
52
+
53
+ def local_level
54
+ Thread.current[:sidekiq_log_level]
55
+ end
56
+
57
+ def local_level=(level)
58
+ case level
59
+ when Integer
60
+ Thread.current[:sidekiq_log_level] = level
61
+ when Symbol, String
62
+ Thread.current[:sidekiq_log_level] = LEVELS[level.to_s]
63
+ when nil
64
+ Thread.current[:sidekiq_log_level] = nil
65
+ else
66
+ raise ArgumentError, "Invalid log level: #{level.inspect}"
67
+ end
68
+ end
69
+
70
+ def level
71
+ local_level || super
72
+ end
73
+
74
+ # Change the thread-local level for the duration of the given block.
75
+ def log_at(level)
76
+ old_local_level = local_level
77
+ self.local_level = level
78
+ yield
79
+ ensure
80
+ self.local_level = old_local_level
81
+ end
82
+
83
+ # Redefined to check severity against #level, and thus the thread-local level, rather than +@level+.
84
+ # FIXME: Remove when the minimum Ruby version supports overriding Logger#level.
85
+ def add(severity, message = nil, progname = nil, &block)
86
+ severity ||= ::Logger::UNKNOWN
87
+ progname ||= @progname
88
+
89
+ return true if @logdev.nil? || severity < level
90
+
91
+ if message.nil?
92
+ if block_given?
93
+ message = yield
94
+ else
95
+ message = progname
96
+ progname = @progname
97
+ end
98
+ end
99
+
100
+ @logdev.write format_message(format_severity(severity), Time.now, progname, message)
101
+ end
102
+ end
103
+
104
+ class Logger < ::Logger
105
+ include LoggingUtils
106
+
107
+ def initialize(*args, **kwargs)
108
+ super
109
+ self.formatter = Sidekiq.log_formatter
110
+ end
111
+
112
+ module Formatters
113
+ class Base < ::Logger::Formatter
114
+ def tid
115
+ Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
116
+ end
117
+
118
+ def ctx
119
+ Sidekiq::Context.current
120
+ end
121
+
122
+ def format_context
123
+ if ctx.any?
124
+ " " + ctx.compact.map { |k, v|
125
+ case v
126
+ when Array
127
+ "#{k}=#{v.join(",")}"
128
+ else
129
+ "#{k}=#{v}"
130
+ end
131
+ }.join(" ")
132
+ end
133
+ end
134
+ end
135
+
136
+ class Pretty < Base
137
+ def call(severity, time, program_name, message)
138
+ "#{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context} #{severity}: #{message}\n"
139
+ end
140
+ end
141
+
142
+ class WithoutTimestamp < Pretty
143
+ def call(severity, time, program_name, message)
144
+ "pid=#{::Process.pid} tid=#{tid}#{format_context} #{severity}: #{message}\n"
145
+ end
146
+ end
147
+
148
+ class JSON < Base
149
+ def call(severity, time, program_name, message)
150
+ hash = {
151
+ ts: time.utc.iso8601(3),
152
+ pid: ::Process.pid,
153
+ tid: tid,
154
+ lvl: severity,
155
+ msg: message
156
+ }
157
+ c = ctx
158
+ hash["ctx"] = c unless c.empty?
159
+
160
+ Sidekiq.dump_json(hash) << "\n"
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -1,13 +1,11 @@
1
- # encoding: utf-8
2
1
  # frozen_string_literal: true
3
- require 'sidekiq/util'
4
- require 'sidekiq/processor'
5
- require 'sidekiq/fetch'
6
- require 'thread'
7
- require 'set'
8
2
 
9
- module Sidekiq
3
+ require "sidekiq/util"
4
+ require "sidekiq/processor"
5
+ require "sidekiq/fetch"
6
+ require "set"
10
7
 
8
+ module Sidekiq
11
9
  ##
12
10
  # The Manager is the central coordination point in Sidekiq, controlling
13
11
  # the lifecycle of the Processors.
@@ -28,16 +26,16 @@ module Sidekiq
28
26
  attr_reader :workers
29
27
  attr_reader :options
30
28
 
31
- def initialize(options={})
29
+ def initialize(options = {})
32
30
  logger.debug { options.inspect }
33
31
  @options = options
34
- @count = options[:concurrency] || 25
32
+ @count = options[:concurrency] || 10
35
33
  raise ArgumentError, "Concurrency of #{@count} is not supported" if @count < 1
36
34
 
37
35
  @done = false
38
36
  @workers = Set.new
39
37
  @count.times do
40
- @workers << Processor.new(self)
38
+ @workers << Processor.new(self, options)
41
39
  end
42
40
  @plock = Mutex.new
43
41
  end
@@ -54,15 +52,15 @@ module Sidekiq
54
52
 
55
53
  logger.info { "Terminating quiet workers" }
56
54
  @workers.each { |x| x.terminate }
57
- fire_event(:quiet, true)
55
+ fire_event(:quiet, reverse: true)
58
56
  end
59
57
 
60
58
  # hack for quicker development / testing environment #2774
61
- PAUSE_TIME = STDOUT.tty? ? 0.1 : 0.5
59
+ PAUSE_TIME = $stdout.tty? ? 0.1 : 0.5
62
60
 
63
61
  def stop(deadline)
64
62
  quiet
65
- fire_event(:shutdown, true)
63
+ fire_event(:shutdown, reverse: true)
66
64
 
67
65
  # some of the shutdown events can be async,
68
66
  # we don't have any way to know when they're done but
@@ -71,11 +69,11 @@ module Sidekiq
71
69
  return if @workers.empty?
72
70
 
73
71
  logger.info { "Pausing to allow workers to finish..." }
74
- remaining = deadline - Time.now
72
+ remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
75
73
  while remaining > PAUSE_TIME
76
74
  return if @workers.empty?
77
75
  sleep PAUSE_TIME
78
- remaining = deadline - Time.now
76
+ remaining = deadline - ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
79
77
  end
80
78
  return if @workers.empty?
81
79
 
@@ -92,7 +90,7 @@ module Sidekiq
92
90
  @plock.synchronize do
93
91
  @workers.delete(processor)
94
92
  unless @done
95
- p = Processor.new(self)
93
+ p = Processor.new(self, options)
96
94
  @workers << p
97
95
  p.start
98
96
  end
@@ -114,7 +112,7 @@ module Sidekiq
114
112
  end
115
113
 
116
114
  if cleanup.size > 0
117
- jobs = cleanup.map {|p| p.job }.compact
115
+ jobs = cleanup.map { |p| p.job }.compact
118
116
 
119
117
  logger.warn { "Terminating #{cleanup.size} busy worker threads" }
120
118
  logger.warn { "Work still in progress #{jobs.inspect}" }
@@ -125,7 +123,7 @@ module Sidekiq
125
123
  # contract says that jobs are run AT LEAST once. Process termination
126
124
  # is delayed until we're certain the jobs are back in Redis because
127
125
  # it is worse to lose a job than to run it twice.
128
- strategy = (@options[:fetch] || Sidekiq::BasicFetch)
126
+ strategy = @options[:fetch]
129
127
  strategy.bulk_requeue(jobs, @options)
130
128
  end
131
129
 
@@ -133,6 +131,5 @@ module Sidekiq
133
131
  processor.kill
134
132
  end
135
133
  end
136
-
137
134
  end
138
135
  end