worker_killer 1.0.2.187805 → 1.0.3.189098

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: 2d3c21030cbbe79605a447ab1c25907b0d75c826090650b03eb1899b98581c7b
4
- data.tar.gz: c32c301433c80c608ca99961ef124fe4669f5db9cad1f07d5124c5b58234e3e1
3
+ metadata.gz: 833e8af55d32a033c006174376730eeb13939b9a71428bced0bd6fbbf31a3fbf
4
+ data.tar.gz: 8867071b25e0bcfaea641fdc5595794cfd4ad166da32f6aa55d600322e925bf0
5
5
  SHA512:
6
- metadata.gz: c483c498cecdb6a0bb04cfd2bbb57d34a386d6398d3dc5d075aedc8cad63cf145f9b78833264a2bfd10d78bc588bbdf9400007d13cab03bf421e2b9d065b9d45
7
- data.tar.gz: 60912f376e59463feaa46b34d88e1ac08d4810ad9388049dafd3f1acacb1da3deee720655a4dff63f85b00037badb9b4d2d8a33d2d3eb77a3cc8c48e8fea180f
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
@@ -4,7 +4,7 @@ module WorkerKiller
4
4
 
5
5
  attr_accessor :config, :kill_attempts, :logger
6
6
 
7
- def initialize(logger: WorkerKiller.configuration.logger, **_kwargs)
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: (1024**3), max: (2 * (1024**3)), check_cycle: 16, verbose: false)
9
- @min = min
10
- @max = max
11
- @limit = @min + WorkerKiller.randomize(@max - @min + 1)
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 #{rss} bytes(#{rss / 1024 / 1024}mb)."
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 (#{rss} bytes > #{@limit} bytes)"
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module WorkerKiller
4
4
 
5
- VERSION = '1.0.2'
5
+ VERSION = '1.0.3'
6
6
 
7
7
  end
8
8
 
@@ -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 do
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
- let(:killer) {double}
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 do
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
@@ -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
- it 'expect to skip check while less than cycle count' do
20
- expect(GetProcessMem).not_to receive(:new)
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
- it 'expect to skip check after cycle count reached' do
26
- memory = instance_double(GetProcessMem)
27
- expect(memory).to receive(:bytes).and_return(min - 1)
28
- expect(GetProcessMem).to receive(:new).and_return(memory)
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
- skip_cycles(subject, check_cycle)
31
- expect(subject.check).to be_falsey
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
- it 'expect call reaction when check succeded' do
35
- memory = instance_double(GetProcessMem)
36
- expect(memory).to receive(:bytes).and_return(subject.limit + 1)
37
- expect(GetProcessMem).to receive(:new).and_return(memory)
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
- skip_cycles(subject, check_cycle)
40
- expect(subject.check).to be_truthy
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.2.187805
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-09-28 00:00:00.000000000 Z
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