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 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