worker_killer 1.0.2.187805 → 1.0.3.189098
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.
- checksums.yaml +4 -4
- data/lib/worker_killer/count_limiter.rb +2 -2
- data/lib/worker_killer/killer/delayed_job.rb +3 -3
- data/lib/worker_killer/killer.rb +5 -1
- data/lib/worker_killer/memory_limiter.rb +29 -8
- data/lib/worker_killer/version.rb +1 -1
- data/spec/delayed_job_plugin/jobs_limiter_spec.rb +3 -4
- data/spec/delayed_job_plugin/oom_limiter_spec.rb +4 -5
- data/spec/memory_limiter_spec.rb +58 -21
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 833e8af55d32a033c006174376730eeb13939b9a71428bced0bd6fbbf31a3fbf
|
4
|
+
data.tar.gz: 8867071b25e0bcfaea641fdc5595794cfd4ad166da32f6aa55d600322e925bf0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ceb0b3fdfb56a24480ff28a809dcb6bf96613d31fefa2c9fd2eca60e12205ef211c160683e6c9f42bb06928126476393430d3d6972da497872c886d2fcdd319
|
7
|
+
data.tar.gz: c74db721a9e0684598abc4f0d40f1aeb7b55be6d77e14a214b15431d0d1012272df43ff584d41673f8f5f28b5037ef441ae39938c093b1aba7a5d85d3fadd60f
|
@@ -17,12 +17,12 @@ module WorkerKiller
|
|
17
17
|
@started_at ||= Time.now
|
18
18
|
|
19
19
|
if @verbose
|
20
|
-
logger.info "#{self}: worker (pid: #{Process.pid}) has #{@left} left before being limited"
|
20
|
+
logger.info "#{self.class}: worker (pid: #{Process.pid}) has #{@left} left before being limited"
|
21
21
|
end
|
22
22
|
|
23
23
|
return false if (@left -= 1) > 0
|
24
24
|
|
25
|
-
logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds max number of requests (limit: #{@limit})"
|
25
|
+
logger.warn "#{self.class}: worker (pid: #{Process.pid}) exceeds max number of requests (limit: #{@limit})"
|
26
26
|
|
27
27
|
true
|
28
28
|
end
|
@@ -4,18 +4,18 @@ module WorkerKiller
|
|
4
4
|
|
5
5
|
def do_kill(sig, pid, alive_sec, dj:, **_params)
|
6
6
|
if sig == :KILL
|
7
|
-
logger.error "#{self} force to #{sig} self (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
|
7
|
+
logger.error "#{self.class}: force to #{sig} self (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
|
8
8
|
Process.kill sig, pid
|
9
9
|
return
|
10
10
|
end
|
11
11
|
|
12
12
|
dj.stop
|
13
|
-
logger.info "#{self} try to stop DelayedJob due to #{sig} self (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
|
13
|
+
logger.info "#{self.class}: try to stop DelayedJob due to #{sig} self (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
|
14
14
|
|
15
15
|
return if sig != :TERM
|
16
16
|
|
17
17
|
if @termination
|
18
|
-
logger.warn "#{self} force to #{sig} self (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
|
18
|
+
logger.warn "#{self.class}: force to #{sig} self (pid: #{pid}) alive: #{alive_sec} sec (trial #{kill_attempts})"
|
19
19
|
Process.kill sig, pid
|
20
20
|
else
|
21
21
|
@termination = true
|
data/lib/worker_killer/killer.rb
CHANGED
@@ -4,7 +4,7 @@ module WorkerKiller
|
|
4
4
|
|
5
5
|
attr_accessor :config, :kill_attempts, :logger
|
6
6
|
|
7
|
-
def initialize(logger:
|
7
|
+
def initialize(logger: nil, **_kwargs)
|
8
8
|
@logger = logger
|
9
9
|
@config = WorkerKiller.configuration
|
10
10
|
@kill_attempts = 0
|
@@ -27,6 +27,10 @@ module WorkerKiller
|
|
27
27
|
raise 'Not Implemented'
|
28
28
|
end
|
29
29
|
# :nocov:
|
30
|
+
|
31
|
+
def logger
|
32
|
+
@logger || WorkerKiller.configuration.logger
|
33
|
+
end
|
30
34
|
|
31
35
|
end
|
32
36
|
end
|
@@ -5,36 +5,57 @@ module WorkerKiller
|
|
5
5
|
|
6
6
|
attr_reader :min, :max, :limit, :started_at, :check_cycle
|
7
7
|
|
8
|
-
def initialize(min
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def initialize(min:, max:, check_cycle: 16, verbose: false)
|
9
|
+
if min
|
10
|
+
# set static memory limits
|
11
|
+
@min = min
|
12
|
+
@max = max
|
13
|
+
@limit = @min + WorkerKiller.randomize(@max - @min + 1)
|
14
|
+
@limit_mb = (@limit / 1024 / 1024).round(1)
|
15
|
+
else
|
16
|
+
# prepare for relative memory limits
|
17
|
+
@max_percent = max
|
18
|
+
end
|
19
|
+
|
12
20
|
@check_cycle = check_cycle
|
13
21
|
@check_count = 0
|
14
22
|
@verbose = verbose
|
15
23
|
end
|
16
24
|
|
17
25
|
def check
|
18
|
-
return nil if @limit <= 1024**2
|
19
|
-
|
20
26
|
@started_at ||= Time.now
|
21
27
|
@check_count += 1
|
22
28
|
|
23
29
|
return nil if (@check_count % @check_cycle) != 0
|
24
30
|
|
25
31
|
rss = GetProcessMem.new.bytes
|
32
|
+
# initialize relative memory limits on first check
|
33
|
+
set_limits(rss, rss + rss * @max_percent) if min.nil?
|
34
|
+
|
35
|
+
do_check(rss)
|
36
|
+
end
|
37
|
+
|
38
|
+
def do_check(rss)
|
39
|
+
rss_mb = (rss / 1024 / 1024).round(1)
|
40
|
+
|
26
41
|
if @verbose
|
27
|
-
logger.info "#{self}: worker (pid: #{Process.pid}) using #{
|
42
|
+
logger.info "#{self.class}: worker (pid: #{Process.pid}) using #{rss_mb} MB (of #{@limit_mb} MB)"
|
28
43
|
end
|
29
44
|
@check_count = 0
|
30
45
|
|
31
46
|
return false if rss <= @limit
|
32
47
|
|
33
|
-
logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds memory limit (#{
|
48
|
+
logger.warn "#{self.class}: worker (pid: #{Process.pid}) exceeds memory limit (#{rss_mb} MB > #{@limit_mb} MB)"
|
34
49
|
|
35
50
|
true
|
36
51
|
end
|
37
52
|
|
53
|
+
def set_limits(min, max)
|
54
|
+
@min = min
|
55
|
+
@limit = @max = max
|
56
|
+
@limit_mb = (@limit / 1024 / 1024).round(1)
|
57
|
+
end
|
58
|
+
|
38
59
|
def logger
|
39
60
|
WorkerKiller.configuration.logger
|
40
61
|
end
|
@@ -1,13 +1,12 @@
|
|
1
1
|
RSpec.describe WorkerKiller::DelayedJobPlugin::JobsLimiter do
|
2
|
-
|
3
|
-
let(:killer) {double}
|
2
|
+
let(:killer) { double }
|
4
3
|
subject(:plugin){ described_class.new(killer: killer) }
|
5
4
|
|
6
5
|
context 'DelayedJob initialization' do
|
7
6
|
let(:lifecycle) { double }
|
8
7
|
subject(:instance) { plugin.new(lifecycle) }
|
9
|
-
|
10
|
-
it
|
8
|
+
|
9
|
+
it do
|
11
10
|
expect(lifecycle).to receive(:after).with(:perform).and_yield
|
12
11
|
expect(lifecycle).to receive(:after).with(:loop).and_yield
|
13
12
|
instance
|
@@ -1,13 +1,12 @@
|
|
1
1
|
RSpec.describe WorkerKiller::DelayedJobPlugin::OOMLimiter do
|
2
|
-
|
3
|
-
|
4
|
-
subject(:plugin){ described_class.new(killer: killer) }
|
2
|
+
let(:killer) { double }
|
3
|
+
subject(:plugin){ described_class.new(min: (1024**3), max: (2 * (1024**3)), killer: killer) }
|
5
4
|
|
6
5
|
context 'DelayedJob initialization' do
|
7
6
|
let(:lifecycle) { double }
|
8
7
|
subject(:instance) { plugin.new(lifecycle) }
|
9
|
-
|
10
|
-
it
|
8
|
+
|
9
|
+
it do
|
11
10
|
expect(lifecycle).to receive(:after).with(:perform).and_yield
|
12
11
|
expect(lifecycle).to receive(:after).with(:loop).and_yield
|
13
12
|
instance
|
data/spec/memory_limiter_spec.rb
CHANGED
@@ -2,13 +2,10 @@ RSpec.describe WorkerKiller::MemoryLimiter do
|
|
2
2
|
let(:logger){ Logger.new(nil) }
|
3
3
|
|
4
4
|
subject{ described_class.new(**options) }
|
5
|
-
let(:mb){ 1024 * 1024 }
|
6
|
-
let(:min){ rand(50..100) * mb }
|
7
|
-
let(:max){ min + rand(100) * mb }
|
8
5
|
let(:check_cycle){ 5 }
|
9
|
-
let(:options){ { min: min, max: max, check_cycle: check_cycle, verbose: true} }
|
6
|
+
let(:options){ { min: min, max: max, check_cycle: check_cycle, verbose: true } }
|
7
|
+
let(:memory) { instance_double(GetProcessMem) }
|
10
8
|
|
11
|
-
it { is_expected.to have_attributes(min: min, max: max, limit: a_value_between(min, max)) }
|
12
9
|
|
13
10
|
def skip_cycles(object, cycles)
|
14
11
|
(cycles - 1).times do
|
@@ -16,28 +13,68 @@ RSpec.describe WorkerKiller::MemoryLimiter do
|
|
16
13
|
end
|
17
14
|
end
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
skip_cycles(subject, check_cycle)
|
16
|
+
before do
|
17
|
+
allow(GetProcessMem).to receive(:new).and_return(memory)
|
23
18
|
end
|
24
19
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
20
|
+
context 'fixed memory limit' do
|
21
|
+
let(:mb){ 1024 * 1024 }
|
22
|
+
let(:min){ rand(50..100) * mb }
|
23
|
+
let(:max){ min + rand(100) * mb }
|
24
|
+
|
25
|
+
it { is_expected.to have_attributes(min: min, max: max, limit: a_value_between(min, max)) }
|
26
|
+
|
27
|
+
it 'expect to skip check while less than cycle count' do
|
28
|
+
expect(GetProcessMem).not_to receive(:new)
|
29
|
+
|
30
|
+
skip_cycles(subject, check_cycle)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'expect to skip check after cycle count reached' do
|
34
|
+
expect(memory).to receive(:bytes).and_return(min - 1)
|
35
|
+
|
36
|
+
skip_cycles(subject, check_cycle)
|
37
|
+
expect(subject.check).to be_falsey
|
38
|
+
end
|
29
39
|
|
30
|
-
|
31
|
-
|
40
|
+
it 'expect call reaction when check succeded' do
|
41
|
+
expect(memory).to receive(:bytes).and_return(subject.limit + 1)
|
42
|
+
|
43
|
+
skip_cycles(subject, check_cycle)
|
44
|
+
expect(subject.check).to be_truthy
|
45
|
+
end
|
32
46
|
end
|
33
47
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
48
|
+
context 'relative memory limit' do
|
49
|
+
let(:min){ nil }
|
50
|
+
let(:max){ 0.5 }
|
51
|
+
let(:rss) { 100 * 1024 * 1024 }
|
52
|
+
let(:min_expected) { rss }
|
53
|
+
let(:max_expected) { min_expected + min_expected * max }
|
38
54
|
|
39
|
-
|
40
|
-
|
55
|
+
it { is_expected.to have_attributes(min: nil, max: nil, limit: nil) }
|
56
|
+
|
57
|
+
it 'expect to initialize limits on first check' do
|
58
|
+
expect(memory).to receive(:bytes).and_return(rss)
|
59
|
+
|
60
|
+
skip_cycles(subject, check_cycle)
|
61
|
+
is_expected.to have_attributes(min: nil, max: nil, limit: nil)
|
62
|
+
|
63
|
+
expect(subject.check).to be_falsey
|
64
|
+
|
65
|
+
is_expected.to have_attributes(min: min_expected, max: max_expected, limit: max_expected)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'expect call reaction when check succeded' do
|
69
|
+
expect(memory).to receive(:bytes).and_return(rss)
|
70
|
+
|
71
|
+
skip_cycles(subject, check_cycle)
|
72
|
+
expect(subject.check).to be_falsey
|
73
|
+
|
74
|
+
expect(memory).to receive(:bytes).and_return(subject.limit + 1)
|
75
|
+
skip_cycles(subject, check_cycle)
|
76
|
+
expect(subject.check).to be_truthy
|
77
|
+
end
|
41
78
|
end
|
42
79
|
end
|
43
80
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: worker_killer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3.189098
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samoilenko Yuri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: get_process_mem
|