stalin 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +20 -6
- data/VERSION +1 -1
- data/lib/stalin/adapter/puma.rb +28 -0
- data/lib/stalin/adapter/rack.rb +51 -10
- data/lib/stalin/adapter/unicorn.rb +15 -0
- data/lib/stalin/adapter.rb +2 -0
- data/lib/stalin/killer.rb +15 -13
- data/stalin.gemspec +11 -3
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0a33b561b1435c293a784b49b327fd2a9ae097a6
|
4
|
+
data.tar.gz: ac36a422c5daff8765e4ef426bae938cdf1b5229
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66788da1669aee34f376f25242b212e620f38914b92fe3b13abac429478bcd54ca2958448d282df3e9437ac7296f8cb80d4ec322e5689c33b6985c7481ff4bc4
|
7
|
+
data.tar.gz: c34ca0a488667afc8023f68bb4514cc41b6df9a59016b787ddf38ec4fcd506c28b61ef7fb8f7011d31279bebde60f9bda092b09141b0bf0134b89016894049d4
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Given Ruby's proclivity for heap fragmentation, Web application worker processes
|
|
4
4
|
all available memory unless the server is restarted periodically. When all of the workers restart
|
5
5
|
at once, downtime or bad request throughput may result.
|
6
6
|
|
7
|
-
Stalin is a gem that
|
7
|
+
Stalin is a gem that gracefully kills your workers before they cause swapping, resulting in better
|
8
8
|
availability for your application. Its design goals are modularity and compatibility with a range
|
9
9
|
of platforms and app servers.
|
10
10
|
|
@@ -18,9 +18,13 @@ Data sources include:
|
|
18
18
|
Supported app servers include:
|
19
19
|
- Rainbows (via Rack middleware + SIGQUIT)
|
20
20
|
- Unicorn (via Rack middleware + SIGQUIT)
|
21
|
+
- Puma (via Rack middleware + SIGTERM/SIGUSR2 depending on execution model)
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
Servers known NOT to work:
|
24
|
+
- Thin (no supervisor process; need an adapter that execs or something)
|
25
|
+
|
26
|
+
As you can see, we are far short of our _goal_ to support many servers! More to come as needed;
|
27
|
+
let me know what you need!
|
24
28
|
|
25
29
|
# Installation
|
26
30
|
|
@@ -30,14 +34,24 @@ Just include stalin in your Gemfile.
|
|
30
34
|
|
31
35
|
# Usage
|
32
36
|
|
33
|
-
|
37
|
+
Decide on which application server you will use. Add some lines to your `config.ru` to
|
38
|
+
install a suitable Stalin middleware.
|
34
39
|
|
35
|
-
#
|
40
|
+
# Gem that kills app processes when their heap becomes too fragmented.
|
36
41
|
require 'stalin'
|
37
42
|
|
38
43
|
# Max memory size (RSS) per worker
|
39
44
|
mb = 1024**2
|
40
|
-
|
45
|
+
|
46
|
+
# Or Stalin::Adapter::Puma
|
47
|
+
use Stalin::Adapter::Unicorn, (192*mb), (256*mb)
|
48
|
+
|
49
|
+
If you instantiate Stalin::Adapter::Rack directly, you have two choices:
|
50
|
+
- Pass three parameters (app, graceful-shutdown signal and abrupt-shutdown signal) to decide on signalling behavior yourself
|
51
|
+
- Pass one parameter (app) to let Stalin decide which adapter to use based on which server is resident in memory
|
52
|
+
|
53
|
+
Instantiating the Rack middleware with one parameter is deprecated; we'd much rather you be
|
54
|
+
explicit about your application server than rely on our heuristic!
|
41
55
|
|
42
56
|
# Tuning
|
43
57
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Stalin::Adapter
|
2
|
+
# A Stalin adapter that is suitable for use in Puma worker processes. Puma uses SIGTERM
|
3
|
+
# to initiate orderly shutdown and seems to respond to SIGQUIT with an abrupt shutdown.
|
4
|
+
class Puma < Rack
|
5
|
+
# Create a middleware instance.
|
6
|
+
#
|
7
|
+
# @param [#call] app inner Rack application
|
8
|
+
# @param [Integer] min lower-bound worker memory consumption before restart
|
9
|
+
# @param [Integer] max upper-bound worker memory consumption before restart
|
10
|
+
# @param [Integer] cycle how frequently to check memory consumption (# requests)
|
11
|
+
# @param [Boolean] verbose log extra information
|
12
|
+
def initialize(app, min=1024**3, max=2*1024**3, cycle=16, verbose=false)
|
13
|
+
cli = nil
|
14
|
+
ObjectSpace.each_object(::Puma::CLI) { |o| cli = o }
|
15
|
+
|
16
|
+
raise RuntimeError, "Puma does not appear to be active; no instances of Puma::CLI reside in memory" unless cli
|
17
|
+
|
18
|
+
if cli.clustered? && cli.options[:workers] > 0
|
19
|
+
# We're clustered, so workers can SIGTERM themselves to cause a restart
|
20
|
+
super(app, :TERM, :QUIT, min, max, cycle, verbose)
|
21
|
+
else
|
22
|
+
# We're running in single mode; our one-and-only process must USR2 to restart
|
23
|
+
# itself
|
24
|
+
super(app, :USR2, :USR1, min, max, cycle, verbose)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/stalin/adapter/rack.rb
CHANGED
@@ -1,24 +1,65 @@
|
|
1
1
|
module Stalin::Adapter
|
2
2
|
# A low-tech but reliable solution that invokes stalin using Rack middleware.
|
3
|
-
# This is suitable for
|
4
|
-
#
|
3
|
+
# This is suitable for application srvers that use a master-and-workers
|
4
|
+
# process architecture, wohse workers respond to a graceful shutdown signal,
|
5
|
+
# and whose masters spawn new workers as needed.
|
6
|
+
#
|
7
|
+
# This functions as a base class for server-specific middlewares, and can be used if
|
8
|
+
# there is an app server that uses a signalling strategy not explicitly supported by Stalin.
|
5
9
|
class Rack
|
6
10
|
# Conversion constant for human-readable memory amounts in log messages.
|
7
11
|
MB = Float(1024**2)
|
8
12
|
|
13
|
+
# Construct a new middleware. If all six arguments are passed, construct an instance
|
14
|
+
# of this class; otherwise, use a heuristic to construct an instance of a suitable
|
15
|
+
# derived class.
|
16
|
+
#
|
17
|
+
# @raise [ArgumentError] if an incorrect number of arguments is passed
|
18
|
+
# @raise [RuntimeError] if 0..5 arguments and the heuristic can't figure out which signals to use
|
19
|
+
def self.new(*args)
|
20
|
+
case args.length
|
21
|
+
when 7
|
22
|
+
# Construct an instance of this (or a derived) class with signals explicitly specified.
|
23
|
+
super
|
24
|
+
when 3..6
|
25
|
+
# Warn that this shim will go away in v2
|
26
|
+
warn "Rack.new with fewer than 7 arguments is deprecated; please instantiate a derived class e.g. Unicorn.new or Puma.new"
|
27
|
+
|
28
|
+
# Use a heuristic to decide on the correct adapter and instantiate a derived class.
|
29
|
+
if defined?(::Unicorn)
|
30
|
+
middleware = Unicorn.allocate
|
31
|
+
elsif defined?(::Puma)
|
32
|
+
middleware = Puma.allocate
|
33
|
+
else
|
34
|
+
raise RuntimeError, "Cannot determine a suitable Stalin adapter; please instantiate this class with six arguments"
|
35
|
+
end
|
36
|
+
|
37
|
+
# Initialize our new object (ugh)
|
38
|
+
middleware.instance_eval { initialize(*args) }
|
39
|
+
middleware
|
40
|
+
else
|
41
|
+
raise ArgumentError, "Wrong number of arguments (#{args.length} for 3..7)"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
9
45
|
# Create a middleware instance.
|
10
46
|
#
|
11
|
-
# @param [#call] app
|
47
|
+
# @param [#call] app inner Rack application
|
48
|
+
# @param [Symbol] graceful name of graceful-shutdown signal
|
49
|
+
# @param [Symbol] abrupt name of abrupt-shutdown signal
|
12
50
|
# @param [Integer] min lower-bound worker memory consumption before restart
|
13
51
|
# @param [Integer] max upper-bound worker memory consumption before restart
|
14
52
|
# @param [Integer] cycle how frequently to check memory consumption (# requests)
|
15
53
|
# @param [Boolean] verbose log extra information
|
16
|
-
|
17
|
-
|
18
|
-
@
|
19
|
-
@
|
20
|
-
@
|
21
|
-
@
|
54
|
+
# @param [Array] signals pair of two Symbol signal-names: one for "graceful shutdown please" and one for "terminate immediately"
|
55
|
+
def initialize(app, graceful, abrupt, min=1024**3, max=2*1024**3, cycle=16, verbose=false)
|
56
|
+
@app = app
|
57
|
+
@graceful = graceful
|
58
|
+
@abrupt = abrupt
|
59
|
+
@min = min
|
60
|
+
@max = max
|
61
|
+
@cycle = cycle
|
62
|
+
@verbose = verbose
|
22
63
|
end
|
23
64
|
|
24
65
|
def call(env)
|
@@ -34,7 +75,7 @@ module Stalin::Adapter
|
|
34
75
|
if @req % @cycle == 0
|
35
76
|
@req = 0
|
36
77
|
@watcher ||= ::Stalin::Watcher.new(Process.pid)
|
37
|
-
@killer ||= ::Stalin::Killer.new(Process.pid)
|
78
|
+
@killer ||= ::Stalin::Killer.new(Process.pid, @graceful, @abrupt)
|
38
79
|
if (used = @watcher.watch) > @lim
|
39
80
|
sig = @killer.kill
|
40
81
|
@watcher.watch
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Stalin::Adapter
|
2
|
+
# A Stalin adapter that is suitable for use in Unicorn worker processes.
|
3
|
+
class Unicorn < Rack
|
4
|
+
# Create a middleware instance.
|
5
|
+
#
|
6
|
+
# @param [#call] app inner Rack application
|
7
|
+
# @param [Integer] min lower-bound worker memory consumption before restart
|
8
|
+
# @param [Integer] max upper-bound worker memory consumption before restart
|
9
|
+
# @param [Integer] cycle how frequently to check memory consumption (# requests)
|
10
|
+
# @param [Boolean] verbose log extra information
|
11
|
+
def initialize(app, min=1024**3, max=2*1024**3, cycle=16, verbose=false)
|
12
|
+
super(app, :QUIT, :TERM, min, max, cycle, verbose)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/stalin/adapter.rb
CHANGED
data/lib/stalin/killer.rb
CHANGED
@@ -1,26 +1,28 @@
|
|
1
1
|
module Stalin
|
2
2
|
# Kill a process by sending SIGQUIT several times, then SIGTERM, and finally SIGKILL.
|
3
3
|
class Killer
|
4
|
-
# Number of cumulative tries
|
5
|
-
|
6
|
-
# Number of cumulative tries before we escalate to SIGKILL
|
7
|
-
|
4
|
+
# Number of cumulative shutdown tries before we escalate to abrupt-shutdown
|
5
|
+
MAX_GRACEFUL = 10
|
6
|
+
# Number of cumulative shutdown tries before we escalate to untrappable SIGKILL
|
7
|
+
MAX_ABRUPT = 15
|
8
8
|
|
9
9
|
# @param [Integer] pid target process ID
|
10
|
-
def initialize(pid)
|
11
|
-
@pid
|
12
|
-
@
|
10
|
+
def initialize(pid, graceful, abrupt)
|
11
|
+
@pid = pid
|
12
|
+
@graceful = graceful
|
13
|
+
@abrupt = abrupt
|
14
|
+
@tries = 0
|
13
15
|
end
|
14
16
|
|
15
17
|
# Try to kill the target process by sending it a shutdown signal.
|
16
18
|
#
|
17
|
-
# @return [
|
19
|
+
# @return [Symbol] name of signal that we sent
|
18
20
|
def kill
|
19
21
|
case @tries
|
20
|
-
when (0...
|
21
|
-
sig =
|
22
|
-
when (
|
23
|
-
sig =
|
22
|
+
when (0...MAX_GRACEFUL)
|
23
|
+
sig = @graceful
|
24
|
+
when (MAX_GRACEFUL...MAX_ABRUPT)
|
25
|
+
sig = @abrupt
|
24
26
|
else
|
25
27
|
sig = :KILL
|
26
28
|
end
|
@@ -30,4 +32,4 @@ module Stalin
|
|
30
32
|
sig
|
31
33
|
end
|
32
34
|
end
|
33
|
-
end
|
35
|
+
end
|
data/stalin.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: stalin 0.0
|
5
|
+
# stub: stalin 0.1.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "stalin"
|
9
|
-
s.version = "0.0
|
9
|
+
s.version = "0.1.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Tony Spataro"]
|
14
|
-
s.date = "2015-
|
14
|
+
s.date = "2015-04-17"
|
15
15
|
s.description = "Kill Web application workers based on arbitrary conditions."
|
16
16
|
s.email = "xeger@xeger.net"
|
17
17
|
s.extra_rdoc_files = [
|
@@ -27,7 +27,9 @@ Gem::Specification.new do |s|
|
|
27
27
|
"VERSION",
|
28
28
|
"lib/stalin.rb",
|
29
29
|
"lib/stalin/adapter.rb",
|
30
|
+
"lib/stalin/adapter/puma.rb",
|
30
31
|
"lib/stalin/adapter/rack.rb",
|
32
|
+
"lib/stalin/adapter/unicorn.rb",
|
31
33
|
"lib/stalin/killer.rb",
|
32
34
|
"lib/stalin/watcher.rb",
|
33
35
|
"lib/stalin/watcher/linux_proc_statm.rb",
|
@@ -47,17 +49,23 @@ Gem::Specification.new do |s|
|
|
47
49
|
s.add_development_dependency(%q<rspec>, ["~> 3.0"])
|
48
50
|
s.add_development_dependency(%q<sinatra>, ["~> 1.4"])
|
49
51
|
s.add_development_dependency(%q<unicorn>, ["~> 4.8"])
|
52
|
+
s.add_development_dependency(%q<thin>, ["~> 1.6"])
|
53
|
+
s.add_development_dependency(%q<puma>, ["~> 2.11"])
|
50
54
|
else
|
51
55
|
s.add_dependency(%q<jeweler>, ["~> 2.0"])
|
52
56
|
s.add_dependency(%q<rspec>, ["~> 3.0"])
|
53
57
|
s.add_dependency(%q<sinatra>, ["~> 1.4"])
|
54
58
|
s.add_dependency(%q<unicorn>, ["~> 4.8"])
|
59
|
+
s.add_dependency(%q<thin>, ["~> 1.6"])
|
60
|
+
s.add_dependency(%q<puma>, ["~> 2.11"])
|
55
61
|
end
|
56
62
|
else
|
57
63
|
s.add_dependency(%q<jeweler>, ["~> 2.0"])
|
58
64
|
s.add_dependency(%q<rspec>, ["~> 3.0"])
|
59
65
|
s.add_dependency(%q<sinatra>, ["~> 1.4"])
|
60
66
|
s.add_dependency(%q<unicorn>, ["~> 4.8"])
|
67
|
+
s.add_dependency(%q<thin>, ["~> 1.6"])
|
68
|
+
s.add_dependency(%q<puma>, ["~> 2.11"])
|
61
69
|
end
|
62
70
|
end
|
63
71
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stalin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Spataro
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jeweler
|
@@ -66,6 +66,34 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '4.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: thin
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.6'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.6'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: puma
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.11'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.11'
|
69
97
|
description: Kill Web application workers based on arbitrary conditions.
|
70
98
|
email: xeger@xeger.net
|
71
99
|
executables: []
|
@@ -82,7 +110,9 @@ files:
|
|
82
110
|
- VERSION
|
83
111
|
- lib/stalin.rb
|
84
112
|
- lib/stalin/adapter.rb
|
113
|
+
- lib/stalin/adapter/puma.rb
|
85
114
|
- lib/stalin/adapter/rack.rb
|
115
|
+
- lib/stalin/adapter/unicorn.rb
|
86
116
|
- lib/stalin/killer.rb
|
87
117
|
- lib/stalin/watcher.rb
|
88
118
|
- lib/stalin/watcher/linux_proc_statm.rb
|