sidekiq-worker-killer 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f40045c5805f35f8792f66c5dac896042c3b50b7b8c42f0f95e14827d4184a5a
4
- data.tar.gz: 5721815ad342ec06d6231c005b60cb0bbda5300343a4bfb99b9b138fff8f6627
3
+ metadata.gz: 1335f7577f6a507808c59651cc9f479a49a9d54e144906b39b06355af0fcfcba
4
+ data.tar.gz: f61243439575d5ec2f75e0e7e803ff6588279b349a4ab9ebabc62e58fb3fcb7e
5
5
  SHA512:
6
- metadata.gz: 4db165333c1a8465b15e514af6b178aec3091bdb90a2ced38b8131b98706922518b25632f45a65fa33b923ac6d2a42c3379dee4e4fde1dde9dd20d9a49aab1e1
7
- data.tar.gz: dd3e9e16d4bfbc5d753559e6b94c95b334ef2855864bcc7ef53f1577e40f6bc5122ad7040133c04d2c019ab37d532f012339fb18180e8efe74dc15274d427f49
6
+ metadata.gz: 97742129013c4374090ed58f35d9b407f6dc2b9ff44a23d4de187b7d3035c11961ebb089b0753bf7b1688d386d77b2aef7cceba4344dd614a2dd3181fc28a0c2
7
+ data.tar.gz: cbf8b55a9bbf3171f988c0eabb4a137bbe3e458f9aa8368cb2c1cbe3759a14ce4b6dd919b9568458e48ac473c7780f01ad72be437503ee8e3695e1b67613f59d
data/README.md CHANGED
@@ -5,7 +5,18 @@
5
5
 
