alicorn 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,8 +1,30 @@
1
- = alicorn
1
+ = Alicorn: Auto-scaler for Unicorn webserver
2
2
 
3
- An experimental auto-scaler for Eric Wong's {unicorn}[http://unicorn.bogomips.org] webserver.
3
+ An experimental auto-scaler for Eric Wong's {Unicorn}[http://unicorn.bogomips.org]
4
+ webserver. Designed for Unicorn webservers running with the {Raindrops}[raindrops.bogomips.org]
5
+ middleware enabled. It depends on the "active" and "queued" fields reported by
6
+ Raindrops, and so only works on Linux.
4
7
 
5
- == Contributing to alicorn
8
+ == Usage
9
+
10
+ Alicorn ships with two executables, alicorn and alicorn-profiler.
11
+
12
+ alicorn: this is the main command-line utility: run alicorn --help to see the
13
+ list of options and learn how to configure it.
14
+
15
+ alicorn-profiler: this is a tool to help determine useful parameters for alicorn.
16
+ it's recommended that you run alicorn in dry-run and verbose mode for awhile,
17
+ collect the resulting logs, and feed those through alicorn-profiler to get a
18
+ handle on what settings you'll need. Same story, run alicorn-profiler --help to
19
+ figure out what the options are and how you can tweak them.
20
+
21
+ == Versioning
22
+
23
+ Alicorn attempts to follow the {Semantic Versioning Specification}[semver.org].
24
+ Every release in the 0.x series should be considered a DEVELOPMENT version.
25
+ The public API should not be considered stable.
26
+
27
+ == Contributing
6
28
 
7
29
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
8
30
  * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
@@ -12,8 +34,14 @@ An experimental auto-scaler for Eric Wong's {unicorn}[http://unicorn.bogomips.or
12
34
  * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
35
  * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
36
 
