unicorn-worker-killer 0.3.6 → 0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/AUTHORS +1 -0
- data/ChangeLog +19 -0
- data/README.md +13 -7
- data/VERSION +1 -1
- data/lib/unicorn/worker_killer.rb +28 -56
- data/lib/unicorn/{configuration.rb → worker_killer/configuration.rb} +0 -0
- data/unicorn-worker-killer.gemspec +5 -3
- metadata +35 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3b19012954bb3a259a52418a03a945bd4e1d0fbe682109a5c6a074f98daeeed2
|
4
|
+
data.tar.gz: d4882e751bae724d4d24a9194a3b76fe519d45427be55b52685a55309a82bca7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 642bf83fc8c7ee86a2d7bfa36910418627b53701e4c53a6b61d2f2df5ce12563ff689f80189a647343677bab1bd05765bb6a05d65fcdd50a1fe9078ade066fe1
|
7
|
+
data.tar.gz: 36100a92f0d917fff98a7ff73bdc64bb0b4adb16b17389d7109807f65a78842d64b4dec0fd370b9571b92780002c94d91e8a2e368ac5d6b958f063bca0b15d34
|
data/AUTHORS
CHANGED
data/ChangeLog
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
Release 0.4.5 - 2021/04/13
|
2
|
+
* update for Unicorn 6
|
3
|
+
|
4
|
+
Release 0.4.4 - 2015/11/13
|
5
|
+
* update for Unicorn 5
|
6
|
+
|
7
|
+
Release 0.4.3 - 2013/12/23
|
8
|
+
* fix for occasional 'invalid argument' error
|
9
|
+
|
10
|
+
Release 0.4.2 - 2013/09/23
|
11
|
+
* `verbose` options provides verbose logging
|
12
|
+
|
13
|
+
Release 0.4.1 - 2013/03/12
|
14
|
+
* Kill attempts happen once per request, not in a loop
|
15
|
+
|
16
|
+
Release 0.4.0 - 2013/03/12
|
17
|
+
* Send signals from inside a Thread (required for QUIT signal)
|
18
|
+
* Fix broken if/else in kill_self
|
19
|
+
|
1
20
|
Release 0.3.6 - 2013/03/09
|
2
21
|
* do not require configuration
|
3
22
|
* fix missing randomize() in MaxRequests killer
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# unicorn-worker-killer
|
2
2
|
|
3
|
-
[Unicorn](http://unicorn.bogomips.org/) is widely used HTTP-server for Rack applications. One thing we thought Unicorn
|
3
|
+
[Unicorn](http://unicorn.bogomips.org/) is widely used HTTP-server for Rack applications. One thing we thought Unicorn missed, is killing the Unicorn workers based on the number of requests and consumed memories.
|
4
4
|
|
5
5
|
`unicorn-worker-killer` gem provides automatic restart of Unicorn workers based on 1) max number of requests, and 2) process memory size (RSS), without affecting any requests. This will greatly improves site's stability by avoiding unexpected memory exhaustion at the application nodes.
|
6
6
|
|
@@ -12,7 +12,7 @@ No external process like `god` is required. Just install one gem: `unicorn-worke
|
|
12
12
|
|
13
13
|
# Usage
|
14
14
|
|
15
|
-
Add these lines to your `config.ru`.
|
15
|
+
Add these lines to your `config.ru`. (These lines should be added above the `require ::File.expand_path('../config/environment', __FILE__)` line.
|
16
16
|
|
17
17
|
# Unicorn self-process killer
|
18
18
|
require 'unicorn/worker_killer'
|
@@ -25,19 +25,25 @@ Add these lines to your `config.ru`.
|
|
25
25
|
|
26
26
|
This gem provides two modules.
|
27
27
|
|
28
|
-
### Unicorn::WorkerKiller::MaxRequests(max_requests_min=3072, max_requests_max=4096)
|
28
|
+
### `Unicorn::WorkerKiller::MaxRequests(max_requests_min=3072, max_requests_max=4096, verbose=false)`
|
29
29
|
|
30
30
|
This module automatically restarts the Unicorn workers, based on the number of requests which worker processed.
|
31
31
|
|
32
32
|
`max_requests_min` and `max_requests_max` specify the min and max of maximum requests per worker. The actual limit is decided by rand() between `max_requests_min` and `max_requests_max` per worker, to prevent all workers to be dead at the same time. Once the number exceeds the limit, that worker is automatically restarted.
|
33
33
|
|
34
|
-
|
34
|
+
If `verbose` is set to true, then after every request, your log will show the requests left before restart. This logging is done at the `info` level.
|
35
|
+
|
36
|
+
### `Unicorn::WorkerKiller::Oom(memory_limit_min=(1024\*\*3), memory_limit_max=(2\*(1024\*\*3)), check_cycle = 16, verbose = false)`
|
35
37
|
|
36
38
|
This module automatically restarts the Unicorn workers, based on its memory size.
|
37
39
|
|
38
|
-
`memory_limit_min` and `memory_limit_max` specify the min and max of maximum memory per worker. The actual limit is decided by rand() between `memory_limit_min` and `memory_limit_max` per worker, to prevent all workers to be dead at the same time. Once the memory size exceeds `memory_size`, that worker is automatically restarted.
|
40
|
+
`memory_limit_min` and `memory_limit_max` specify the min and max of maximum memory in bytes per worker. The actual limit is decided by rand() between `memory_limit_min` and `memory_limit_max` per worker, to prevent all workers to be dead at the same time. Once the memory size exceeds `memory_size`, that worker is automatically restarted.
|
39
41
|
|
40
42
|
The memory size check is done in every `check_cycle` requests.
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
+
If `verbose` is set to true, then every memory size check will be shown in your logs. This logging is done at the `info` level.
|
45
|
+
|
46
|
+
# Special Thanks
|
47
|
+
|
48
|
+
- [@hotchpotch](http://github.com/hotchpotch/) for the [original idea](https://gist.github.com/hotchpotch/1258681)
|
49
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.5
|
@@ -1,32 +1,30 @@
|
|
1
|
-
require 'unicorn
|
1
|
+
require 'unicorn'
|
2
|
+
require 'unicorn/worker_killer/configuration'
|
3
|
+
require 'get_process_mem'
|
2
4
|
|
3
5
|
module Unicorn::WorkerKiller
|
4
6
|
class << self
|
5
7
|
attr_accessor :configuration
|
6
8
|
end
|
7
9
|
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
10
|
+
# Kill the current process by telling it to send signals to itself. If
|
11
|
+
# the process isn't killed after `configuration.max_quit` QUIT signals,
|
12
|
+
# send TERM signals until `configuration.max_term`. Finally, send a KILL
|
13
|
+
# signal. A single signal is sent per request.
|
11
14
|
# @see http://unicorn.bogomips.org/SIGNALS.html
|
12
15
|
def self.kill_self(logger, start_time)
|
13
|
-
alive_sec = (Time.now - start_time).
|
14
|
-
|
15
|
-
i = 0
|
16
|
-
while true
|
17
|
-
i += 1
|
18
|
-
sig = :QUIT
|
19
|
-
if i > configuration.max_quit
|
20
|
-
sig = :TERM
|
21
|
-
elsif i > configuration.max_term
|
22
|
-
sig = :KILL
|
23
|
-
end
|
16
|
+
alive_sec = (Time.now - start_time).round
|
17
|
+
worker_pid = Process.pid
|
24
18
|
|
25
|
-
|
26
|
-
|
19
|
+
@@kill_attempts ||= 0
|
20
|
+
@@kill_attempts += 1
|
27
21
|
|
28
|
-
|
29
|
-
|
22
|
+
sig = :QUIT
|
23
|
+
sig = :TERM if @@kill_attempts > configuration.max_quit
|
24
|
+
sig = :KILL if @@kill_attempts > configuration.max_term
|
25
|
+
|
26
|
+
logger.warn "#{self} send SIG#{sig} (pid: #{worker_pid}) alive: #{alive_sec} sec (trial #{@@kill_attempts})"
|
27
|
+
Process.kill sig, worker_pid
|
30
28
|
end
|
31
29
|
|
32
30
|
module Oom
|
@@ -35,19 +33,20 @@ module Unicorn::WorkerKiller
|
|
35
33
|
# affect the request.
|
36
34
|
#
|
37
35
|
# @see https://github.com/defunkt/unicorn/blob/master/lib/unicorn/oob_gc.rb#L40
|
38
|
-
def self.new(app, memory_limit_min = (1024**3), memory_limit_max = (2*(1024**3)), check_cycle = 16)
|
36
|
+
def self.new(app, memory_limit_min = (1024**3), memory_limit_max = (2*(1024**3)), check_cycle = 16, verbose = false)
|
39
37
|
ObjectSpace.each_object(Unicorn::HttpServer) do |s|
|
40
38
|
s.extend(self)
|
41
39
|
s.instance_variable_set(:@_worker_memory_limit_min, memory_limit_min)
|
42
40
|
s.instance_variable_set(:@_worker_memory_limit_max, memory_limit_max)
|
43
41
|
s.instance_variable_set(:@_worker_check_cycle, check_cycle)
|
44
42
|
s.instance_variable_set(:@_worker_check_count, 0)
|
43
|
+
s.instance_variable_set(:@_verbose, verbose)
|
45
44
|
end
|
46
45
|
app # pretend to be Rack middleware since it was in the past
|
47
46
|
end
|
48
47
|
|
49
48
|
def randomize(integer)
|
50
|
-
RUBY_VERSION > "1.9" ? Random.rand(integer) : rand(integer)
|
49
|
+
RUBY_VERSION > "1.9" ? Random.rand(integer.abs) : rand(integer)
|
51
50
|
end
|
52
51
|
|
53
52
|
def process_client(client)
|
@@ -58,7 +57,8 @@ module Unicorn::WorkerKiller
|
|
58
57
|
@_worker_memory_limit ||= @_worker_memory_limit_min + randomize(@_worker_memory_limit_max - @_worker_memory_limit_min + 1)
|
59
58
|
@_worker_check_count += 1
|
60
59
|
if @_worker_check_count % @_worker_check_cycle == 0
|
61
|
-
rss =
|
60
|
+
rss = GetProcessMem.new.bytes
|
61
|
+
logger.info "#{self}: worker (pid: #{Process.pid}) using #{rss} bytes." if @_verbose
|
62
62
|
if rss > @_worker_memory_limit
|
63
63
|
logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds memory limit (#{rss} bytes > #{@_worker_memory_limit} bytes)"
|
64
64
|
Unicorn::WorkerKiller.kill_self(logger, @_worker_process_start)
|
@@ -66,38 +66,6 @@ module Unicorn::WorkerKiller
|
|
66
66
|
@_worker_check_count = 0
|
67
67
|
end
|
68
68
|
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
def _worker_rss
|
73
|
-
proc_status = "/proc/#{Process.pid}/status"
|
74
|
-
if File.exists? proc_status
|
75
|
-
open(proc_status).each_line { |l|
|
76
|
-
if l.include? 'VmRSS'
|
77
|
-
ls = l.split
|
78
|
-
if ls.length == 3
|
79
|
-
value = ls[1].to_i
|
80
|
-
unit = ls[2]
|
81
|
-
case unit.downcase
|
82
|
-
when 'kb'
|
83
|
-
return value*(1024**1)
|
84
|
-
when 'mb'
|
85
|
-
return value*(1024**2)
|
86
|
-
when 'gb'
|
87
|
-
return value*(1024**3)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
}
|
92
|
-
end
|
93
|
-
|
94
|
-
# Forking the child process sometimes fails under low memory condition.
|
95
|
-
# It would be ideal for not forking the process to get RSS. For Linux,
|
96
|
-
# this module reads '/proc/<pid>/status' to get RSS, but not for other
|
97
|
-
# environments (e.g. MacOS and Windows).
|
98
|
-
kb = `ps -o rss= -p #{Process.pid}`.to_i
|
99
|
-
return kb * 1024
|
100
|
-
end
|
101
69
|
end
|
102
70
|
|
103
71
|
module MaxRequests
|
@@ -106,17 +74,19 @@ module Unicorn::WorkerKiller
|
|
106
74
|
# affect the request.
|
107
75
|
#
|
108
76
|
# @see https://github.com/defunkt/unicorn/blob/master/lib/unicorn/oob_gc.rb#L40
|
109
|
-
def self.new(app, max_requests_min = 3072, max_requests_max = 4096)
|
77
|
+
def self.new(app, max_requests_min = 3072, max_requests_max = 4096, verbose = false)
|
110
78
|
ObjectSpace.each_object(Unicorn::HttpServer) do |s|
|
111
79
|
s.extend(self)
|
112
80
|
s.instance_variable_set(:@_worker_max_requests_min, max_requests_min)
|
113
81
|
s.instance_variable_set(:@_worker_max_requests_max, max_requests_max)
|
82
|
+
s.instance_variable_set(:@_verbose, verbose)
|
114
83
|
end
|
84
|
+
|
115
85
|
app # pretend to be Rack middleware since it was in the past
|
116
86
|
end
|
117
87
|
|
118
88
|
def randomize(integer)
|
119
|
-
RUBY_VERSION > "1.9" ? Random.rand(integer) : rand(integer)
|
89
|
+
RUBY_VERSION > "1.9" ? Random.rand(integer.abs) : rand(integer)
|
120
90
|
end
|
121
91
|
|
122
92
|
def process_client(client)
|
@@ -126,6 +96,8 @@ module Unicorn::WorkerKiller
|
|
126
96
|
@_worker_process_start ||= Time.now
|
127
97
|
@_worker_cur_requests ||= @_worker_max_requests_min + randomize(@_worker_max_requests_max - @_worker_max_requests_min + 1)
|
128
98
|
@_worker_max_requests ||= @_worker_cur_requests
|
99
|
+
logger.info "#{self}: worker (pid: #{Process.pid}) has #{@_worker_cur_requests} left before being killed" if @_verbose
|
100
|
+
|
129
101
|
if (@_worker_cur_requests -= 1) <= 0
|
130
102
|
logger.warn "#{self}: worker (pid: #{Process.pid}) exceeds max number of requests (limit: #{@_worker_max_requests})"
|
131
103
|
Unicorn::WorkerKiller.kill_self(logger, @_worker_process_start)
|
File without changes
|
@@ -7,14 +7,16 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.homepage = "https://github.com/kzk/unicorn-worker-killer"
|
8
8
|
gem.summary = gem.description
|
9
9
|
gem.version = File.read("VERSION").strip
|
10
|
-
gem.authors = ["Kazuki Ohta", "Sadayuki Furuhashi"]
|
11
|
-
gem.email = ["kazuki.ohta@gmail.com", "frsyuki@gmail.com"]
|
10
|
+
gem.authors = ["Kazuki Ohta", "Sadayuki Furuhashi", "Jonathan Clem"]
|
11
|
+
gem.email = ["kazuki.ohta@gmail.com", "frsyuki@gmail.com", "jonathan@jclem.net"]
|
12
12
|
gem.has_rdoc = false
|
13
13
|
#gem.platform = Gem::Platform::RUBY
|
14
14
|
gem.files = `git ls-files`.split("\n")
|
15
15
|
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
16
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
gem.licenses = ["GPLv2+", "Ruby 1.8"]
|
17
18
|
gem.require_paths = ['lib']
|
18
|
-
gem.add_dependency "unicorn",
|
19
|
+
gem.add_dependency "unicorn", [">= 4", "< 7"]
|
20
|
+
gem.add_dependency "get_process_mem", "~> 0"
|
19
21
|
gem.add_development_dependency "rake", ">= 0.9.2"
|
20
22
|
end
|
metadata
CHANGED
@@ -1,48 +1,70 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn-worker-killer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kazuki Ohta
|
8
8
|
- Sadayuki Furuhashi
|
9
|
+
- Jonathan Clem
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date:
|
13
|
+
date: 2021-04-14 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: unicorn
|
16
17
|
requirement: !ruby/object:Gem::Requirement
|
17
18
|
requirements:
|
18
|
-
- -
|
19
|
+
- - ">="
|
19
20
|
- !ruby/object:Gem::Version
|
20
21
|
version: '4'
|
22
|
+
- - "<"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '7'
|
21
25
|
type: :runtime
|
22
26
|
prerelease: false
|
23
27
|
version_requirements: !ruby/object:Gem::Requirement
|
24
28
|
requirements:
|
25
|
-
- -
|
29
|
+
- - ">="
|
26
30
|
- !ruby/object:Gem::Version
|
27
31
|
version: '4'
|
32
|
+
- - "<"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '7'
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: get_process_mem
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
type: :runtime
|
43
|
+
prerelease: false
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
28
49
|
- !ruby/object:Gem::Dependency
|
29
50
|
name: rake
|
30
51
|
requirement: !ruby/object:Gem::Requirement
|
31
52
|
requirements:
|
32
|
-
- -
|
53
|
+
- - ">="
|
33
54
|
- !ruby/object:Gem::Version
|
34
55
|
version: 0.9.2
|
35
56
|
type: :development
|
36
57
|
prerelease: false
|
37
58
|
version_requirements: !ruby/object:Gem::Requirement
|
38
59
|
requirements:
|
39
|
-
- -
|
60
|
+
- - ">="
|
40
61
|
- !ruby/object:Gem::Version
|
41
62
|
version: 0.9.2
|
42
63
|
description: Kill unicorn workers by memory and request counts
|
43
64
|
email:
|
44
65
|
- kazuki.ohta@gmail.com
|
45
66
|
- frsyuki@gmail.com
|
67
|
+
- jonathan@jclem.net
|
46
68
|
executables: []
|
47
69
|
extensions: []
|
48
70
|
extra_rdoc_files: []
|
@@ -54,11 +76,13 @@ files:
|
|
54
76
|
- README.md
|
55
77
|
- Rakefile
|
56
78
|
- VERSION
|
57
|
-
- lib/unicorn/configuration.rb
|
58
79
|
- lib/unicorn/worker_killer.rb
|
80
|
+
- lib/unicorn/worker_killer/configuration.rb
|
59
81
|
- unicorn-worker-killer.gemspec
|
60
82
|
homepage: https://github.com/kzk/unicorn-worker-killer
|
61
|
-
licenses:
|
83
|
+
licenses:
|
84
|
+
- GPLv2+
|
85
|
+
- Ruby 1.8
|
62
86
|
metadata: {}
|
63
87
|
post_install_message:
|
64
88
|
rdoc_options: []
|
@@ -66,17 +90,16 @@ require_paths:
|
|
66
90
|
- lib
|
67
91
|
required_ruby_version: !ruby/object:Gem::Requirement
|
68
92
|
requirements:
|
69
|
-
- -
|
93
|
+
- - ">="
|
70
94
|
- !ruby/object:Gem::Version
|
71
95
|
version: '0'
|
72
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
97
|
requirements:
|
74
|
-
- -
|
98
|
+
- - ">="
|
75
99
|
- !ruby/object:Gem::Version
|
76
100
|
version: '0'
|
77
101
|
requirements: []
|
78
|
-
|
79
|
-
rubygems_version: 2.0.0
|
102
|
+
rubygems_version: 3.0.8
|
80
103
|
signing_key:
|
81
104
|
specification_version: 4
|
82
105
|
summary: Kill unicorn workers by memory and request counts
|