stalin 0.0.2 → 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.
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