bounscale 0.0.1
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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/bounscale.gemspec +22 -0
- data/lib/bounscale/collector/base.rb +13 -0
- data/lib/bounscale/collector/busyness.rb +61 -0
- data/lib/bounscale/collector/cpu.rb +21 -0
- data/lib/bounscale/collector/memory.rb +21 -0
- data/lib/bounscale/collector/throughput.rb +19 -0
- data/lib/bounscale/middlerware.rb +32 -0
- data/lib/bounscale/railtie.rb +7 -0
- data/lib/bounscale/version.rb +3 -0
- data/lib/bounscale/writer/base.rb +31 -0
- data/lib/bounscale/writer/heroku_writer.rb +5 -0
- data/lib/bounscale.rb +15 -0
- data/spec/busyness_spec.rb +94 -0
- data/spec/collector_base_spec.rb +9 -0
- data/spec/cpu_spec.rb +32 -0
- data/spec/heroku_writer_spec.rb +27 -0
- data/spec/memory_spec.rb +35 -0
- data/spec/middleware_spec.rb +42 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/throughput_spec.rb +100 -0
- data/spec/writer_base_spec.rb +70 -0
- metadata +126 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 DTS Corporation
|
2
|
+
|
3
|
+
MIT License
|
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
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Bounscale
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'bounscale'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install bounscale
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bounscale.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'bounscale/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "bounscale"
|
8
|
+
gem.version = Bounscale::VERSION
|
9
|
+
gem.authors = ["DTS Corporation"]
|
10
|
+
gem.email = ["info@bounscale.com"]
|
11
|
+
gem.description = %q{Rack agent of auto scaling for Heroku by Bounscale.}
|
12
|
+
gem.summary = %q{Rack agent for Bounscale}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency 'json'
|
21
|
+
gem.add_development_dependency "rspec"
|
22
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class Bounscale::Collector::Busyness < Bounscale::Collector::Base
|
2
|
+
HISTORY_HOLDING_SEC = 10
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def clear_history!
|
6
|
+
Thread.current[:bounscale_busyness_history] = []
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def pre
|
11
|
+
@pre_time = Time.now
|
12
|
+
end
|
13
|
+
|
14
|
+
def post
|
15
|
+
@post_time = Time.now
|
16
|
+
history << [@pre_time, @post_time]
|
17
|
+
fix_history
|
18
|
+
end
|
19
|
+
|
20
|
+
def name
|
21
|
+
"busyness"
|
22
|
+
end
|
23
|
+
|
24
|
+
def value
|
25
|
+
#2つ以上のアクセスがないと測定不能なので0を返す
|
26
|
+
return 0 if history.length < 2
|
27
|
+
|
28
|
+
#積算値 / 全体の秒数 がビジー率(%なので100をかける)
|
29
|
+
(estimate_sec / whole_sec) * 100
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def history
|
34
|
+
Thread.current[:bounscale_busyness_history] ||= []
|
35
|
+
Thread.current[:bounscale_busyness_history]
|
36
|
+
end
|
37
|
+
|
38
|
+
def fix_history
|
39
|
+
history.delete_if do |h|
|
40
|
+
(@post_time.to_f - h[1].to_f) > HISTORY_HOLDING_SEC
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def whole_sec
|
45
|
+
#履歴の最初から最後までの秒数を算出
|
46
|
+
oldest_pre = history[0][0].to_f
|
47
|
+
newest_post = history[-1][1].to_f
|
48
|
+
whole_sec = newest_post - oldest_pre
|
49
|
+
end
|
50
|
+
|
51
|
+
def estimate_sec
|
52
|
+
#各履歴の所要時間の積算値を算出
|
53
|
+
result = 0.0
|
54
|
+
history.each do |h|
|
55
|
+
pre_time = h[0].to_f
|
56
|
+
post_time = h[1].to_f
|
57
|
+
result += (post_time - pre_time)
|
58
|
+
end
|
59
|
+
result
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Bounscale::Collector::Cpu < Bounscale::Collector::Base
|
2
|
+
def pre
|
3
|
+
@pre_user_time = Process.times.utime
|
4
|
+
@pre_system_time = Process.times.stime
|
5
|
+
end
|
6
|
+
|
7
|
+
def post
|
8
|
+
@post_user_time = Process.times.utime
|
9
|
+
@post_system_time = Process.times.stime
|
10
|
+
@elapsed_user_time = @post_user_time - @pre_user_time
|
11
|
+
@elapsed_system_time = @post_system_time - @pre_system_time
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
"cpu"
|
16
|
+
end
|
17
|
+
|
18
|
+
def value
|
19
|
+
(@elapsed_user_time + @elapsed_system_time) * 1000
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Bounscale::Collector::Memory < Bounscale::Collector::Base
|
2
|
+
def pre
|
3
|
+
end
|
4
|
+
|
5
|
+
def post
|
6
|
+
process = $$
|
7
|
+
@post_memory = ps_value(process).split("\n")[1].to_f / 1024.0 rescue 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def name
|
11
|
+
"memory"
|
12
|
+
end
|
13
|
+
|
14
|
+
def value
|
15
|
+
@post_memory
|
16
|
+
end
|
17
|
+
|
18
|
+
def ps_value(process)
|
19
|
+
`ps -o rsz #{process}`
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Bounscale::Collector::Throughput < Bounscale::Collector::Busyness
|
2
|
+
def pre
|
3
|
+
end
|
4
|
+
|
5
|
+
def post
|
6
|
+
end
|
7
|
+
|
8
|
+
def name
|
9
|
+
"throughput"
|
10
|
+
end
|
11
|
+
|
12
|
+
def value
|
13
|
+
#2つ以上のアクセスがないと測定不能なので0を返す
|
14
|
+
return 0 if history.length < 2
|
15
|
+
|
16
|
+
#リクエスト数 / 全体の時間 がスループット(分あたり換算なので60をかける)
|
17
|
+
(history.length.to_f / whole_sec) * 60
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "bounscale/collector/base"
|
2
|
+
|
3
|
+
module Bounscale
|
4
|
+
class Middleware
|
5
|
+
COLLECTOR_CLASSES = [
|
6
|
+
Bounscale::Collector::Cpu,
|
7
|
+
Bounscale::Collector::Memory,
|
8
|
+
Bounscale::Collector::Busyness,
|
9
|
+
Bounscale::Collector::Throughput
|
10
|
+
]
|
11
|
+
def initialize(app)
|
12
|
+
@app = app
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
collector_instances = COLLECTOR_CLASSES.map do |klass|
|
17
|
+
collector = klass.new
|
18
|
+
collector.pre
|
19
|
+
collector
|
20
|
+
end
|
21
|
+
|
22
|
+
app_response = @app.call(env)
|
23
|
+
|
24
|
+
collector_instances.each do |collector|
|
25
|
+
collector.post
|
26
|
+
end
|
27
|
+
|
28
|
+
Bounscale::Writer::HerokuWriter.new.write(collector_instances)
|
29
|
+
return app_response
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Bounscale
|
4
|
+
module Writer
|
5
|
+
class Base
|
6
|
+
FORMAT_VERSION = 0
|
7
|
+
BOUNSCALE_OPEN_UUID = "b74e646e-7e55-448f-814d-e36eedc44ea9"
|
8
|
+
BOUNSCALE_CLOSE_UUID = "4a061908-db52-4224-ad4b-9850a47c7edf"
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def strip_uuid(str)
|
12
|
+
str.gsub(BOUNSCALE_OPEN_UUID, "").gsub(BOUNSCALE_CLOSE_UUID, "")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def write(collector_instances)
|
17
|
+
result = {:format_ver => FORMAT_VERSION, :datetime => Time.now.to_s, :data => []}
|
18
|
+
result[:framework_ver] = defined?(Rails) ? "Rails " + Rails::VERSION::STRING : "Not Support"
|
19
|
+
collector_instances.each do |collector|
|
20
|
+
result[:data] << {:name => collector.name, :value => collector.value}
|
21
|
+
end
|
22
|
+
str = "#{BOUNSCALE_OPEN_UUID}#{result.to_json}#{BOUNSCALE_CLOSE_UUID}"
|
23
|
+
self.output(str)
|
24
|
+
str
|
25
|
+
end
|
26
|
+
|
27
|
+
def output(str)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/bounscale.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
|
3
|
+
require "bounscale/version"
|
4
|
+
|
5
|
+
require "bounscale/collector/base"
|
6
|
+
require "bounscale/collector/cpu"
|
7
|
+
require "bounscale/collector/memory"
|
8
|
+
require "bounscale/collector/busyness"
|
9
|
+
require "bounscale/collector/throughput"
|
10
|
+
|
11
|
+
require "bounscale/writer/base"
|
12
|
+
require "bounscale/writer/heroku_writer"
|
13
|
+
|
14
|
+
require "bounscale/middlerware"
|
15
|
+
require "bounscale/railtie" if defined?(Rails::Railtie)
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ビジー率を取得できていることを確認する" do
|
4
|
+
before do
|
5
|
+
Bounscale::Collector::Busyness.clear_history!
|
6
|
+
end
|
7
|
+
|
8
|
+
it "collection名がbusynessであること" do
|
9
|
+
busy = Bounscale::Collector::Busyness.new
|
10
|
+
busy.name.should == "busyness"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "ビジー率は%であること" do
|
14
|
+
base_time = Time.parse("2013/1/1 00:00:00")
|
15
|
+
busy = Bounscale::Collector::Busyness.new
|
16
|
+
Thread.current[:bounscale_busyness_history] = [
|
17
|
+
[base_time, base_time + 3],
|
18
|
+
[base_time + 8, base_time + 9]
|
19
|
+
]
|
20
|
+
#%なので100をかける
|
21
|
+
busy.value.should == (4.0/9.0) * 100
|
22
|
+
end
|
23
|
+
|
24
|
+
it "2つ以上の履歴がない場合は0を返す事" do
|
25
|
+
base_time = Time.parse("2013/1/1 00:00:00")
|
26
|
+
busy = Bounscale::Collector::Busyness.new
|
27
|
+
|
28
|
+
Thread.current[:bounscale_busyness_history] = []
|
29
|
+
busy.value.should == 0
|
30
|
+
|
31
|
+
Thread.current[:bounscale_busyness_history] = [
|
32
|
+
[base_time, base_time + 3]
|
33
|
+
]
|
34
|
+
busy.value.should == 0
|
35
|
+
|
36
|
+
Thread.current[:bounscale_busyness_history] = [
|
37
|
+
[base_time, base_time + 3],
|
38
|
+
[base_time + 8, base_time + 9]
|
39
|
+
]
|
40
|
+
busy.value.should_not == 0
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
it "ビジー率が取得できることを確認(モック)" do
|
45
|
+
#1秒→(2秒)→3秒→(4秒)→5秒の場合
|
46
|
+
fake_now = Time.parse("2013/1/1 00:00:00")
|
47
|
+
|
48
|
+
busy = Bounscale::Collector::Busyness.new
|
49
|
+
#[]サンプルが1つもないと0を返す
|
50
|
+
busy.value.should == 0
|
51
|
+
|
52
|
+
Time.stub!(:now).and_return(fake_now)
|
53
|
+
busy.pre
|
54
|
+
Time.stub!(:now).and_return(fake_now + 1)
|
55
|
+
busy.post
|
56
|
+
|
57
|
+
#[1]サンプルが一つしかないと0を返す
|
58
|
+
busy.value.should == 0
|
59
|
+
|
60
|
+
busy = Bounscale::Collector::Busyness.new
|
61
|
+
Time.stub!(:now).and_return(fake_now + 1 + 2)
|
62
|
+
busy.pre
|
63
|
+
Time.stub!(:now).and_return(fake_now + 1 + 2 + 3)
|
64
|
+
busy.post
|
65
|
+
|
66
|
+
#[1, (2), 3]なので4/6がビジー率
|
67
|
+
busy.value.should == (4.0 / 6.0) * 100
|
68
|
+
|
69
|
+
busy = Bounscale::Collector::Busyness.new
|
70
|
+
Time.stub!(:now).and_return(fake_now + 1 + 2 + 3 + 4)
|
71
|
+
busy.pre
|
72
|
+
Time.stub!(:now).and_return(fake_now + 1 + 2 + 3 + 4 + 5)
|
73
|
+
busy.post
|
74
|
+
|
75
|
+
#[1, (2), 3, (4), 5]だが、処理終端が10秒前以前は消えるので
|
76
|
+
#[3, (4), 5]で、8/12がビジー率
|
77
|
+
busy.value.should == (8.0 / 12.0) * 100
|
78
|
+
end
|
79
|
+
|
80
|
+
it "ビジー率がモックなしで大体取得できることを確認" do
|
81
|
+
busy = nil
|
82
|
+
10.times do
|
83
|
+
busy = Bounscale::Collector::Busyness.new
|
84
|
+
busy.name.should eq "busyness"
|
85
|
+
busy.pre
|
86
|
+
sleep(0.01)
|
87
|
+
busy.post
|
88
|
+
sleep(0.04)
|
89
|
+
end
|
90
|
+
busy.value.should > 20
|
91
|
+
busy.value.should < 40
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
data/spec/cpu_spec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "CPU時間を取得できていることを確認する" do
|
4
|
+
it "CPU時間が取得できることを確認" do
|
5
|
+
cpu = Bounscale::Collector::Cpu.new
|
6
|
+
cpu.name.should eq "cpu"
|
7
|
+
cpu.pre
|
8
|
+
1000000.times{}
|
9
|
+
cpu.post
|
10
|
+
cpu.value.should > 0
|
11
|
+
end
|
12
|
+
|
13
|
+
it "collector名がcpuであること" do
|
14
|
+
cpu = Bounscale::Collector::Cpu.new
|
15
|
+
cpu.name.should == "cpu"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "user/system timeを足したCPU時間がミリ秒で返却されること" do
|
19
|
+
cpu = Bounscale::Collector::Cpu.new
|
20
|
+
|
21
|
+
times = double(:utime => 0.001, :stime => 0.002)
|
22
|
+
Process.stub!(:times).and_return(times)
|
23
|
+
|
24
|
+
cpu.pre
|
25
|
+
|
26
|
+
times = double(:utime => 0.005, :stime => 0.008)
|
27
|
+
Process.stub!(:times).and_return(times)
|
28
|
+
|
29
|
+
cpu.post
|
30
|
+
cpu.value.should == 10
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "文字列化が正常にできることの確認" do
|
4
|
+
before :each do
|
5
|
+
fake_now = Time.parse("2013/01/01 00:00:00")
|
6
|
+
Time.stub!(:now).and_return(fake_now)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "データが標準出力に書き込まれていること" do
|
10
|
+
collectors = [
|
11
|
+
double(:name => "collector1", :value => 10),
|
12
|
+
double(:name => "collector2", :value => 20)
|
13
|
+
]
|
14
|
+
|
15
|
+
str = capture(:stdout) {
|
16
|
+
writer = Bounscale::Writer::HerokuWriter.new
|
17
|
+
str = writer.write(collectors)
|
18
|
+
}
|
19
|
+
|
20
|
+
str.include?("b74e646e-7e55-448f-814d-e36eedc44ea9").should be_true
|
21
|
+
str.include?("\"datetime\":\"Tue Jan 01 00:00:00 +0000 2013\"").should be_true
|
22
|
+
str.include?("\"framework_ver\":\"Not Support\"").should be_true
|
23
|
+
str.include?("\"data\":[{\"value\":10,\"name\":\"collector1\"},{\"value\":20,\"name\":\"collector2\"}]").should be_true
|
24
|
+
str.include?("\"format_ver\":0").should be_true
|
25
|
+
str.include?("4a061908-db52-4224-ad4b-9850a47c7edf").should be_true
|
26
|
+
end
|
27
|
+
end
|
data/spec/memory_spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "メモリ使用量を取得できていることを確認する" do
|
4
|
+
it "collector名がmemoryであること" do
|
5
|
+
mem = Bounscale::Collector::Memory.new
|
6
|
+
mem.name.should == "memory"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "メモリ使用量が取得できること(モック)" do
|
10
|
+
dummy_ps = " RSZ\n2036 \n780"
|
11
|
+
mem = Bounscale::Collector::Memory.new
|
12
|
+
mem.stub!(:ps_value).and_return(dummy_ps)
|
13
|
+
mem.pre
|
14
|
+
mem.post
|
15
|
+
mem.value.should == 2036.to_f / 1024
|
16
|
+
end
|
17
|
+
|
18
|
+
it "メモリ使用量が正常に取得できない場合0が返ること" do
|
19
|
+
mem = Bounscale::Collector::Memory.new
|
20
|
+
mem.should_receive(:ps_value) do
|
21
|
+
raise
|
22
|
+
end
|
23
|
+
mem.pre
|
24
|
+
mem.post
|
25
|
+
mem.value.should == 0
|
26
|
+
end
|
27
|
+
|
28
|
+
it "モックなしでメモリ使用量が大体取得できることを確認" do
|
29
|
+
mem = Bounscale::Collector::Memory.new
|
30
|
+
mem.name.should eq "memory"
|
31
|
+
mem.pre
|
32
|
+
mem.post
|
33
|
+
mem.value.should > 0
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Bounscale::MockApp
|
4
|
+
attr_accessor :called
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
self.called = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
self.called = true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "情報を収集し、書きこめていることを確認する" do
|
16
|
+
before do
|
17
|
+
Bounscale::Collector::Busyness.clear_history!
|
18
|
+
end
|
19
|
+
|
20
|
+
it "情報を収集できていること" do
|
21
|
+
$stdout = StringIO.new
|
22
|
+
|
23
|
+
mock_app = Bounscale::MockApp.new
|
24
|
+
middleware = Bounscale::Middleware.new(mock_app)
|
25
|
+
result = middleware.call(ENV)
|
26
|
+
|
27
|
+
out = $stdout.string
|
28
|
+
$stdout = STDOUT
|
29
|
+
|
30
|
+
out = Bounscale::Writer::Base.strip_uuid(out)
|
31
|
+
result_json = JSON.parse(out)
|
32
|
+
result_json["format_ver"].should eq 0
|
33
|
+
result_json["data"][0]["name"].should eq "cpu"
|
34
|
+
result_json["data"][0]["value"].should eq 0
|
35
|
+
result_json["data"][1]["name"].should eq "memory"
|
36
|
+
result_json["data"][1]["value"].should > 0
|
37
|
+
result_json["data"][2]["name"].should eq "busyness"
|
38
|
+
result_json["data"][2]["value"].should eq 0
|
39
|
+
result_json["data"][3]["name"].should eq "throughput"
|
40
|
+
result_json["data"][3]["value"].should eq 0
|
41
|
+
end
|
42
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'lib/bounscale'
|
4
|
+
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.mock_framework = :rspec
|
9
|
+
end
|
10
|
+
|
11
|
+
def capture(stream)
|
12
|
+
begin
|
13
|
+
stream = stream.to_s
|
14
|
+
eval "$#{stream} = StringIO.new"
|
15
|
+
yield
|
16
|
+
result = eval("$#{stream}").string
|
17
|
+
ensure
|
18
|
+
eval "$#{stream} = #{stream.upcase}"
|
19
|
+
end
|
20
|
+
result
|
21
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "スループットを取得できていることを確認する" do
|
4
|
+
before :each do
|
5
|
+
Bounscale::Collector::Busyness.clear_history!
|
6
|
+
end
|
7
|
+
|
8
|
+
it "collector名がthroughputであること" do
|
9
|
+
th = Bounscale::Collector::Throughput.new
|
10
|
+
th.name.should == "throughput"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "Busynessで収集された履歴を用いてリクエスト数/全体の時間の分辺りの値で算出すること" do
|
14
|
+
base_time = Time.parse("2013/1/1 00:00:00")
|
15
|
+
throughput = Bounscale::Collector::Throughput.new
|
16
|
+
Thread.current[:bounscale_busyness_history] = [
|
17
|
+
[base_time, base_time + 3],
|
18
|
+
[base_time + 8, base_time + 9]
|
19
|
+
]
|
20
|
+
|
21
|
+
#2リクエストが9秒の間にきている負荷の分辺りの負荷がスループット
|
22
|
+
throughput.value.should == ((2 / 9.0) * 60.0)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "履歴が2つ以下の場合スループットは0を返す事" do
|
26
|
+
base_time = Time.parse("2013/1/1 00:00:00")
|
27
|
+
throughput = Bounscale::Collector::Throughput.new
|
28
|
+
Thread.current[:bounscale_busyness_history] = []
|
29
|
+
throughput.value.should == 0
|
30
|
+
|
31
|
+
Thread.current[:bounscale_busyness_history] = [
|
32
|
+
[base_time, base_time + 3]
|
33
|
+
]
|
34
|
+
throughput.value.should == 0
|
35
|
+
|
36
|
+
Thread.current[:bounscale_busyness_history] = [
|
37
|
+
[base_time, base_time + 3],
|
38
|
+
[base_time + 8, base_time + 9]
|
39
|
+
]
|
40
|
+
throughput.value.should_not == 0
|
41
|
+
end
|
42
|
+
|
43
|
+
it "スループットが取得できる事を確認(モック)" do
|
44
|
+
#1秒→(2秒)→3秒→(4秒)→5秒の場合
|
45
|
+
fake_now = Time.parse("2013/1/1 00:00:00")
|
46
|
+
|
47
|
+
busy = Bounscale::Collector::Busyness.new
|
48
|
+
th = Bounscale::Collector::Throughput.new
|
49
|
+
|
50
|
+
Time.stub!(:now).and_return(fake_now)
|
51
|
+
busy.pre
|
52
|
+
th.pre
|
53
|
+
Time.stub!(:now).and_return(fake_now + 1)
|
54
|
+
busy.post
|
55
|
+
th.post
|
56
|
+
|
57
|
+
#[1]サンプルが一つしかないと0を返す
|
58
|
+
th.value.should == 0
|
59
|
+
|
60
|
+
busy = Bounscale::Collector::Busyness.new
|
61
|
+
th = Bounscale::Collector::Throughput.new
|
62
|
+
|
63
|
+
Time.stub!(:now).and_return(fake_now + 1 + 2)
|
64
|
+
busy.pre
|
65
|
+
th.pre
|
66
|
+
Time.stub!(:now).and_return(fake_now + 1 + 2 + 3)
|
67
|
+
busy.post
|
68
|
+
th.post
|
69
|
+
|
70
|
+
#[1, (2), 3]なので2/6がスループット
|
71
|
+
th.value.should == (2 / 6.0) * 60
|
72
|
+
|
73
|
+
busy = Bounscale::Collector::Busyness.new
|
74
|
+
th = Bounscale::Collector::Throughput.new
|
75
|
+
Time.stub!(:now).and_return(fake_now + 1 + 2 + 3 + 4)
|
76
|
+
busy.pre
|
77
|
+
th.pre
|
78
|
+
Time.stub!(:now).and_return(fake_now + 1 + 2 + 3 + 4 + 5)
|
79
|
+
busy.post
|
80
|
+
th.post
|
81
|
+
|
82
|
+
#[1, (2), 3, (4), 5]だが、処理終端が10秒前以前は消えるので
|
83
|
+
#[3, (4), 5]で、3/12がスループット
|
84
|
+
th.value.should == (2 / 12.0) * 60
|
85
|
+
end
|
86
|
+
|
87
|
+
it "スループットがモックなしで代替の値で取得できることを確認" do
|
88
|
+
throughput = nil
|
89
|
+
10.times do
|
90
|
+
busyness = Bounscale::Collector::Busyness.new
|
91
|
+
throughput = Bounscale::Collector::Throughput.new
|
92
|
+
throughput.name.should eq "throughput"
|
93
|
+
busyness.pre
|
94
|
+
busyness.post
|
95
|
+
sleep(0.1)
|
96
|
+
end
|
97
|
+
throughput.value.should > 500
|
98
|
+
throughput.value.should < 700
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "文字列化が正常にできることの確認" do
|
4
|
+
before :each do
|
5
|
+
fake_now = Time.parse("2013/01/01 00:00:00")
|
6
|
+
Time.stub!(:now).and_return(fake_now)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "ひきわたされたcollectorの情報が全て書き込み文字列に含まれていること" do
|
10
|
+
collectors = [
|
11
|
+
double(:name => "collector1", :value => 10),
|
12
|
+
double(:name => "collector2", :value => 20)
|
13
|
+
]
|
14
|
+
writer = Bounscale::Writer::Base.new
|
15
|
+
str = writer.write(collectors)
|
16
|
+
|
17
|
+
str.include?("b74e646e-7e55-448f-814d-e36eedc44ea9").should be_true
|
18
|
+
str.include?("\"datetime\":\"Tue Jan 01 00:00:00 +0000 2013\"").should be_true
|
19
|
+
str.include?("\"framework_ver\":\"Not Support\"").should be_true
|
20
|
+
str.include?("\"data\":[{\"value\":10,\"name\":\"collector1\"},{\"value\":20,\"name\":\"collector2\"}]").should be_true
|
21
|
+
str.include?("\"format_ver\":0").should be_true
|
22
|
+
str.include?("4a061908-db52-4224-ad4b-9850a47c7edf").should be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "フレームワークバージョンが書きこまれていること" do
|
26
|
+
class Rails
|
27
|
+
class VERSION
|
28
|
+
STRING = "9.9.9"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
writer = Bounscale::Writer::Base.new
|
33
|
+
str = writer.write([])
|
34
|
+
str.include?("\"framework_ver\":\"Not Support\"").should be_false
|
35
|
+
str.include?("\"framework_ver\":\"Rails 9.9.9\"").should be_true
|
36
|
+
end
|
37
|
+
|
38
|
+
it "想定しないフレームワークではフレームワークバージョンが書きこまれないこと" do
|
39
|
+
Object.class_eval do
|
40
|
+
remove_const :Rails
|
41
|
+
end
|
42
|
+
|
43
|
+
writer = Bounscale::Writer::Base.new
|
44
|
+
str = writer.write([])
|
45
|
+
|
46
|
+
str.include?("\"framework_ver\":\"Not Support\"").should be_true
|
47
|
+
end
|
48
|
+
|
49
|
+
it "フォーマットバージョンが書きこまれていること" do
|
50
|
+
writer = Bounscale::Writer::Base.new
|
51
|
+
str = writer.write([])
|
52
|
+
|
53
|
+
str.include?("\"format_ver\":0").should be_true
|
54
|
+
end
|
55
|
+
|
56
|
+
it "識別用UUIDに囲まれた領域をくくりだせること" do
|
57
|
+
before_str = "b74e646e-7e55-448f-814d-e36eedc44ea9DUMMYSTRING4a061908-db52-4224-ad4b-9850a47c7edf"
|
58
|
+
after_str = Bounscale::Writer::Base.strip_uuid(before_str)
|
59
|
+
after_str.should == "DUMMYSTRING"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "取得したデータがJSON形式でパースできること" do
|
63
|
+
writer = Bounscale::Writer::HerokuWriter.new
|
64
|
+
str = writer.write([])
|
65
|
+
str = Bounscale::Writer::Base.strip_uuid(str)
|
66
|
+
result = JSON.parse(str)
|
67
|
+
result["format_ver"].should eq 0
|
68
|
+
(Time.now.to_i - Time.parse(result["datetime"]).to_i).should < 10
|
69
|
+
end
|
70
|
+
end
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bounscale
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- DTS Corporation
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2013-06-21 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: json
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rspec
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
description: Rack agent of auto scaling for Heroku by Bounscale.
|
49
|
+
email:
|
50
|
+
- info@bounscale.com
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- Gemfile
|
60
|
+
- LICENSE.txt
|
61
|
+
- README.md
|
62
|
+
- Rakefile
|
63
|
+
- bounscale.gemspec
|
64
|
+
- lib/bounscale.rb
|
65
|
+
- lib/bounscale/collector/base.rb
|
66
|
+
- lib/bounscale/collector/busyness.rb
|
67
|
+
- lib/bounscale/collector/cpu.rb
|
68
|
+
- lib/bounscale/collector/memory.rb
|
69
|
+
- lib/bounscale/collector/throughput.rb
|
70
|
+
- lib/bounscale/middlerware.rb
|
71
|
+
- lib/bounscale/railtie.rb
|
72
|
+
- lib/bounscale/version.rb
|
73
|
+
- lib/bounscale/writer/base.rb
|
74
|
+
- lib/bounscale/writer/heroku_writer.rb
|
75
|
+
- spec/busyness_spec.rb
|
76
|
+
- spec/collector_base_spec.rb
|
77
|
+
- spec/cpu_spec.rb
|
78
|
+
- spec/heroku_writer_spec.rb
|
79
|
+
- spec/memory_spec.rb
|
80
|
+
- spec/middleware_spec.rb
|
81
|
+
- spec/spec_helper.rb
|
82
|
+
- spec/throughput_spec.rb
|
83
|
+
- spec/writer_base_spec.rb
|
84
|
+
homepage: ""
|
85
|
+
licenses: []
|
86
|
+
|
87
|
+
post_install_message:
|
88
|
+
rdoc_options: []
|
89
|
+
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
hash: 3
|
98
|
+
segments:
|
99
|
+
- 0
|
100
|
+
version: "0"
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
hash: 3
|
107
|
+
segments:
|
108
|
+
- 0
|
109
|
+
version: "0"
|
110
|
+
requirements: []
|
111
|
+
|
112
|
+
rubyforge_project:
|
113
|
+
rubygems_version: 1.8.25
|
114
|
+
signing_key:
|
115
|
+
specification_version: 3
|
116
|
+
summary: Rack agent for Bounscale
|
117
|
+
test_files:
|
118
|
+
- spec/busyness_spec.rb
|
119
|
+
- spec/collector_base_spec.rb
|
120
|
+
- spec/cpu_spec.rb
|
121
|
+
- spec/heroku_writer_spec.rb
|
122
|
+
- spec/memory_spec.rb
|
123
|
+
- spec/middleware_spec.rb
|
124
|
+
- spec/spec_helper.rb
|
125
|
+
- spec/throughput_spec.rb
|
126
|
+
- spec/writer_base_spec.rb
|