37
+ == Credits
38
+
39
+ Alicorn is maintained by {Ben Somers}[http://github.com/bensomers] and is
40
+ funded by {Rafter}[http://www.rafter.com]. Huge thanks to Eric
41
+ Wong and the rest of the {Unicorn}[http://unicorn.bogomips.org] and
42
+ {Raindrops}[http://raindrops.bogomips.org] contributors.
43
+
15
44
  == Copyright
16
45
 
17
46
  Copyright (c) 2012 Ben Somers. See LICENSE.txt for
18
47
  further details.
19
-
data/alicorn.gemspec CHANGED
@@ -5,14 +5,14 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "alicorn"
8
- s.version = "0.2.2"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Ben Somers"]
12
- s.date = "2012-05-22"
12
+ s.date = "2012-05-29"
13
13
  s.description = "Highly configurable dumb auto-scaler for managing unicorn web servers"
14
14
  s.email = "somers.ben@gmail.com"
15
- s.executables = ["alicorn", "alicorn_profiler"]
15
+ s.executables = ["alicorn", "alicorn-profiler"]
16
16
  s.extra_rdoc_files = [
17
17
  "LICENSE.txt",
18
18
  "README.rdoc"
@@ -26,9 +26,11 @@ Gem::Specification.new do |s|
26
26
  "Rakefile",
27
27
  "alicorn.gemspec",
28
28
  "bin/alicorn",
29
- "bin/alicorn_profiler",
29
+ "bin/alicorn-profiler",
30
+ "example/config.yml",
30
31
  "lib/alicorn.rb",
31
32
  "lib/alicorn/dataset.rb",
33
+ "lib/alicorn/errors.rb",
32
34
  "lib/alicorn/log_parser.rb",
33
35
  "lib/alicorn/profiler.rb",
34
36
  "lib/alicorn/scaler.rb",
data/bin/alicorn CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  $:.push File.join(File.dirname(__FILE__),'..','lib')
3
+ require 'yaml'
3
4
  require 'optparse'
4
5
  require 'alicorn'
5
6
 
@@ -56,6 +57,10 @@ data sets. \n\nUsage: alicorn [options]"
56
57
  options[:log_path] = v
57
58
  end
58
59
 
60
+ opts.on("-c", "--config PATH", String, "path for stored options - write a yaml file containing all options") do |v|
61
+ options[:config_path] = v
62
+ end
63
+
59
64
  opts.on("-v", "--[no-]verbose", "turn on to write profiling info") do
60
65
  options[:verbose] = true
61
66
  end
@@ -65,6 +70,14 @@ data sets. \n\nUsage: alicorn [options]"
65
70
  end
66
71
 
67
72
  end.parse!
73
+
74
+ if options[:config_path]
75
+ options = YAML.load_file(File.open(options[:config_path]))
76
+ # I prefer symbol keys, but nobody ever writes yamls with symbol keys, so convert them
77
+ options = options.inject({}) { |memo, pair| memo.store(pair.first.to_sym, pair.last); memo }
78
+ end
79
+
68
80
  raise OptionParser::MissingArgument.new("--max-workers is a mandatory argument") unless options[:max_workers]
69
81
 
82
+
70
83
  Alicorn::Scaler.new(options).scale
File without changes
@@ -0,0 +1,11 @@
1
+ :min_workers: 2
2
+ :max_workers: 25
3
+ :buffer: 2
4
+ :target_ratio: 1.3
5
+ :url: localhost/_raindrops
6
+ :sample_count: 30
7
+ :delay: 0.5
8
+ :app_name: unicorn
9
+ :log_path: test.log
10
+ :verbose: false
11
+ :dry_run: true
@@ -0,0 +1,6 @@
1
+ module Alicorn
2
+ class AlicornError < StandardError; end
3
+ class NoUnicornsError < AlicornError; end
4
+ class NoMasterError < AlicornError; end
5
+ class AmbiguousMasterError < AlicornError; end
6
+ end
@@ -1,6 +1,7 @@
1
1
  require 'curl'
2
2
  require 'logger'
3
3
  require 'alicorn/dataset'
4
+ require 'alicorn/errors'
4
5
 
5
6
  module Alicorn
6
7
  class Scaler
@@ -40,9 +41,9 @@ module Alicorn
40
41
  sleep(signal_delay) # Make sure unicorn doesn't discard repeated signals
41
42
  end
42
43
  end
43
- rescue Exception => e
44
+ rescue StandardError => e
44
45
  logger.error "exception occurred: #{e.class}\n\n#{e.message}"
45
- raise e
46
+ raise e unless e.is_a?(AmbiguousMasterError) # AmbiguousMasters are fine, usually just indicate a restart
46
47
  end
47
48
 
48
49
  def auto_scale(data, worker_count)
@@ -106,11 +107,11 @@ module Alicorn
106
107
  def find_master_pid(unicorns)
107
108
  master_lines = unicorns.select { |line| line.match /master/ }
108
109
  if master_lines.size == 0
109
- raise "No unicorn master processes detected. You may still be starting up."
110
+ raise NoMasterError.new("No unicorn master processes detected. You may still be starting up.")
110
111
  elsif master_lines.size > 1
111
- raise "Too many unicorn master processes detected. You may be restarting, or have an app name collision: #{master_lines}"
112
+ raise AmbiguousMasterError.new("Too many unicorn master processes detected. You may be restarting, or have an app name collision: #{master_lines}")
112
113
  elsif master_lines.first.match /\(old\)/
113
- raise "Old master process detected. You may be restarting: #{master_lines.first}"
114
+ raise AmbiguousMasterError.new("Old master process detected. You may be restarting: #{master_lines.first}")
114
115
  else
115
116
  master_lines.first.split.first.to_i
116
117
  end
@@ -123,7 +124,7 @@ module Alicorn
123
124
  def find_unicorns
124
125
  ptable = grep_process_list.split("\n")
125
126
  unicorns = ptable.select { |line| line.match(/unicorn/) && line.match(/#{Regexp.escape(app_name)}/) }
126
- raise "Could not find any unicorn processes" if unicorns.empty?
127
+ raise NoUnicornsError.new("Could not find any unicorn processes") if unicorns.empty?
127
128
 
128
129
  unicorns.map(&:strip)
129
130
  end
@@ -1,3 +1,3 @@
1
1
  module Alicorn
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
data/test/test_scaler.rb CHANGED
@@ -26,7 +26,7 @@ class TestScaler < Test::Unit::TestCase
26
26
  end
27
27
 
28
28
  should "raise an error" do
29
- exception = assert_raise(RuntimeError) do
29
+ exception = assert_raise(Alicorn::NoUnicornsError) do
30
30
  @scaler.scale
31
31
  end
32
32
  assert_equal "Could not find any unicorn processes", exception.message
@@ -41,7 +41,7 @@ class TestScaler < Test::Unit::TestCase
41
41
  end
42
42
 
43
43
  should "raise an error" do
44
- exception = assert_raise(RuntimeError) do
44
+ exception = assert_raise(Alicorn::NoMasterError) do
45
45
  @scaler.scale
46
46
  end
47
47
  assert_match /No unicorn master processes/, exception.message
@@ -55,11 +55,9 @@ class TestScaler < Test::Unit::TestCase
55
55
  @scaler.stubs(:grep_process_list).returns(plist)
56
56
  end
57
57
 
58
- should "raise an error" do
59
- exception = assert_raise(RuntimeError) do
60
- @scaler.scale
61
- end
62
- assert_match /Too many unicorn master processes/, exception.message
58
+ should "die quietly" do
59
+ @scaler.expects(:auto_scale).never
60
+ @scaler.scale
63
61
  end
64
62
  end
65
63
 
@@ -70,11 +68,9 @@ class TestScaler < Test::Unit::TestCase
70
68
  @scaler.stubs(:grep_process_list).returns(plist)
71
69
  end
72
70
 
73
- should "raise an error" do
74
- exception = assert_raise(RuntimeError) do
75
- @scaler.scale
76
- end
77
- assert_match /Old master process detected/, exception.message
71
+ should "die quietly" do
72
+ @scaler.expects(:auto_scale).never
73
+ @scaler.scale
78
74
  end
79
75
  end
80
76
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alicorn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-23 00:00:00.000000000 Z
12
+ date: 2012-05-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: curb
16
- requirement: &70109358372400 !ruby/object:Gem::Requirement
16
+ requirement: &70222522833040 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.8.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70109358372400
24
+ version_requirements: *70222522833040
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: shoulda
27
- requirement: &70109358371920 !ruby/object:Gem::Requirement
27
+ requirement: &70222522832560 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 3.0.1
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70109358371920
35
+ version_requirements: *70222522832560
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rdoc
38
- requirement: &70109358371440 !ruby/object:Gem::Requirement
38
+ requirement: &70222522832080 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '3.12'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70109358371440
46
+ version_requirements: *70222522832080
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: bundler
49
- requirement: &70109358370960 !ruby/object:Gem::Requirement
49
+ requirement: &70222522831600 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 1.1.3
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70109358370960
57
+ version_requirements: *70222522831600
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: jeweler
60
- requirement: &70109358370480 !ruby/object:Gem::Requirement
60
+ requirement: &70222522831120 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: 1.8.3
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70109358370480
68
+ version_requirements: *70222522831120
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: mocha
71
- requirement: &70109358369960 !ruby/object:Gem::Requirement
71
+ requirement: &70222522830640 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: 0.11.4
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70109358369960
79
+ version_requirements: *70222522830640
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: simplecov
82
- requirement: &70109358369460 !ruby/object:Gem::Requirement
82
+ requirement: &70222522830160 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ~>
@@ -87,12 +87,12 @@ dependencies:
87
87
  version: 0.6.4
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70109358369460
90
+ version_requirements: *70222522830160
91
91
  description: Highly configurable dumb auto-scaler for managing unicorn web servers
92
92
  email: somers.ben@gmail.com
93
93
  executables:
94
94
  - alicorn
95
- - alicorn_profiler
95
+ - alicorn-profiler
96
96
  extensions: []
97
97
  extra_rdoc_files:
98
98
  - LICENSE.txt
@@ -106,9 +106,11 @@ files:
106
106
  - Rakefile
107
107
  - alicorn.gemspec
108
108
  - bin/alicorn
109
- - bin/alicorn_profiler
109
+ - bin/alicorn-profiler
110
+ - example/config.yml
110
111
  - lib/alicorn.rb
111
112
  - lib/alicorn/dataset.rb
113
+ - lib/alicorn/errors.rb
112
114
  - lib/alicorn/log_parser.rb
113
115
  - lib/alicorn/profiler.rb
114
116
  - lib/alicorn/scaler.rb
@@ -133,7 +135,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
135
  version: '0'
134
136
  segments:
135
137
  - 0
136
- hash: -3046697900382344279
138
+ hash: -82603868672489802
137
139
  required_rubygems_version: !ruby/object:Gem::Requirement
138
140
  none: false
139
141
  requirements: