stalin 0.0.2

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: 1895ae07adb3fd17811e68a5762343a42a424c55
4
+ data.tar.gz: d703399879596243653ed8d4b2a1380fdbc64890
5
+ SHA512:
6
+ metadata.gz: fec5772f43ae44d775b5fce828f47d64555423eb2f271bcee8a006af8aeb0c53127b6d1091b840e85442c393d7cb41f4cc65aad7a363c7e1dfcfc4bf11736948
7
+ data.tar.gz: 1753c88baab75f9e0a77f1427b00f2f0d3194b973fcde1886f1d3eb4ef7d67ba7d68716d8e15595dc0696a2f4b9855d136c99d671e72623da501b95f6269bc7b
data/AUTHORS ADDED
@@ -0,0 +1,4 @@
1
+ Kazuki Ohta <kazuki.ohta _at_ gmail.com>
2
+ Sadayuki FURUHASHI <frsyuki _at_ gmail.com>
3
+ Jonathan Clem <jonathan _at_ jclem.net>
4
+ Tony Spataro <xeger _at_ xeger.net>
data/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ Release 0.0.1 - 2015/02/09
2
+ * initial fork of unicorn-worker-killer
data/LICENSE ADDED
@@ -0,0 +1,93 @@
1
+ ===== Stalin License
2
+
3
+ Copyright (c) 2015 Tony Spataro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ Stalin is derived from https://github.com/kzk/unicorn-worker-killer
25
+ including some code which was copied verbatim. The licensing
26
+ information for unicorn-worker-killer is reproduced below.
27
+
28
+ ===== unicorn-worker-killer license
29
+
30
+ Unicorn is copyrighted free software by all contributors, see logs in
31
+ revision control for names and email addresses of all of them.
32
+
33
+ You can redistribute it and/or modify it under either the terms of the
34
+ GNU General Public License (GPL) as published by the Free Software
35
+ Foundation (FSF), version {3.0}[http://www.gnu.org/licenses/gpl-3.0.txt]
36
+ or version {2.0}[http://www.gnu.org/licenses/gpl-2.0.txt]
37
+ or the Ruby-specific license terms (see below).
38
+
39
+ The unicorn project leader (Eric Wong) reserves the right to add future
40
+ versions of the GPL (and no other licenses) as published by the FSF to
41
+ the licensing terms.
42
+
43
+ === Ruby-specific terms (if you're not using the GPLv2 or GPLv3)
44
+
45
+ 1. You may make and give away verbatim copies of the source form of the
46
+ software without restriction, provided that you duplicate all of the
47
+ original copyright notices and associated disclaimers.
48
+
49
+ 2. You may modify your copy of the software in any way, provided that
50
+ you do at least ONE of the following:
51
+
52
+ a) place your modifications in the Public Domain or otherwise make them
53
+ Freely Available, such as by posting said modifications to Usenet or an
54
+ equivalent medium, or by allowing the author to include your
55
+ modifications in the software.
56
+
57
+ b) use the modified software only within your corporation or
58
+ organization.
59
+
60
+ c) rename any non-standard executables so the names do not conflict with
61
+ standard executables, which must also be provided.
62
+
63
+ d) make other distribution arrangements with the author.
64
+
65
+ 3. You may distribute the software in object code or executable
66
+ form, provided that you do at least ONE of the following:
67
+
68
+ a) distribute the executables and library files of the software,
69
+ together with instructions (in the manual page or equivalent) on where
70
+ to get the original distribution.
71
+
72
+ b) accompany the distribution with the machine-readable source of the
73
+ software.
74
+
75
+ c) give non-standard executables non-standard names, with
76
+ instructions on where to get the original software distribution.
77
+
78
+ d) make other distribution arrangements with the author.
79
+
80
+ 4. You may modify and include the part of the software into any other
81
+ software (possibly commercial). But some files in the distribution
82
+ are not written by the author, so that they are not under this terms.
83
+
84
+ 5. The scripts and library files supplied as input to or produced as
85
+ output from the software do not automatically fall under the
86
+ copyright of the software, but belong to whomever generated them,
87
+ and may be sold commercially, and may be aggregated with this
88
+ software.
89
+
90
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
91
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
92
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
93
+ PURPOSE.
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # stalin
2
+
3
+ Given Ruby's proclivity for heap fragmentation, Web application worker processes tend to exhaust
4
+ all available memory unless the server is restarted periodically. When all of the workers restart
5
+ at once, downtime or bad request throughput may result.
6
+
7
+ Stalin is a gem that gradually kills your workers before they cause swapping, resulting in better
8
+ availability for your application. Its design goals are modularity and compatibility with a range
9
+ of platforms and app servers.
10
+
11
+ Metrics include:
12
+ - Resident Set Size (RSS)
13
+
14
+ Data sources include:
15
+ - Linux ProcFS
16
+ - Generic ps
17
+
18
+ Supported app servers include:
19
+ - Rainbows (via Rack middleware + SIGQUIT)
20
+ - Unicorn (via Rack middleware + SIGQUIT)
21
+
22
+ (As you can see, we are far short of our _goal_ to support many servers! More to come as needed;
23
+ I'm happy to take your contributions.)
24
+
25
+ # Installation
26
+
27
+ Just include stalin in your Gemfile.
28
+
29
+ gem 'stalin'
30
+
31
+ # Usage
32
+
33
+ Add these lines near the top of your `config.ru`
34
+
35
+ # Unicorn self-process killer
36
+ require 'stalin'
37
+
38
+ # Max memory size (RSS) per worker
39
+ mb = 1024**2
40
+ use Stalin::Adapter::Rack, (192*mb), (256*mb)
41
+
42
+ # Tuning
43
+
44
+ TODO
45
+
46
+ # Special Thanks
47
+
48
+ - [@kzk](http://github.com/kzk/) for the [unicorn-worker-killer] gem which this is derived from
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'bundler/setup'
2
+
3
+ require 'jeweler'
4
+ Jeweler::Tasks.new do |gem|
5
+ gem.name = "stalin"
6
+ gem.homepage = "https://github.com/xeger/stalin"
7
+ gem.license = "MIT"
8
+ gem.summary = %Q{Kill rack }
9
+ gem.description = %Q{Kill Web application workers based on arbitrary conditions.}
10
+ gem.email = "xeger@xeger.net"
11
+ gem.authors = ["Tony Spataro"]
12
+ gem.files.exclude ".rspec"
13
+ gem.files.exclude "Gemfile*"
14
+ gem.files.exclude "fixtures/**/*"
15
+ gem.files.exclude "spec/**/*"
16
+ end
17
+ Jeweler::RubygemsDotOrgTasks.new
18
+
19
+ CLEAN.include('pkg')
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.2
data/lib/stalin.rb ADDED
@@ -0,0 +1,10 @@
1
+ module Stalin
2
+ # Exception that watchers raise when they are not supported on the current platform.
3
+ class Unsupported < NotImplementedError
4
+ end
5
+
6
+ end
7
+
8
+ require 'stalin/killer'
9
+ require 'stalin/watcher'
10
+ require 'stalin/adapter'
@@ -0,0 +1,7 @@
1
+ module Stalin
2
+ module Adapter
3
+
4
+ end
5
+ end
6
+
7
+ require 'stalin/adapter/rack'
@@ -0,0 +1,67 @@
1
+ module Stalin::Adapter
2
+ # A low-tech but reliable solution that invokes stalin using Rack middleware.
3
+ # This is suitable for servers that handle SIGQUIT gracefully and spawn new
4
+ # worker processes as needed.
5
+ class Rack
6
+ # Conversion constant for human-readable memory amounts in log messages.
7
+ MB = Float(1024**2)
8
+
9
+ # Create a middleware instance.
10
+ #
11
+ # @param [#call] app
12
+ # @param [Integer] min lower-bound worker memory consumption before restart
13
+ # @param [Integer] max upper-bound worker memory consumption before restart
14
+ # @param [Integer] cycle how frequently to check memory consumption (# requests)
15
+ # @param [Boolean] verbose log extra information
16
+ def initialize(app, min=1024**3, max=2*1024**3, cycle=16, verbose=false)
17
+ @app = app
18
+ @min = min
19
+ @max = max
20
+ @cycle = cycle
21
+ @verbose = verbose
22
+ end
23
+
24
+ def call(env)
25
+ result = @app.call(env)
26
+
27
+ logger = logger_for(env)
28
+
29
+ begin
30
+ @lim ||= @min + randomize(@max - @min + 1)
31
+ @req ||= 0
32
+ @req += 1
33
+
34
+ if @req % @cycle == 0
35
+ @req = 0
36
+ @watcher ||= ::Stalin::Watcher.new(Process.pid)
37
+ @killer ||= ::Stalin::Killer.new(Process.pid)
38
+ if (used = @watcher.watch) > @lim
39
+ sig = @killer.kill
40
+ @watcher.watch
41
+ logger.info "stalin (pid: %d) send SIG%s; memory usage %.1f MB > %.1f MB" %
42
+ [Process.pid, sig, used / MB, @lim / MB]
43
+ @cycle = 2
44
+ elsif @verbose
45
+ logger.info "stalin (pid: %d) soldiers on; memory usage %.1f MB < %.1f MB" %
46
+ [Process.pid, used / MB, @lim / MB]
47
+ end
48
+ end
49
+ rescue Exception => e
50
+ logger.error "stalin (pid: %d) ERROR %s: %s (%s)" %
51
+ [Process.pid, e.class.name, e.message, e.backtrace.first]
52
+ end
53
+
54
+ result
55
+ end
56
+
57
+ private
58
+
59
+ def randomize(integer)
60
+ RUBY_VERSION > "1.9" ? Random.rand(integer.abs) : rand(integer)
61
+ end
62
+
63
+ def logger_for(env)
64
+ env['rack.logger'] || (@logger ||= Logger.new(STDERR))
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,33 @@
1
+ module Stalin
2
+ # Kill a process by sending SIGQUIT several times, then SIGTERM, and finally SIGKILL.
3
+ class Killer
4
+ # Number of cumulative tries to send SIGQUIT before we escalate to SIGTERM
5
+ MAX_QUIT = 10
6
+ # Number of cumulative tries before we escalate to SIGKILL
7
+ MAX_TERM = 15
8
+
9
+ # @param [Integer] pid target process ID
10
+ def initialize(pid)
11
+ @pid = pid
12
+ @tries = 0
13
+ end
14
+
15
+ # Try to kill the target process by sending it a shutdown signal.
16
+ #
17
+ # @return [:QUIT,:TERM,:KILL] name of signal that we sent
18
+ def kill
19
+ case @tries
20
+ when (0...MAX_QUIT)
21
+ sig = :QUIT
22
+ when (MAX_QUIT...MAX_TERM)
23
+ sig = :TERM
24
+ else
25
+ sig = :KILL
26
+ end
27
+
28
+ @tries += 1
29
+ Process.kill(sig, @pid)
30
+ sig
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ module Stalin
2
+ module Watcher
3
+ def self.new(pid)
4
+ it = nil
5
+
6
+ constants.each do |konst|
7
+ begin
8
+ konst = const_get(konst)
9
+ it = konst.new(pid)
10
+ break
11
+ rescue Stalin::Unsupported
12
+ next
13
+ end
14
+ end
15
+
16
+ it || raise(Stalin::Unsupported, "No compatible Watcher was found among #{constants}")
17
+ end
18
+ end
19
+ end
20
+
21
+ require 'stalin/watcher/linux_proc_statm'
22
+ require 'stalin/watcher/unix_ps'
@@ -0,0 +1,30 @@
1
+ module Stalin::Watcher
2
+ # Watcher that uses procfs (specifically /proc/<pid>/statm) to determine memory usage.
3
+ class LinuxProcStatm
4
+ # @raise [Stalin::Unsupported] if watcher cannot be instantiated on current platform
5
+ # @param [Integer] pid target process ID
6
+ def initialize(pid)
7
+ @statm = "/proc/%d/statm" % [pid]
8
+ raise Stalin::Unsupported, "Unreadable or nonexistent file: #{@statm}" unless File.readable?(@statm)
9
+
10
+ page_size = `getconf PAGESIZE`
11
+ @page_size = Integer(page_size) rescue nil
12
+ raise Stalin::Unsupported, "Cannot determine page size: #{page_size}" unless $?.success? && @page_size.kind_of?(Integer)
13
+ end
14
+
15
+ # Report on memory usage.
16
+ #
17
+ # @return [Integer,nil] target process' memory usage in bytes, nil if process not found
18
+ def watch
19
+ vsz, rss, shared = File.read(@statm).split(' ')
20
+ vsz = Integer(vsz) * @page_size
21
+ rss = Integer(rss) * @page_size
22
+ shared = Integer(shared) * @page_size
23
+
24
+ rss
25
+ rescue SystemCallError
26
+ # assume any failure means that process has gone away
27
+ nil
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ module Stalin::Watcher
2
+ class UnixPs
3
+ # @raise [Stalin::Unsupported] if watcher cannot be instantiated on current platform
4
+ # @param [Integer] pid target process ID
5
+ def initialize(pid)
6
+ begin
7
+ ps(Process.pid)
8
+ rescue StandardError => e
9
+ raise Stalin::Unsupported, "ps failed: #{e.message}"
10
+ end
11
+
12
+ @pid = pid
13
+ end
14
+
15
+ # Report on memory usage.
16
+ #
17
+ # @return [Integer,nil] target process' memory usage in bytes, nil if process not found
18
+ def watch
19
+ ps(@pid)
20
+ rescue ArgumentError
21
+ nil
22
+ end
23
+
24
+ private
25
+
26
+ # Report on memory usage.
27
+ #
28
+ # @return [Integer] target process' memory usage in bytes
29
+ def ps(pid)
30
+ Integer(`#{"ps -o rss= -p %d" % [pid]}`) * 1024
31
+ end
32
+ end
33
+ end
data/stalin.gemspec ADDED
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: stalin 0.0.2 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "stalin"
9
+ s.version = "0.0.2"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
13
+ s.authors = ["Tony Spataro"]
14
+ s.date = "2015-02-10"
15
+ s.description = "Kill Web application workers based on arbitrary conditions."
16
+ s.email = "xeger@xeger.net"
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ "AUTHORS",
23
+ "CHANGELOG.md",
24
+ "LICENSE",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "lib/stalin.rb",
29
+ "lib/stalin/adapter.rb",
30
+ "lib/stalin/adapter/rack.rb",
31
+ "lib/stalin/killer.rb",
32
+ "lib/stalin/watcher.rb",
33
+ "lib/stalin/watcher/linux_proc_statm.rb",
34
+ "lib/stalin/watcher/unix_ps.rb",
35
+ "stalin.gemspec"
36
+ ]
37
+ s.homepage = "https://github.com/xeger/stalin"
38
+ s.licenses = ["MIT"]
39
+ s.rubygems_version = "2.2.2"
40
+ s.summary = "Kill rack"
41
+
42
+ if s.respond_to? :specification_version then
43
+ s.specification_version = 4
44
+
45
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
+ s.add_development_dependency(%q<jeweler>, ["~> 2.0"])
47
+ s.add_development_dependency(%q<rspec>, ["~> 3.0"])
48
+ s.add_development_dependency(%q<sinatra>, ["~> 1.4"])
49
+ s.add_development_dependency(%q<unicorn>, ["~> 4.8"])
50
+ else
51
+ s.add_dependency(%q<jeweler>, ["~> 2.0"])
52
+ s.add_dependency(%q<rspec>, ["~> 3.0"])
53
+ s.add_dependency(%q<sinatra>, ["~> 1.4"])
54
+ s.add_dependency(%q<unicorn>, ["~> 4.8"])
55
+ end
56
+ else
57
+ s.add_dependency(%q<jeweler>, ["~> 2.0"])
58
+ s.add_dependency(%q<rspec>, ["~> 3.0"])
59
+ s.add_dependency(%q<sinatra>, ["~> 1.4"])
60
+ s.add_dependency(%q<unicorn>, ["~> 4.8"])
61
+ end
62
+ end
63
+
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stalin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Tony Spataro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jeweler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sinatra
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.4'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.4'
55
+ - !ruby/object:Gem::Dependency
56
+ name: unicorn
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.8'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4.8'
69
+ description: Kill Web application workers based on arbitrary conditions.
70
+ email: xeger@xeger.net
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files:
74
+ - LICENSE
75
+ - README.md
76
+ files:
77
+ - AUTHORS
78
+ - CHANGELOG.md
79
+ - LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - VERSION
83
+ - lib/stalin.rb
84
+ - lib/stalin/adapter.rb
85
+ - lib/stalin/adapter/rack.rb
86
+ - lib/stalin/killer.rb
87
+ - lib/stalin/watcher.rb
88
+ - lib/stalin/watcher/linux_proc_statm.rb
89
+ - lib/stalin/watcher/unix_ps.rb
90
+ - stalin.gemspec
91
+ homepage: https://github.com/xeger/stalin
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.2.2
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Kill rack
115
+ test_files: []