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 +7 -0
- data/lib/unicorn/worker-killer-2/max_requests.rb +43 -0
- data/lib/unicorn/worker-killer-2/oom.rb +51 -0
- data/lib/unicorn/worker-killer-2.rb +38 -0
- metadata +80 -0
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: []
|