unicorn-worker-killer-2 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 978787e2d1d75226ad11e4bcd96c84c39a9102c3
4
+ data.tar.gz: f59f3e586709b9ee51972ee9d3698829ba74a529
5
+ SHA512:
6
+ metadata.gz: 657ff2fc32758ff7255c9d999acb3bce1e36cc145d2bf35a602399544f72bf9ebc4a0ffc019cb83fb5dcbc6967ebe4916f21e469f7a04c515c6ed79f6a4929f9
7
+ data.tar.gz: '0903a412426f770137ef330f505db9245df49c8c8df4aadd0e5889e5eb82f3b67996f31f6334d6351617f38e731b70cf9148953cd40c12fd3bf68aaaf7756657'
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unicorn
4
+ module WorkerKiller
5
+ module MaxRequests
6
+ module MonkeyPatch
7
+ def process_client(client)
8
+ super(client) # Unicorn::HttpServer#process_client
9
+
10
+ return if @_worker_max_requests_min.zero? &&
11
+ @_worker_max_requests_max.zero?
12
+
13
+ logger.info "#{self}: worker (pid: #{Process.pid}) has #{@_worker_request_limit} left before being killed" if @_verbose
14
+
15
+ @_worker_request_limit -= 1
16
+ return if @_worker_request_limit.positive?
17
+
18
+ logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds max number of requests (limit: #{@_worker_max_requests})"
19
+ WorkerKiller.kill_self(logger, @_worker_process_start)
20
+ end
21
+ end
22
+
23
+ def self.monkey_patch(opts = {})
24
+ min = opts[:max_requests_min] || 3072
25
+ max = opts[:max_requests_max] || 4096
26
+ verbose = opts[:verbose] || false
27
+
28
+ ObjectSpace.each_object(HttpServer) do |s|
29
+ s.extend(MonkeyPatch)
30
+
31
+ s.instance_variable_set(:@_worker_process_start, WorkerKiller.now)
32
+
33
+ s.instance_variable_set(:@_worker_max_requests_min, min)
34
+ s.instance_variable_set(:@_worker_max_requests_max, max)
35
+ s.instance_variable_set(:@_verbose, verbose)
36
+
37
+ r = WorkerKiller.randomize(max - min + 1)
38
+ s.instance_variable_set(:@_worker_request_limit, min + r)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'get_process_mem'
4
+
5
+ module Unicorn
6
+ module WorkerKiller
7
+ module Oom
8
+ module MonkeyPatch
9
+ def process_client(client)
10
+ super(client) # Unicorn::HttpServer#process_client
11
+
12
+ return if @_worker_memory_limit_min.zero? &&
13
+ @_worker_memory_limit_max.zero?
14
+
15
+ @_worker_check_count ||= 0
16
+ @_worker_check_count += 1
17
+
18
+ return unless (@_worker_check_count % @_worker_check_cycle).zero?
19
+
20
+ rss = GetProcessMem.new.bytes
21
+ logger.info "#{self}: worker (pid: #{Process.pid}) using #{rss} bytes." if @_verbose
22
+ return if rss < @_worker_memory_limit
23
+
24
+ logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds memory limit (#{rss} bytes > #{@_worker_memory_limit} bytes)"
25
+ WorkerKiller.kill_self(logger, @_worker_process_start)
26
+ end
27
+ end
28
+
29
+ def self.monkey_patch(opts = {})
30
+ min = opts[:memory_limit_min] || (1024**3)
31
+ max = opts[:memory_limit_max] || 2 * (1024**3)
32
+ check_cycle = opts[:check_cycle] || 16
33
+ verbose = opts[:verbose] || false
34
+
35
+ ObjectSpace.each_object(HttpServer) do |s|
36
+ s.extend(MonkeyPatch)
37
+
38
+ s.instance_variable_set(:@_worker_process_start, WorkerKiller.now)
39
+
40
+ s.instance_variable_set(:@_worker_memory_limit_min, min)
41
+ s.instance_variable_set(:@_worker_memory_limit_max, max)
42
+ s.instance_variable_set(:@_worker_check_cycle, check_cycle)
43
+ s.instance_variable_set(:@_verbose, verbose)
44
+
45
+ r = WorkerKiller.randomize(max - min + 1)
46
+ s.instance_variable_set(:@_worker_memory_limit, min + r)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'unicorn'
4
+
5
+ require_relative 'worker-killer-2/oom'
6
+ require_relative 'worker-killer-2/max_requests'
7
+
8
+ module Unicorn
9
+ module WorkerKiller
10
+ # Kill the current process by telling it to send signals to itself. If the
11
+ # process isn't killed after 5 QUIT signals, send 10 TERM signals. Finally,
12
+ # send a KILL signal. A single signal is sent per request.
13
+ #
14
+ # @see http://unicorn.bogomips.org/SIGNALS.html
15
+ def self.kill_self(logger, start_time)
16
+ alive_sec = (now - start_time).round
17
+ worker_pid = Process.pid
18
+
19
+ @kill_attempts ||= 0
20
+ @kill_attempts += 1
21
+
22
+ sig = :QUIT
23
+ sig = :TERM if @kill_attempts > 10
24
+ sig = :KILL if @kill_attempts > 15
25
+
26
+ logger.warn "#{self} send SIG#{sig} (pid: #{worker_pid}) alive: #{alive_sec} sec (trial #{@kill_attempts})"
27
+ Process.kill sig, worker_pid
28
+ end
29
+
30
+ def self.randomize(integer)
31
+ Random.rand(integer.abs)
32
+ end
33
+
34
+ def self.now
35
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
36
+ end
37
+ end
38
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unicorn-worker-killer-2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Elsworth
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-03-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: get_process_mem
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: unicorn
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '4'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '6'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '4'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '6'
47
+ description: Kill Unicorn child processes when they exceed memory/request limits
48
+ email:
49
+ - chris.elsworth@bytemark.co.uk
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - lib/unicorn/worker-killer-2.rb
55
+ - lib/unicorn/worker-killer-2/max_requests.rb
56
+ - lib/unicorn/worker-killer-2/oom.rb
57
+ homepage: https://gitlab.bytemark.co.uk/bytemark/unicorn-worker-killer-2
58
+ licenses: []
59
+ metadata: {}
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.6.13
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: Rewrite of unicorn-worker-kill
80
+ test_files: []