unicorn-worker-killer-2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []