unicorn-worker-killer 0.1.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.
data/AUTHORS ADDED
@@ -0,0 +1,2 @@
1
+ Kazuki Ohta <kazuki.ohta _at_ gmail.com>
2
+ Sadayuki FURUHASHI <frsyuki _at_ gmail.com>
@@ -0,0 +1,3 @@
1
+ Release 0.1 - 2012/11/17
2
+
3
+ * First release
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,64 @@
1
+ Unicorn is copyrighted free software by all contributors, see logs in
2
+ revision control for names and email addresses of all of them.
3
+
4
+ You can redistribute it and/or modify it under either the terms of the
5
+ GNU General Public License (GPL) as published by the Free Software
6
+ Foundation (FSF), version {3.0}[http://www.gnu.org/licenses/gpl-3.0.txt]
7
+ or version {2.0}[http://www.gnu.org/licenses/gpl-2.0.txt]
8
+ or the Ruby-specific license terms (see below).
9
+
10
+ The unicorn project leader (Eric Wong) reserves the right to add future
11
+ versions of the GPL (and no other licenses) as published by the FSF to
12
+ the licensing terms.
13
+
14
+ === Ruby-specific terms (if you're not using the GPLv2 or GPLv3)
15
+
16
+ 1. You may make and give away verbatim copies of the source form of the
17
+ software without restriction, provided that you duplicate all of the
18
+ original copyright notices and associated disclaimers.
19
+
20
+ 2. You may modify your copy of the software in any way, provided that
21
+ you do at least ONE of the following:
22
+
23
+ a) place your modifications in the Public Domain or otherwise make them
24
+ Freely Available, such as by posting said modifications to Usenet or an
25
+ equivalent medium, or by allowing the author to include your
26
+ modifications in the software.
27
+
28
+ b) use the modified software only within your corporation or
29
+ organization.
30
+
31
+ c) rename any non-standard executables so the names do not conflict with
32
+ standard executables, which must also be provided.
33
+
34
+ d) make other distribution arrangements with the author.
35
+
36
+ 3. You may distribute the software in object code or executable
37
+ form, provided that you do at least ONE of the following:
38
+
39
+ a) distribute the executables and library files of the software,
40
+ together with instructions (in the manual page or equivalent) on where
41
+ to get the original distribution.
42
+
43
+ b) accompany the distribution with the machine-readable source of the
44
+ software.
45
+
46
+ c) give non-standard executables non-standard names, with
47
+ instructions on where to get the original software distribution.
48
+
49
+ d) make other distribution arrangements with the author.
50
+
51
+ 4. You may modify and include the part of the software into any other
52
+ software (possibly commercial). But some files in the distribution
53
+ are not written by the author, so that they are not under this terms.
54
+
55
+ 5. The scripts and library files supplied as input to or produced as
56
+ output from the software do not automatically fall under the
57
+ copyright of the software, but belong to whomever generated them,
58
+ and may be sold commercially, and may be aggregated with this
59
+ software.
60
+
61
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
62
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
63
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
64
+ PURPOSE.
@@ -0,0 +1,23 @@
1
+ # unicorn-worker-killer
2
+
3
+ Killing Unicorn worker based on 1) Max number of requests and 2) Process memory size (RSS), without affecting the request.
4
+
5
+ # Install
6
+
7
+ gem 'unicorn-worker-killer'
8
+
9
+ # Usage
10
+
11
+ add these lines to your config.ru.
12
+
13
+ # Unicorn self-process killer
14
+ require 'unicorn/worker_killer'
15
+
16
+ # Max requests per worker
17
+ use UnicornWorkerKiller::MaxRequests, 10240 + Random.rand(10240)
18
+
19
+ # Max memory size (RSS) per worker
20
+ use UnicornWorkerKiller::Oom, (96 + Random.rand(32)) * 1024**2
21
+
22
+ # TODO
23
+ - Get RSS (Resident Set Size) without forking the child process at Mac OS and Windows
@@ -0,0 +1,5 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+ task :default => [:build]
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,111 @@
1
+ module UnicornWorkerKiller
2
+ # Self-destruction by sending the signals to myself. The process sometimes
3
+ # doesn't terminate by SIGTERM, so this tries to send SIGQUIT and SIGKILL
4
+ # if it doesn't finish immediately.
5
+ def self.kill_self(logger, start_time)
6
+ alive_sec = (Time.now - start_time).to_i
7
+
8
+ i = 0
9
+ while true
10
+ i += 1
11
+ sig = :TERM
12
+ if i > 10 # TODO configurable TERM MAX
13
+ sig = :QUIT
14
+ elsif i > 15 # TODO configurable QUIT MAX
15
+ sig = :KILL
16
+ end
17
+
18
+ logger.warn "#{self} send SIGTERM (pid: #{Process.pid})\talive: #{alive_sec} sec (trial #{i})"
19
+ Process.kill sig, Process.pid
20
+
21
+ sleep 1 # TODO configurable sleep
22
+ end
23
+ end
24
+
25
+ module Oom
26
+ # Killing the process must be occurred at the outside of the request. We're
27
+ # using similar techniques used by OobGC, to ensure actual killing doesn't
28
+ # affect the request.
29
+ #
30
+ # @see https://github.com/defunkt/unicorn/blob/master/lib/unicorn/oob_gc.rb#L40
31
+ def self.new(app, memory_size = (1024**3), check_cycle = 16)
32
+ ObjectSpace.each_object(Unicorn::HttpServer) do |s|
33
+ s.extend(self)
34
+ s.instance_variable_set(:@_worker_memory_size, memory_size)
35
+ s.instance_variable_set(:@_worker_check_cycle, check_cycle)
36
+ s.instance_variable_set(:@_worker_check_count, 0)
37
+ end
38
+ app # pretend to be Rack middleware since it was in the past
39
+ end
40
+
41
+ def process_client(client)
42
+ @_worker_process_start ||= Time.now
43
+ super(client) # Unicorn::HttpServer#process_client
44
+
45
+ c = @_worker_check_count + 1
46
+ if c % @_worker_check_cycle == 0
47
+ @_worker_check_count = 0
48
+ if _worker_rss() > @_worker_memory_size
49
+ UnicornWorkerKiller.kill_self(logger, @_worker_process_start)
50
+ end
51
+ else
52
+ @_worker_check_count = c
53
+ end
54
+ end
55
+
56
+ private
57
+ def _worker_rss
58
+ proc_status = "/proc/#{Process.pid}/status"
59
+ if File.exists? proc_status
60
+ open(proc_status).each_line { |l|
61
+ if l.include? 'VmRSS'
62
+ ls = l.split
63
+ if ls.length == 3
64
+ value = ls[1].to_i
65
+ unit = ls[2]
66
+ case unit.downcase
67
+ when 'kb'
68
+ return value*(1024**1)
69
+ when 'mb'
70
+ return value*(1024**2)
71
+ when 'gb'
72
+ return value*(1024**3)
73
+ end
74
+ end
75
+ end
76
+ }
77
+ end
78
+
79
+ # Forking the child process sometimes fails under low memory condition.
80
+ # It would be ideal for not forking the process to get RSS. For Linux,
81
+ # this module reads '/proc/<pid>/status' to get RSS, but not for other
82
+ # environments (e.g. MacOS and Windows).
83
+ kb = `ps -o rss= -p #{Process.pid}`.to_i
84
+ return kb * 1024
85
+ end
86
+ end
87
+
88
+ module MaxRequests
89
+ # Killing the process must be occurred at the outside of the request. We're
90
+ # using similar techniques used by OobGC, to ensure actual killing doesn't
91
+ # affect the request.
92
+ #
93
+ # @see https://github.com/defunkt/unicorn/blob/master/lib/unicorn/oob_gc.rb#L40
94
+ def self.new(app, max_requests = 1024)
95
+ ObjectSpace.each_object(Unicorn::HttpServer) do |s|
96
+ s.extend(self)
97
+ s.instance_variable_set(:@_worker_max_requests, max_requests)
98
+ end
99
+ app # pretend to be Rack middleware since it was in the past
100
+ end
101
+
102
+ def process_client(client)
103
+ @_worker_process_start ||= Time.now
104
+ super(client) # Unicorn::HttpServer#process_client
105
+
106
+ if (@_worker_max_requests -= 1) <= 0
107
+ UnicornWorkerKiller.kill_self(logger, @_worker_process_start)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "unicorn-worker-killer"
6
+ gem.description = "Kill unicorn workers by memory and request counts"
7
+ gem.homepage = "https://github.com/treasure-data/unicorn-worker-killer"
8
+ gem.summary = gem.description
9
+ gem.version = File.read("VERSION").strip
10
+ gem.authors = ["Kazuki Ohta", "Sadayuki Furuhashi"]
11
+ gem.email = ["kazuki.ohta@gmail.com", "frsyuki@gmail.com"]
12
+ gem.has_rdoc = false
13
+ #gem.platform = Gem::Platform::RUBY
14
+ gem.files = `git ls-files`.split("\n")
15
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ gem.require_paths = ['lib']
18
+ gem.add_dependency "unicorn", "~> 4.2.0"
19
+ gem.add_development_dependency "rake", ">= 0.9.2"
20
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unicorn-worker-killer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kazuki Ohta
9
+ - Sadayuki Furuhashi
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-11-18 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: unicorn
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: 4.2.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: 4.2.0
31
+ - !ruby/object:Gem::Dependency
32
+ name: rake
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: 0.9.2
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: 0.9.2
47
+ description: Kill unicorn workers by memory and request counts
48
+ email:
49
+ - kazuki.ohta@gmail.com
50
+ - frsyuki@gmail.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - AUTHORS
56
+ - ChangeLog
57
+ - Gemfile
58
+ - LICENSE
59
+ - README.md
60
+ - Rakefile
61
+ - VERSION
62
+ - lib/unicorn/worker_killer.rb
63
+ - unicorn-worker-killer.gemspec
64
+ homepage: https://github.com/treasure-data/unicorn-worker-killer
65
+ licenses: []
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 1.8.24
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: Kill unicorn workers by memory and request counts
88
+ test_files: []