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 +4 -4
- data/README.md +18 -6
- data/lib/sidekiq/worker_killer/version.rb +1 -1
- data/lib/sidekiq/worker_killer.rb +23 -3
- data/spec/sidekiq/worker_killer_spec.rb +187 -108
- metadata +21 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1335f7577f6a507808c59651cc9f479a49a9d54e144906b39b06355af0fcfcba
|
4
|
+
data.tar.gz: f61243439575d5ec2f75e0e7e803ff6588279b349a4ab9ebabc62e58fb3fcb7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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-
|
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
|
45
|
+
The following options can be overridden.
|
35
46
|
|
36
47
|
| Option | Defaults | Description |
|
37
48
|
| ------- | ------- | ----------- |
|
38
|
-
| max_rss | 0 MB (disabled) |
|
39
|
-
| grace_time | 900 seconds |
|
40
|
-
| shutdown_wait | 30 seconds |
|
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,12 +1,18 @@
|
|
1
1
|
require "get_process_mem"
|
2
2
|
require "sidekiq"
|
3
|
-
|
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
|
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
|
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 "#
|
18
|
-
let(:
|
19
|
-
|
20
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
29
|
-
|
67
|
+
describe "#call" do
|
68
|
+
let(:worker){ double("worker") }
|
69
|
+
let(:job){ double("job") }
|
70
|
+
let(:queue){ double("queue") }
|
30
71
|
|
31
|
-
|
32
|
-
|
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
|
-
|
36
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
47
|
-
|
85
|
+
it "should request shutdown" do
|
86
|
+
expect(subject).to receive(:request_shutdown)
|
87
|
+
subject.call(worker, job, queue){}
|
88
|
+
end
|
48
89
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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 "
|
66
|
-
|
67
|
-
it "should
|
68
|
-
|
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 "
|
74
|
-
|
75
|
-
it "should
|
76
|
-
expect(subject).
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
expect(
|
87
|
-
subject.
|
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
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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
|
-
|
137
|
-
|
208
|
+
allow(Process).to receive(:kill)
|
209
|
+
allow(Process).to receive(:pid).and_return(99)
|
138
210
|
|
139
|
-
|
211
|
+
subject.send(:request_shutdown).join
|
140
212
|
|
141
|
-
|
213
|
+
elapsed_time = shutdown_time - shutdown_request_time
|
142
214
|
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
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
|
-
|
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.
|
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:
|
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
|
-
|
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.
|
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:
|