6
6
  [Sidekiq](https://github.com/mperham/sidekiq) is probably the best background processing framework today. At the same time, memory leaks are very hard to tackle in Ruby and we often find ourselves with growing memory consumption. Instead of spending herculean effort fixing leaks, why not kill your processes when they got to be too large?
7
7
 
8
- Highly inspired by [Gitlab Sidekiq MemoryKiller](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/sidekiq_middleware/shutdown.rb) and [Noxa Sidekiq killer](https://github.com/Noxa/sidekiq-killer).
8
+ Highly inspired by [Gitlab Sidekiq MemoryKiller](https://gitlab.com/gitlab-org/gitlab-foss/-/blob/39c1731a53d1014eab7c876d70632b1abf738712/lib/gitlab/sidekiq_middleware/shutdown.rb) and [Noxa Sidekiq killer](https://github.com/Noxa/sidekiq-killer).
9
+
10
+ ---
11
+ **NOTE**
12
+
13
+ This gem needs to get the used memory of the Sidekiq process. For
14
+ this we use [GetProcessGem](https://github.com/schneems/get_process_mem),
15
+ but be aware that if you are running Sidekiq in Heroku(or any container) the
16
+ memory usage will
17
+ [not be accurate](https://github.com/schneems/get_process_mem/issues/7).
18
+
19
+ ---
9
20
 
10
21
  quick-refs: [install](#install) | [usage](#usage) | [available options](#available-options) | [development](#development)
11
22
 
@@ -31,16 +42,17 @@ end
31
42
 
32
43
  ## Available options
33
44
 
34
- The following options can be overrided.
45
+ The following options can be overridden.
35
46
 
36
47
  | Option | Defaults | Description |
37
48
  | ------- | ------- | ----------- |
38
- | max_rss | 0 MB (disabled) | max RSS in megabytes. Above this, shutdown will be triggered. |
39
- | grace_time | 900 seconds | when shutdown is triggered, the Sidekiq process will not accept new job and wait at most 15 minutes for running jobs to finish. If Float::INFINITY specified, will wait forever. |
40
- | shutdown_wait | 30 seconds | when the grace time expires, still running jobs get 30 seconds to stop. After that, kill signal is triggered. |
49
+ | max_rss | 0 MB (disabled) | Max RSS in megabytes used by the Sidekiq process. Above this, shutdown will be triggered. |
50
+ | grace_time | 900 seconds | When shutdown is triggered, the Sidekiq process will not accept new job and wait at most 15 minutes for running jobs to finish. If Float::INFINITY specified, will wait forever. |
51
+ | shutdown_wait | 30 seconds | When the grace time expires, still running jobs get 30 seconds to stop. After that, kill signal is triggered. |
41
52
  | kill_signal | SIGKILL | Signal to use to kill Sidekiq process if it doesn't stop. |
42
53
  | gc | true | Try to run garbage collection before Sidekiq process stops in case of exceeded max_rss. |
43
54
  | skip_shutdown_if | proc {false} | Executes a block of code after max_rss exceeds but before requesting shutdown. |
55
+ | on_shutdown | nil | Executes a block of code before just before requesting shutdown. This can be used to send custom logs or metrics to external services. |
44
56
 
45
57
  *skip_shutdown_if* is expected to return anything other than `false` or `nil` to skip shutdown.
46
58
 
@@ -75,4 +87,4 @@ See the list of [contributors](https://github.com/klaxit/sidekiq-worker-killer/c
75
87
 
76
88
  ## License
77
89
 
78
- Please see LICENSE
90
+ Please see [LICENSE](https://github.com/klaxit/sidekiq-worker-killer/blob/master/LICENSE)
@@ -1,7 +1,7 @@
1
1
  # rubocop:disable Style/ClassAndModuleChildren
2
2
  module Sidekiq
3
3
  class WorkerKiller
4
- VERSION = "1.0.0".freeze
4
+ VERSION = "1.1.0".freeze
5
5
  end
6
6
  end
7
7
  # rubocop:enable Style/ClassAndModuleChildren
@@ -1,12 +1,18 @@
1
1
  require "get_process_mem"
2
2
  require "sidekiq"
3
- require "sidekiq/util"
3
+ begin
4
+ require "sidekiq/util"
5
+ SidekiqComponent = Sidekiq::Util
6
+ rescue LoadError
7
+ require "sidekiq/middleware/modules"
8
+ SidekiqComponent = Sidekiq::ServerMiddleware
9
+ end
4
10
  require "sidekiq/api"
5
11
 
6
12
  # Sidekiq server middleware. Kill worker when the RSS memory exceeds limit
7
13
  # after a given grace time.
8
14
  class Sidekiq::WorkerKiller
9
- include Sidekiq::Util
15
+ include SidekiqComponent
10
16
 
11
17
  MUTEX = Mutex.new
12
18
 
@@ -30,6 +36,9 @@ class Sidekiq::WorkerKiller
30
36
  # @option options [Proc] skip_shutdown_if
31
37
  # Executes a block of code after max_rss exceeds but before requesting
32
38
  # shutdown. (default: `proc {false}`)
39
+ # @option options [Proc] on_shutdown
40
+ # Executes a block of code right before a shutdown happens.
41
+ # (default: `nil`)
33
42
  def initialize(options = {})
34
43
  @max_rss = options.fetch(:max_rss, 0)
35
44
  @grace_time = options.fetch(:grace_time, 15 * 60)
@@ -37,6 +46,7 @@ class Sidekiq::WorkerKiller
37
46
  @kill_signal = options.fetch(:kill_signal, "SIGKILL")
38
47
  @gc = options.fetch(:gc, true)
39
48
  @skip_shutdown = options.fetch(:skip_shutdown_if, proc { false })
49
+ @on_shutdown = options.fetch(:on_shutdown, nil)
40
50
  end
41
51
 
42
52
  # @param [String, Class] worker_class
@@ -62,17 +72,23 @@ class Sidekiq::WorkerKiller
62
72
 
63
73
  warn "current RSS #{current_rss} of #{identity} exceeds " \
64
74
  "maximum RSS #{@max_rss}"
75
+
76
+ run_shutdown_hook(worker, job, queue)
65
77
  request_shutdown
66
78
  end
67
79
 
68
80
  private
69
81
 
82
+ def run_shutdown_hook(worker, job, queue)
83
+ @on_shutdown.respond_to?(:call) && @on_shutdown.call(worker, job, queue)
84
+ end
85
+
70
86
  def skip_shutdown?(worker, job, queue)
71
87
  @skip_shutdown.respond_to?(:call) && @skip_shutdown.call(worker, job, queue)
72
88
  end
73
89
 
74
90
  def request_shutdown
75
- # In another thread to allow undelying job to finish
91
+ # In another thread to allow underlying job to finish
76
92
  Thread.new do
77
93
  # Only if another thread is not already
78
94
  # shutting down the Sidekiq process
@@ -125,6 +141,10 @@ class Sidekiq::WorkerKiller
125
141
  end || raise("No sidekiq worker with identity #{identity} found")
126
142
  end
127
143
 
144
+ def identity
145
+ config[:identity] || config["identity"]
146
+ end unless method_defined?(:identity)
147
+
128
148
  def warn(msg)
129
149
  Sidekiq.logger.warn(msg)
130
150
  end
@@ -7,156 +7,235 @@ describe Sidekiq::WorkerKiller do
7
7
  before do
8
8
  allow(subject).to receive(:warn) # silence "warn" logs
9
9
  allow(subject).to receive(:sleep) # reduces tests running time
10
- allow(Sidekiq::ProcessSet).to receive(:new) { sidekiq_process_set }
11
- allow(sidekiq_process_set).to receive(:find) { sidekiq_process }
12
10
  allow(sidekiq_process).to receive(:quiet!)
13
11
  allow(sidekiq_process).to receive(:stop!)
14
12
  allow(sidekiq_process).to receive(:stopping?)
15
13
  end
16
14
 
17
- describe "#call" do
18
- let(:worker){ double("worker") }
19
- let(:job){ double("job") }
20
- let(:queue){ double("queue") }
15
+ describe "#sidekiq_process" do
16
+ let(:identity) do
17
+ "foobar"
18
+ end
19
+ let(:mock_processes) do
20
+ [{
21
+ "identity" => identity,
22
+ "tag" => "baz",
23
+ "started_at" => Time.now,
24
+ "concurrency" => 5,
25
+ "busy" => 2,
26
+ "queues" => %w[low medium high]
27
+ }]
28
+ end
29
+ it "finds the process by identity" do
30
+ if subject.respond_to?(:config=)
31
+ rspec_info "variant 6.5+: Sidekiq::ServerMiddleware"
32
+ subject.config = mock_processes.first
33
+ else
34
+ rspec_info "variant pre 6.5: Sidekiq::Util"
35
+ allow(subject).to receive(:identity).and_return(identity)
36
+ end
37
+ allow(Sidekiq).to receive(:redis)
38
+ sidekiq_process_set = Sidekiq::ProcessSet.new
39
+ sidekiq_mock_processes = mock_processes.map {|mock_process|
40
+ Sidekiq::Process.new(mock_process)
41
+ }
42
+ allow(sidekiq_process_set).to receive(:each) {|&block|
43
+ sidekiq_mock_processes.each(&block)
44
+ }
45
+ expect(sidekiq_process_set.to_a.map(&:identity)).to eq([identity])
46
+ allow(Sidekiq::ProcessSet).to receive(:new).and_return(sidekiq_process_set)
47
+ expect(subject.send(:identity)).to eq(identity)
48
+ expect(subject.send(:sidekiq_process)).to be_a(Sidekiq::Process)
49
+ expect(subject.send(:sidekiq_process).identity).to eq(identity)
50
+ end
51
+ end
21
52
 
22
- it "should yield" do
23
- expect { |b|
24
- subject.call(worker, job, queue, &b)
25
- }.to yield_with_no_args
53
+ context "with stubbed sidekiq_process" do
54
+ before do
55
+ allow(Sidekiq::ProcessSet).to receive(:new) { sidekiq_process_set }
56
+ allow(sidekiq_process_set).to receive(:find) { sidekiq_process }
57
+ identity = "some-identity"
58
+ if subject.respond_to?(:config=)
59
+ rspec_info "variant 6.5+: Sidekiq::ServerMiddleware"
60
+ subject.config = { "identity" => identity }
61
+ else
62
+ rspec_info "variant pre 6.5: Sidekiq::Util"
63
+ allow(subject).to receive(:identity).and_return(identity)
64
+ end
26
65
  end
27
66
 
28
- context "when current rss is over max rss" do
29
- subject{ described_class.new(max_rss: 2) }
67
+ describe "#call" do
68
+ let(:worker){ double("worker") }
69
+ let(:job){ double("job") }
70
+ let(:queue){ double("queue") }
30
71
 
31
- before do
32
- allow(subject).to receive(:current_rss).and_return(3)
72
+ it "should yield" do
73
+ expect { |b|
74
+ subject.call(worker, job, queue, &b)
75
+ }.to yield_with_no_args
33
76
  end
34
77
 
35
- it "should request shutdown" do
36
- expect(subject).to receive(:request_shutdown)
37
- subject.call(worker, job, queue){}
38
- end
78
+ context "when current rss is over max rss" do
79
+ subject{ described_class.new(max_rss: 2) }
39
80
 
40
- it "should call garbage collect" do
41
- allow(subject).to receive(:request_shutdown)
42
- expect(GC).to receive(:start).with(full_mark: true, immediate_sweep: true)
43
- subject.call(worker, job, queue){}
44
- end
81
+ before do
82
+ allow(subject).to receive(:current_rss).and_return(3)
83
+ end
45
84
 
46
- context "and skip_shutdown_if is given" do
47
- subject{ described_class.new(max_rss: 2, skip_shutdown_if: skip_shutdown_proc) }
85
+ it "should request shutdown" do
86
+ expect(subject).to receive(:request_shutdown)
87
+ subject.call(worker, job, queue){}
88
+ end
48
89
 
49
- context "and skip_shutdown_if is a proc" do
50
- let(:skip_shutdown_proc) { proc { |worker| true } }
51
- it "should NOT request shutdown" do
52
- expect(subject).not_to receive(:request_shutdown)
53
- subject.call(worker, job, queue){}
90
+ it "should call garbage collect" do
91
+ allow(subject).to receive(:request_shutdown)
92
+ expect(GC).to receive(:start).with(full_mark: true, immediate_sweep: true)
93
+ subject.call(worker, job, queue){}
94
+ end
95
+
96
+ context "and skip_shutdown_if is given" do
97
+ subject{ described_class.new(max_rss: 2, skip_shutdown_if: skip_shutdown_proc) }
98
+
99
+ context "and skip_shutdown_if is a proc" do
100
+ let(:skip_shutdown_proc) { proc { |worker| true } }
101
+ it "should NOT request shutdown" do
102
+ expect(subject).not_to receive(:request_shutdown)
103
+ subject.call(worker, job, queue){}
104
+ end
105
+ end
106
+
107
+ context "and skip_shutdown_if is a lambda" do
108
+ let(:skip_shutdown_proc) { ->(worker, job, queue) { true } }
109
+ it "should NOT request shutdown" do
110
+ expect(subject).not_to receive(:request_shutdown)
111
+ subject.call(worker, job, queue){}
112
+ end
113
+ end
114
+
115
+ context "and skip_shutdown_if returns false" do
116
+ let(:skip_shutdown_proc) { proc { |worker, job, queue| false } }
117
+ it "should still request shutdown" do
118
+ expect(subject).to receive(:request_shutdown)
119
+ subject.call(worker, job, queue){}
120
+ end
121
+ end
122
+
123
+ context "and skip_shutdown_if returns nil" do
124
+ let(:skip_shutdown_proc) { proc { |worker, job, queue| nil } }
125
+ it "should still request shutdown" do
126
+ expect(subject).to receive(:request_shutdown)
127
+ subject.call(worker, job, queue){}
128
+ end
54
129
  end
55
130
  end
56
131
 
57
- context "and skip_shutdown_if is a lambda" do
58
- let(:skip_shutdown_proc) { ->(worker, job, queue) { true } }
59
- it "should NOT request shutdown" do
60
- expect(subject).not_to receive(:request_shutdown)
61
- subject.call(worker, job, queue){}
132
+ context "and on_shutdown is given" do
133
+ subject{ described_class.new(max_rss: 2, on_shutdown: on_shutdown_proc) }
134
+
135
+ context "and on_shutdown is a proc" do
136
+ let(:on_shutdown_proc) { proc { |worker, job, queue| nil } }
137
+ it "should execute on_shutdown hook" do
138
+ expect(subject).to receive(:request_shutdown).once
139
+ expect(on_shutdown_proc).to receive(:call).once
140
+ subject.call(worker, job, queue){}
141
+ end
142
+ end
143
+
144
+ context "and on_shutdown is a lambda" do
145
+ let(:on_shutdown_proc) { ->(worker, job, queue) { nil } }
146
+ it "should execute on_shutdown hook" do
147
+ expect(subject).to receive(:request_shutdown).once
148
+ expect(on_shutdown_proc).to receive(:call).once
149
+ subject.call(worker, job, queue){}
150
+ end
62
151
  end
63
152
  end
64
153
 
65
- context "and skip_shutdown_if returns false" do
66
- let(:skip_shutdown_proc) { proc { |worker, job, queue| false } }
67
- it "should still request shutdown" do
68
- expect(subject).to receive(:request_shutdown)
154
+ context "when gc is false" do
155
+ subject{ described_class.new(max_rss: 2, gc: false) }
156
+ it "should not call garbage collect" do
157
+ allow(subject).to receive(:request_shutdown)
158
+ expect(GC).not_to receive(:start)
69
159
  subject.call(worker, job, queue){}
70
160
  end
71
161
  end
72
162
 
73
- context "and skip_shutdown_if returns nil" do
74
- let(:skip_shutdown_proc) { proc { |worker, job, queue| nil } }
75
- it "should still request shutdown" do
76
- expect(subject).to receive(:request_shutdown)
163
+ context "but max rss is 0" do
164
+ subject{ described_class.new(max_rss: 0) }
165
+ it "should not request shutdown" do
166
+ expect(subject).to_not receive(:request_shutdown)
77
167
  subject.call(worker, job, queue){}
78
168
  end
79
169
  end
80
170
  end
171
+ end
81
172
 
82
- context "when gc is false" do
83
- subject{ described_class.new(max_rss: 2, gc: false) }
84
- it "should not call garbage collect" do
85
- allow(subject).to receive(:request_shutdown)
86
- expect(GC).not_to receive(:start)
87
- subject.call(worker, job, queue){}
173
+ describe "#request_shutdown" do
174
+ context "grace time is default" do
175
+ before { allow(subject).to receive(:shutdown){ sleep 0.01 } }
176
+ it "should call shutdown" do
177
+ expect(subject).to receive(:shutdown)
178
+ subject.send(:request_shutdown).join
88
179
  end
89
- end
90
-
91
- context "but max rss is 0" do
92
- subject{ described_class.new(max_rss: 0) }
93
- it "should not request shutdown" do
94
- expect(subject).to_not receive(:request_shutdown)
95
- subject.call(worker, job, queue){}
180
+ it "should not call shutdown twice when called concurrently" do
181
+ expect(subject).to receive(:shutdown).once
182
+ 2.times.map{ subject.send(:request_shutdown) }.each(&:join)
96
183
  end
97
184
  end
98
- end
99
- end
100
-
101
- describe "#request_shutdown" do
102
- context "grace time is default" do
103
- before { allow(subject).to receive(:shutdown){ sleep 0.01 } }
104
- it "should call shutdown" do
105
- expect(subject).to receive(:shutdown)
106
- subject.send(:request_shutdown).join
107
- end
108
- it "should not call shutdown twice when called concurrently" do
109
- expect(subject).to receive(:shutdown).once
110
- 2.times.map{ subject.send(:request_shutdown) }.each(&:join)
111
- end
112
- end
113
185
 
114
- context "grace time is 5 seconds" do
115
- subject{ described_class.new(max_rss: 2, grace_time: 5.0, shutdown_wait: 0) }
116
- it "should wait the specified grace time before calling shutdown" do
117
- # there are busy jobs that will not terminate within the grace time
118
- allow(subject).to receive(:no_jobs_on_quiet_processes?).and_return(false)
119
-
120
- shutdown_request_time = nil
121
- shutdown_time = nil
122
-
123
- # replace the original #request_shutdown to track
124
- # when the shutdown is requested
125
- original_request_shutdown = subject.method(:request_shutdown)
126
- allow(subject).to receive(:request_shutdown) do
127
- shutdown_request_time= Time.now
128
- original_request_shutdown.call
129
- end
186
+ context "grace time is 5 seconds" do
187
+ subject{ described_class.new(max_rss: 2, grace_time: 5.0, shutdown_wait: 0) }
188
+ it "should wait the specified grace time before calling shutdown" do
189
+ # there are busy jobs that will not terminate within the grace time
190
+ allow(subject).to receive(:no_jobs_on_quiet_processes?).and_return(false)
191
+
192
+ shutdown_request_time = nil
193
+ shutdown_time = nil
194
+
195
+ # replace the original #request_shutdown to track
196
+ # when the shutdown is requested
197
+ original_request_shutdown = subject.method(:request_shutdown)
198
+ allow(subject).to receive(:request_shutdown) do
199
+ shutdown_request_time= Time.now
200
+ original_request_shutdown.call
201
+ end
130
202
 
131
- # track when the process has been required to stop
132
- expect(sidekiq_process).to receive(:stop!) do |*args|
133
- shutdown_time = Time.now
134
- end
203
+ # track when the process has been required to stop
204
+ expect(sidekiq_process).to receive(:stop!) do |*args|
205
+ shutdown_time = Time.now
206
+ end
135
207
 
136
- allow(Process).to receive(:kill)
137
- allow(Process).to receive(:pid).and_return(99)
208
+ allow(Process).to receive(:kill)
209
+ allow(Process).to receive(:pid).and_return(99)
138
210
 
139
- subject.send(:request_shutdown).join
211
+ subject.send(:request_shutdown).join
140
212
 
141
- elapsed_time = shutdown_time - shutdown_request_time
213
+ elapsed_time = shutdown_time - shutdown_request_time
142
214
 
143
- # the elapsed time beetween shutdown request and the actual
144
- # shutdown signal should be greater than the specificed grace_time
145
- expect(elapsed_time).to be >= 5.0
215
+ # the elapsed time between shutdown request and the actual
216
+ # shutdown signal should be greater than the specified grace_time
217
+ expect(elapsed_time).to be >= 5.0
218
+ end
146
219
  end
147
- end
148
220
 
149
- context "grace time is Float::INFINITY" do
150
- subject{ described_class.new(max_rss: 2, grace_time: Float::INFINITY, shutdown_wait: 0) }
151
- it "call signal only on jobs" do
152
- allow(subject).to receive(:jobs_finished?).and_return(true)
153
- allow(Process).to receive(:pid).and_return(99)
154
- expect(sidekiq_process).to receive(:quiet!)
155
- expect(sidekiq_process).to receive(:stop!)
156
- expect(Process).to receive(:kill).with('SIGKILL', 99)
221
+ context "grace time is Float::INFINITY" do
222
+ subject{ described_class.new(max_rss: 2, grace_time: Float::INFINITY, shutdown_wait: 0) }
223
+ it "call signal only on jobs" do
224
+ allow(subject).to receive(:jobs_finished?).and_return(true)
225
+ allow(Process).to receive(:pid).and_return(99)
226
+ expect(sidekiq_process).to receive(:quiet!)
227
+ expect(sidekiq_process).to receive(:stop!)
228
+ expect(Process).to receive(:kill).with('SIGKILL', 99)
157
229
 
158
- subject.send(:request_shutdown).join
230
+ subject.send(:request_shutdown).join
231
+ end
159
232
  end
160
233
  end
161
234
  end
235
+
236
+ def rspec_info(msg)
237
+ is_printing_progress = RSpec.configuration.formatters.any? { |f| /ProgressFormatter/.match?(f.class.name) }
238
+ return if is_printing_progress
239
+ RSpec.configuration.reporter.message msg
240
+ end
162
241
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-worker-killer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cyrille Courtiere
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-28 00:00:00.000000000 Z
11
+ date: 2023-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: get_process_mem
@@ -66,7 +66,21 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.49.1
69
- description:
69
+ - !ruby/object:Gem::Dependency
70
+ name: appraisal
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
70
84
  email:
71
85
  - dev@klaxit.com
72
86
  executables: []
@@ -81,7 +95,7 @@ files:
81
95
  homepage: http://github.com/klaxit/sidekiq-worker-killer
82
96
  licenses: []
83
97
  metadata: {}
84
- post_install_message:
98
+ post_install_message:
85
99
  rdoc_options: []
86
100
  require_paths:
87
101
  - lib
@@ -96,8 +110,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
110
  - !ruby/object:Gem::Version
97
111
  version: '0'
98
112
  requirements: []
99
- rubygems_version: 3.1.0.pre3
100
- signing_key:
113
+ rubygems_version: 3.3.26
114
+ signing_key:
101
115
  specification_version: 4
102
116
  summary: Sidekiq worker killer
103
117
  test_files: