sidekiq-worker-killer 0.4.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +34 -8
- data/lib/sidekiq/worker_killer/version.rb +1 -1
- data/lib/sidekiq/worker_killer.rb +65 -35
- data/spec/sidekiq/worker_killer_spec.rb +184 -90
- metadata +19 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f8573256c162630662fbb9be660375da63c3c8cbe3fb11d9851e36b1c0456eff
|
4
|
+
data.tar.gz: 63a6bea822d499f22f283f704f80e0954ac006ed442f6cb7632fa204602051f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1fe7ab3d39860cbd742af569a3e5b3a37173bbdffa751ebc76aae1cbf67d09a5745991e8ad9c5b50671705b110abeab3ee4124e69c3ebbccc658579409cbd6d
|
7
|
+
data.tar.gz: f65acd7d78a86152a2d04cd5c4ef0046885366540c362fe0a8c61caa4f1d04d4263f338bd9769c00ea09530151b9a8da48f34cf71377ecfec6517c4c36075d80
|
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,15 +42,30 @@ 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 |
|
41
|
-
| kill_signal | SIGKILL | Signal to use kill Sidekiq process if it doesn't
|
42
|
-
| gc | true | Try to run garbage collection before Sidekiq process
|
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. |
|
52
|
+
| kill_signal | SIGKILL | Signal to use to kill Sidekiq process if it doesn't stop. |
|
53
|
+
| gc | true | Try to run garbage collection before Sidekiq process stops in case of exceeded max_rss. |
|
54
|
+
| skip_shutdown_if | proc {false} | Executes a block of code after max_rss exceeds but before requesting shutdown. |
|
55
|
+
|
56
|
+
*skip_shutdown_if* is expected to return anything other than `false` or `nil` to skip shutdown.
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
require 'sidekiq/worker_killer'
|
60
|
+
|
61
|
+
Sidekiq.configure_server do |config|
|
62
|
+
config.server_middleware do |chain|
|
63
|
+
chain.add Sidekiq::WorkerKiller, max_rss: 480, skip_shutdown_if: ->(worker, job, queue) do
|
64
|
+
worker.to_s == 'LongWorker'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
43
69
|
|
44
70
|
## Development
|
45
71
|
|
@@ -60,4 +86,4 @@ See the list of [contributors](https://github.com/klaxit/sidekiq-worker-killer/c
|
|
60
86
|
|
61
87
|
## License
|
62
88
|
|
63
|
-
Please see LICENSE
|
89
|
+
Please see [LICENSE](https://github.com/klaxit/sidekiq-worker-killer/blob/master/LICENSE)
|
@@ -1,30 +1,71 @@
|
|
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
|
10
|
+
require "sidekiq/api"
|
4
11
|
|
5
12
|
# Sidekiq server middleware. Kill worker when the RSS memory exceeds limit
|
6
13
|
# after a given grace time.
|
7
14
|
class Sidekiq::WorkerKiller
|
8
|
-
include
|
15
|
+
include SidekiqComponent
|
9
16
|
|
10
17
|
MUTEX = Mutex.new
|
11
18
|
|
19
|
+
# @param [Hash] options
|
20
|
+
# @option options [Integer] max_rss
|
21
|
+
# Max RSS in MB. Above this, shutdown will be triggered.
|
22
|
+
# (default: `0` (disabled))
|
23
|
+
# @option options [Integer] grace_time
|
24
|
+
# When shutdown is triggered, the Sidekiq process will not accept new job
|
25
|
+
# and wait at most 15 minutes for running jobs to finish.
|
26
|
+
# If Float::INFINITY is specified, will wait forever. (default: `900`)
|
27
|
+
# @option options [Integer] shutdown_wait
|
28
|
+
# when the grace time expires, still running jobs get 30 seconds to
|
29
|
+
# stop. After that, kill signal is triggered. (default: `30`)
|
30
|
+
# @option options [String] kill_signal
|
31
|
+
# Signal to use to kill Sidekiq process if it doesn't stop.
|
32
|
+
# (default: `"SIGKILL"`)
|
33
|
+
# @option options [Boolean] gc
|
34
|
+
# Try to run garbage collection before Sidekiq process stops in case
|
35
|
+
# of exceeded max_rss. (default: `true`)
|
36
|
+
# @option options [Proc] skip_shutdown_if
|
37
|
+
# Executes a block of code after max_rss exceeds but before requesting
|
38
|
+
# shutdown. (default: `proc {false}`)
|
12
39
|
def initialize(options = {})
|
13
40
|
@max_rss = options.fetch(:max_rss, 0)
|
14
41
|
@grace_time = options.fetch(:grace_time, 15 * 60)
|
15
42
|
@shutdown_wait = options.fetch(:shutdown_wait, 30)
|
16
43
|
@kill_signal = options.fetch(:kill_signal, "SIGKILL")
|
17
44
|
@gc = options.fetch(:gc, true)
|
45
|
+
@skip_shutdown = options.fetch(:skip_shutdown_if, proc { false })
|
18
46
|
end
|
19
47
|
|
20
|
-
|
48
|
+
# @param [String, Class] worker_class
|
49
|
+
# the string or class of the worker class being enqueued
|
50
|
+
# @param [Hash] job
|
51
|
+
# the full job payload
|
52
|
+
# @see https://github.com/mperham/sidekiq/wiki/Job-Format
|
53
|
+
# @param [String] queue
|
54
|
+
# the name of the queue the job was pulled from
|
55
|
+
# @yield the next middleware in the chain or the enqueuing of the job
|
56
|
+
def call(worker, job, queue)
|
21
57
|
yield
|
22
58
|
# Skip if the max RSS is not exceeded
|
23
59
|
return unless @max_rss > 0
|
24
60
|
return unless current_rss > @max_rss
|
25
61
|
GC.start(full_mark: true, immediate_sweep: true) if @gc
|
26
62
|
return unless current_rss > @max_rss
|
27
|
-
|
63
|
+
if skip_shutdown?(worker, job, queue)
|
64
|
+
warn "current RSS #{current_rss} exceeds maximum RSS #{@max_rss}, " \
|
65
|
+
"however shutdown will be ignored"
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
28
69
|
warn "current RSS #{current_rss} of #{identity} exceeds " \
|
29
70
|
"maximum RSS #{@max_rss}"
|
30
71
|
request_shutdown
|
@@ -32,6 +73,10 @@ class Sidekiq::WorkerKiller
|
|
32
73
|
|
33
74
|
private
|
34
75
|
|
76
|
+
def skip_shutdown?(worker, job, queue)
|
77
|
+
@skip_shutdown.respond_to?(:call) && @skip_shutdown.call(worker, job, queue)
|
78
|
+
end
|
79
|
+
|
35
80
|
def request_shutdown
|
36
81
|
# In another thread to allow undelying job to finish
|
37
82
|
Thread.new do
|
@@ -42,30 +87,28 @@ class Sidekiq::WorkerKiller
|
|
42
87
|
end
|
43
88
|
|
44
89
|
def shutdown
|
45
|
-
warn "sending
|
46
|
-
|
90
|
+
warn "sending quiet to #{identity}"
|
91
|
+
sidekiq_process.quiet!
|
92
|
+
|
93
|
+
sleep(5) # gives Sidekiq API 5 seconds to update ProcessSet
|
47
94
|
|
48
95
|
warn "shutting down #{identity} in #{@grace_time} seconds"
|
49
96
|
wait_job_finish_in_grace_time
|
50
97
|
|
51
|
-
warn "
|
52
|
-
|
98
|
+
warn "stopping #{identity}"
|
99
|
+
sidekiq_process.stop!
|
53
100
|
|
54
101
|
warn "waiting #{@shutdown_wait} seconds before sending " \
|
55
102
|
"#{@kill_signal} to #{identity}"
|
56
103
|
sleep(@shutdown_wait)
|
57
104
|
|
58
105
|
warn "sending #{@kill_signal} to #{identity}"
|
59
|
-
|
106
|
+
::Process.kill(@kill_signal, ::Process.pid)
|
60
107
|
end
|
61
108
|
|
62
109
|
def wait_job_finish_in_grace_time
|
63
110
|
start = Time.now
|
64
|
-
|
65
|
-
break if grace_time_exceeded?(start)
|
66
|
-
break if no_jobs_on_quiet_processes?
|
67
|
-
sleep(1)
|
68
|
-
end
|
111
|
+
sleep(1) until grace_time_exceeded?(start) || jobs_finished?
|
69
112
|
end
|
70
113
|
|
71
114
|
def grace_time_exceeded?(start)
|
@@ -74,36 +117,23 @@ class Sidekiq::WorkerKiller
|
|
74
117
|
start + @grace_time < Time.now
|
75
118
|
end
|
76
119
|
|
77
|
-
def
|
78
|
-
|
79
|
-
return false if process["busy"] != 0 && process["quiet"] == "true"
|
80
|
-
end
|
81
|
-
true
|
120
|
+
def jobs_finished?
|
121
|
+
sidekiq_process.stopping? && sidekiq_process["busy"] == 0
|
82
122
|
end
|
83
123
|
|
84
124
|
def current_rss
|
85
125
|
::GetProcessMem.new.mb
|
86
126
|
end
|
87
127
|
|
88
|
-
def
|
89
|
-
::
|
90
|
-
|
91
|
-
|
92
|
-
def pid
|
93
|
-
::Process.pid
|
128
|
+
def sidekiq_process
|
129
|
+
Sidekiq::ProcessSet.new.find do |process|
|
130
|
+
process["identity"] == identity
|
131
|
+
end || raise("No sidekiq worker with identity #{identity} found")
|
94
132
|
end
|
95
133
|
|
96
134
|
def identity
|
97
|
-
"
|
98
|
-
end
|
99
|
-
|
100
|
-
def quiet_signal
|
101
|
-
if Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new("5.0")
|
102
|
-
"TSTP"
|
103
|
-
else
|
104
|
-
"USR1"
|
105
|
-
end
|
106
|
-
end
|
135
|
+
config[:identity] || config["identity"]
|
136
|
+
end unless method_defined?(:identity)
|
107
137
|
|
108
138
|
def warn(msg)
|
109
139
|
Sidekiq.logger.warn(msg)
|
@@ -1,125 +1,219 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Sidekiq::WorkerKiller do
|
4
|
+
let(:sidekiq_process_set) { instance_double(Sidekiq::ProcessSet) }
|
5
|
+
let(:sidekiq_process) { instance_double(Sidekiq::Process) }
|
4
6
|
|
5
7
|
before do
|
6
8
|
allow(subject).to receive(:warn) # silence "warn" logs
|
9
|
+
allow(subject).to receive(:sleep) # reduces tests running time
|
10
|
+
allow(sidekiq_process).to receive(:quiet!)
|
11
|
+
allow(sidekiq_process).to receive(:stop!)
|
12
|
+
allow(sidekiq_process).to receive(:stopping?)
|
7
13
|
end
|
8
14
|
|
9
|
-
describe "#
|
10
|
-
let(:
|
11
|
-
|
12
|
-
let(:queue){ double("queue") }
|
13
|
-
it "should yield" do
|
14
|
-
expect { |b|
|
15
|
-
subject.call(worker, job, queue, &b)
|
16
|
-
}.to yield_with_no_args
|
15
|
+
describe "#sidekiq_process" do
|
16
|
+
let(:identity) do
|
17
|
+
"foobar"
|
17
18
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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)
|
22
36
|
end
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
52
|
+
|
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)
|
26
64
|
end
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#call" do
|
68
|
+
let(:worker){ double("worker") }
|
69
|
+
let(:job){ double("job") }
|
70
|
+
let(:queue){ double("queue") }
|
71
|
+
|
72
|
+
it "should yield" do
|
73
|
+
expect { |b|
|
74
|
+
subject.call(worker, job, queue, &b)
|
75
|
+
}.to yield_with_no_args
|
31
76
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
77
|
+
|
78
|
+
context "when current rss is over max rss" do
|
79
|
+
subject{ described_class.new(max_rss: 2) }
|
80
|
+
|
81
|
+
before do
|
82
|
+
allow(subject).to receive(:current_rss).and_return(3)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should request shutdown" do
|
86
|
+
expect(subject).to receive(:request_shutdown)
|
37
87
|
subject.call(worker, job, queue){}
|
38
88
|
end
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
expect(subject).to_not receive(:request_shutdown)
|
89
|
+
|
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)
|
44
93
|
subject.call(worker, job, queue){}
|
45
94
|
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
95
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context "when gc is false" do
|
133
|
+
subject{ described_class.new(max_rss: 2, gc: false) }
|
134
|
+
it "should not call garbage collect" do
|
135
|
+
allow(subject).to receive(:request_shutdown)
|
136
|
+
expect(GC).not_to receive(:start)
|
137
|
+
subject.call(worker, job, queue){}
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "but max rss is 0" do
|
142
|
+
subject{ described_class.new(max_rss: 0) }
|
143
|
+
it "should not request shutdown" do
|
144
|
+
expect(subject).to_not receive(:request_shutdown)
|
145
|
+
subject.call(worker, job, queue){}
|
146
|
+
end
|
147
|
+
end
|
60
148
|
end
|
61
149
|
end
|
62
150
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
shutdown_request_time = nil
|
70
|
-
shutdown_time = nil
|
71
|
-
|
72
|
-
# replace the original #request_shutdown to track
|
73
|
-
# when the shutdown is requested
|
74
|
-
original_request_shutdown = subject.method(:request_shutdown)
|
75
|
-
allow(subject).to receive(:request_shutdown) do
|
76
|
-
shutdown_request_time= Time.now
|
77
|
-
original_request_shutdown.call
|
151
|
+
describe "#request_shutdown" do
|
152
|
+
context "grace time is default" do
|
153
|
+
before { allow(subject).to receive(:shutdown){ sleep 0.01 } }
|
154
|
+
it "should call shutdown" do
|
155
|
+
expect(subject).to receive(:shutdown)
|
156
|
+
subject.send(:request_shutdown).join
|
78
157
|
end
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
shutdown_time = Time.now if args[0] == 'SIGTERM'
|
158
|
+
it "should not call shutdown twice when called concurrently" do
|
159
|
+
expect(subject).to receive(:shutdown).once
|
160
|
+
2.times.map{ subject.send(:request_shutdown) }.each(&:join)
|
83
161
|
end
|
162
|
+
end
|
84
163
|
|
85
|
-
|
164
|
+
context "grace time is 5 seconds" do
|
165
|
+
subject{ described_class.new(max_rss: 2, grace_time: 5.0, shutdown_wait: 0) }
|
166
|
+
it "should wait the specified grace time before calling shutdown" do
|
167
|
+
# there are busy jobs that will not terminate within the grace time
|
168
|
+
allow(subject).to receive(:no_jobs_on_quiet_processes?).and_return(false)
|
86
169
|
|
87
|
-
|
170
|
+
shutdown_request_time = nil
|
171
|
+
shutdown_time = nil
|
88
172
|
|
89
|
-
|
173
|
+
# replace the original #request_shutdown to track
|
174
|
+
# when the shutdown is requested
|
175
|
+
original_request_shutdown = subject.method(:request_shutdown)
|
176
|
+
allow(subject).to receive(:request_shutdown) do
|
177
|
+
shutdown_request_time= Time.now
|
178
|
+
original_request_shutdown.call
|
179
|
+
end
|
90
180
|
|
91
|
-
|
92
|
-
|
93
|
-
|
181
|
+
# track when the process has been required to stop
|
182
|
+
expect(sidekiq_process).to receive(:stop!) do |*args|
|
183
|
+
shutdown_time = Time.now
|
184
|
+
end
|
185
|
+
|
186
|
+
allow(Process).to receive(:kill)
|
187
|
+
allow(Process).to receive(:pid).and_return(99)
|
188
|
+
|
189
|
+
subject.send(:request_shutdown).join
|
190
|
+
|
191
|
+
elapsed_time = shutdown_time - shutdown_request_time
|
192
|
+
|
193
|
+
# the elapsed time between shutdown request and the actual
|
194
|
+
# shutdown signal should be greater than the specified grace_time
|
195
|
+
expect(elapsed_time).to be >= 5.0
|
196
|
+
end
|
94
197
|
end
|
95
|
-
end
|
96
198
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
199
|
+
context "grace time is Float::INFINITY" do
|
200
|
+
subject{ described_class.new(max_rss: 2, grace_time: Float::INFINITY, shutdown_wait: 0) }
|
201
|
+
it "call signal only on jobs" do
|
202
|
+
allow(subject).to receive(:jobs_finished?).and_return(true)
|
203
|
+
allow(Process).to receive(:pid).and_return(99)
|
204
|
+
expect(sidekiq_process).to receive(:quiet!)
|
205
|
+
expect(sidekiq_process).to receive(:stop!)
|
206
|
+
expect(Process).to receive(:kill).with('SIGKILL', 99)
|
105
207
|
|
106
|
-
|
208
|
+
subject.send(:request_shutdown).join
|
209
|
+
end
|
107
210
|
end
|
108
211
|
end
|
109
212
|
end
|
110
213
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
stub_const("Sidekiq::VERSION", "5.2.1")
|
116
|
-
expect(subject.send :quiet_signal).to eq "TSTP"
|
117
|
-
end
|
118
|
-
it "should give USR1 if Sidekiq version is < 5.0" do
|
119
|
-
stub_const("Sidekiq::VERSION", "3.0")
|
120
|
-
expect(subject.send :quiet_signal).to eq "USR1"
|
121
|
-
stub_const("Sidekiq::VERSION", "4.6.7")
|
122
|
-
expect(subject.send :quiet_signal).to eq "USR1"
|
123
|
-
end
|
214
|
+
def rspec_info(msg)
|
215
|
+
is_printing_progress = RSpec.configuration.formatters.any? { |f| /ProgressFormatter/.match?(f.class.name) }
|
216
|
+
return if is_printing_progress
|
217
|
+
RSpec.configuration.reporter.message msg
|
124
218
|
end
|
125
219
|
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: 0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cyrille Courtiere
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-09-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: get_process_mem
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '5'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '5'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.49.1
|
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'
|
69
83
|
description:
|
70
84
|
email:
|
71
85
|
- dev@klaxit.com
|
@@ -96,8 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
110
|
- !ruby/object:Gem::Version
|
97
111
|
version: '0'
|
98
112
|
requirements: []
|
99
|
-
|
100
|
-
rubygems_version: 2.5.2.3
|
113
|
+
rubygems_version: 3.0.3
|
101
114
|
signing_key:
|
102
115
|
specification_version: 4
|
103
116
|
summary: Sidekiq worker killer
|