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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1895ae07adb3fd17811e68a5762343a42a424c55
4
- data.tar.gz: d703399879596243653ed8d4b2a1380fdbc64890
3
+ metadata.gz: 0a33b561b1435c293a784b49b327fd2a9ae097a6
4
+ data.tar.gz: ac36a422c5daff8765e4ef426bae938cdf1b5229
5
5
  SHA512:
6
- metadata.gz: fec5772f43ae44d775b5fce828f47d64555423eb2f271bcee8a006af8aeb0c53127b6d1091b840e85442c393d7cb41f4cc65aad7a363c7e1dfcfc4bf11736948
7
- data.tar.gz: 1753c88baab75f9e0a77f1427b00f2f0d3194b973fcde1886f1d3eb4ef7d67ba7d68716d8e15595dc0696a2f4b9855d136c99d671e72623da501b95f6269bc7b
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 gradually kills your workers before they cause swapping, resulting in better
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
- (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.)
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
- Add these lines near the top of your `config.ru`
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
- # Unicorn self-process killer
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
- use Stalin::Adapter::Rack, (192*mb), (256*mb)
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.2
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
@@ -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 servers that handle SIGQUIT gracefully and spawn new
4
- # worker processes as needed.
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
- 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
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
@@ -5,3 +5,5 @@ module Stalin
5
5
  end
6
6
 
7
7
  require 'stalin/adapter/rack'
8
+ require 'stalin/adapter/unicorn'
9
+ require 'stalin/adapter/puma'
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 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
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 = pid
12
- @tries = 0
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 [:QUIT,:TERM,:KILL] name of signal that we sent
19
+ # @return [Symbol] name of signal that we sent
18
20
  def kill
19
21
  case @tries
20
- when (0...MAX_QUIT)
21
- sig = :QUIT
22
- when (MAX_QUIT...MAX_TERM)
23
- sig = :TERM
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.2 ruby lib
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.2"
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-02-10"
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.2
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-02-12 00:00:00.000000000 Z
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