autograph 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.rdoc +2 -44
- data/Rakefile +1 -5
- data/TODO +12 -16
- data/VERSION +1 -1
- data/bin/autograph +2 -11
- data/lib/autograph.rb +3 -7
- data/lib/autograph/autoperf.rb +32 -27
- data/lib/autograph/configuration.rb +3 -9
- data/lib/autograph/graph.rb +9 -0
- data/lib/autograph/graph_series.rb +2 -4
- data/lib/autograph/html_report.rb +0 -3
- data/lib/autograph/report.html.erb +251 -166
- data/lib/autograph/table.rb +66 -0
- data/test/test_configuration.rb +0 -5
- data/test/test_graph.rb +17 -0
- data/test/test_graph_series.rb +6 -10
- data/test/test_table.rb +131 -0
- metadata +11 -65
- data/lib/autograph/graph_renderers/base_renderer.rb +0 -82
- data/lib/autograph/graph_renderers/flot_renderer.rb +0 -50
- data/lib/autograph/graph_renderers/gchart_renderer.rb +0 -82
- data/lib/autograph/graph_renderers/rasterized_scruffy_renderer.rb +0 -20
- data/lib/autograph/graph_renderers/scruffy_renderer.rb +0 -35
data/.gitignore
CHANGED
data/README.rdoc
CHANGED
@@ -4,11 +4,10 @@
|
|
4
4
|
|
5
5
|
Autograph drives httperf, varying the request rate and graphing the output. This exercise provides graphical data showing how the requested resources hold up under an increasing rate, particularly with request to response time and achieved request rate.
|
6
6
|
|
7
|
-
Can work with SOAP.
|
8
|
-
|
9
7
|
== NOTES:
|
10
8
|
|
11
|
-
|
9
|
+
Start off with broad ranges and zero in on rate/call/connection limits
|
10
|
+
Run from a server, not your laptop, you don't want to be limited by your internet connection.
|
12
11
|
|
13
12
|
== PROBLEMS:
|
14
13
|
|
@@ -21,46 +20,6 @@ A note on stats, from Zed Shaw: http://zedshaw.com/essays/programmer_stats.html
|
|
21
20
|
|
22
21
|
autograph --host example.com --low-rate 10 --high-rate 50 --rate-step 10 --output-file my_load_test.html
|
23
22
|
|
24
|
-
== INSTALL:
|
25
|
-
|
26
|
-
* Get httperf installed ('brew install httperf' if you've got homebrew installed)
|
27
|
-
* Download project and run 'rake build install'
|
28
|
-
|
29
|
-
nick-stielaus-computer-3:autograph nick$ httperf -h
|
30
|
-
Usage: httperf [-hdvV] [--add-header S] [--burst-length N] [--client N/N]
|
31
|
-
[--close-with-reset] [--debug N] [--failure-status N]
|
32
|
-
[--help] [--hog] [--http-version S] [--max-connections N]
|
33
|
-
[--max-piped-calls N] [--method S] [--no-host-hdr]
|
34
|
-
[--num-calls N] [--num-conns N] [--period [d|u|e]T1[,T2]]
|
35
|
-
[--port N] [--print-reply [header|body]] [--print-request [header|body]]
|
36
|
-
[--rate X] [--recv-buffer N] [--retry-on-failure] [--send-buffer N]
|
37
|
-
[--server S] [--server-name S] [--session-cookies]
|
38
|
-
[--ssl] [--ssl-ciphers L] [--ssl-no-reuse]
|
39
|
-
[--think-timeout X] [--timeout X] [--uri S] [--verbose] [--version]
|
40
|
-
[--wlog y|n,file] [--wsess N,N,X] [--wsesslog N,X,file]
|
41
|
-
[--wset N,X]
|
42
|
-
nick-stielaus-computer-3:autograph nick$ autograph
|
43
|
-
Usage: autograph [options]
|
44
|
-
--host HOST The host to load test
|
45
|
-
--port PORT The port to load test
|
46
|
-
--uris PATH,PATH A comma separated list of pages to cycle through
|
47
|
-
--output-file PATH Specify the file to output to.
|
48
|
-
--output-dir PATH Specify a directory to write output files to.
|
49
|
-
--notes NOTES Notes to be written to the report.
|
50
|
-
--test Do not run benchmarks. Use test data to generate reports.
|
51
|
-
Httperf Knobs:
|
52
|
-
--timeout SECONDS The length in seconds before a request is marked as errored
|
53
|
-
--num-call NUMCALLS The number of calls to make for each connection in the test (defaults to one).
|
54
|
-
--num-conns NUMCONNS The number of connections to make for each test
|
55
|
-
--low-rate LOWRATE The starting rate
|
56
|
-
--high-rate HIGHRATE The highest rate at which to perform a test
|
57
|
-
--rate-step RATESTEP The ammount at which to increment the rate for each interation of the test
|
58
|
-
--wsesslog PATH Path to the wsesslog file.
|
59
|
-
Common options:
|
60
|
-
-h, --help Displays this help info
|
61
|
-
-v, --verbose Verbose output
|
62
|
-
|
63
|
-
|
64
23
|
== THANKS:
|
65
24
|
|
66
25
|
Thanks to
|
@@ -80,7 +39,6 @@ http://www.hpl.hp.com/research/linux/httperf/
|
|
80
39
|
|
81
40
|
== LICENSE:
|
82
41
|
|
83
|
-
|
84
42
|
Autograph
|
85
43
|
Copyright (c) 2009 Nick Stielau
|
86
44
|
|
data/Rakefile
CHANGED
@@ -10,10 +10,6 @@ begin
|
|
10
10
|
gem.email = "nick.stielau@gmail.com"
|
11
11
|
gem.homepage = "http://github.com/nstielau/autograph"
|
12
12
|
gem.authors = ["Nick Stielau"]
|
13
|
-
gem.add_runtime_dependency 'builder', '= 2.1.2'
|
14
|
-
gem.add_runtime_dependency 'ruport', '= 1.6.3'
|
15
|
-
gem.add_runtime_dependency 'scruffy', '= 0.2.5'
|
16
|
-
gem.add_runtime_dependency 'gchart', '= 1.0.0'
|
17
13
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
14
|
end
|
19
15
|
Jeweler::GemcutterTasks.new
|
@@ -30,7 +26,7 @@ end
|
|
30
26
|
|
31
27
|
|
32
28
|
task :install_local do
|
33
|
-
`sudo gem install --local ./pkg
|
29
|
+
`sudo gem install --local ./pkg/$(ls ./pkg | head -1) --no-ri --no-rdoc`
|
34
30
|
end
|
35
31
|
|
36
32
|
task :test => :check_dependencies
|
data/TODO
CHANGED
@@ -1,32 +1,28 @@
|
|
1
|
-
#
|
2
|
-
# Skin report page
|
3
|
-
# Compare two hosts
|
4
|
-
# Config file?
|
1
|
+
# Compare multiple hosts hosts
|
5
2
|
|
6
3
|
# Deal with AcceptEncoding, add options
|
7
4
|
|
8
|
-
# Factor out outputs types into a report_renderers dir
|
9
5
|
# Control verbose output with a Logger, instead of just going to STDOUT
|
10
6
|
|
11
7
|
# Don't include overview for one page...
|
12
|
-
# Flag maxes
|
13
|
-
# Set report overview y-axis to the max request rate
|
14
8
|
|
15
9
|
# Pass-through httperf args
|
16
|
-
# Pick output format
|
17
|
-
# Pick graph types
|
18
|
-
# take a txt file for the urls
|
19
10
|
|
20
|
-
#
|
11
|
+
# Config file?
|
12
|
+
# Take a txt file for the urls
|
13
|
+
|
14
|
+
# Remove default timeout httperf option...(what happens if it is removed?)
|
21
15
|
# Add interactive mode
|
22
|
-
# Add badass testing server
|
23
16
|
|
24
|
-
#
|
17
|
+
# Trap exit and write output to file? (Can you have two traps?)
|
25
18
|
|
26
|
-
#
|
19
|
+
# Skinning
|
20
|
+
Improve table styles
|
27
21
|
|
28
|
-
#
|
22
|
+
# Put real connection/timeout etc. values in discussion.
|
29
23
|
|
30
24
|
|
31
|
-
#
|
25
|
+
# Docs
|
26
|
+
Add EC2 instructions
|
32
27
|
|
28
|
+
# Add gh-pages with graphs
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/bin/autograph
CHANGED
@@ -50,11 +50,6 @@ opt_parse_opts = OptionParser.new do |opts|
|
|
50
50
|
command_parts << "--test"
|
51
51
|
end
|
52
52
|
|
53
|
-
opts.on("--graph-renderer RENDERER", "The class to use as a graph renderer (#{BaseRenderer::AVAILABLE_GRAPH_RENDERERS.join(', ')}).") do |opt|
|
54
|
-
options['graph_renderer'] = opt
|
55
|
-
command_parts << "--graph-renderer '#{opt}'"
|
56
|
-
end
|
57
|
-
|
58
53
|
opts.separator ""
|
59
54
|
opts.separator "Httperf Knobs:"
|
60
55
|
|
@@ -93,11 +88,6 @@ opt_parse_opts = OptionParser.new do |opts|
|
|
93
88
|
command_parts << "--rate-step #{opt}"
|
94
89
|
end
|
95
90
|
|
96
|
-
# opts.on("--wlog PATH", String, "A file a ASCII nul terminated pages to cycle through") do |opt|
|
97
|
-
# options['httperf_wlog'] = "y,#{opt}";
|
98
|
-
# command_parts << "--wlog #{opt}"
|
99
|
-
# end
|
100
|
-
|
101
91
|
opts.on("--wsesslog PATH", "Path to the wsesslog file.") do |opt|
|
102
92
|
options['httperf_wsesslog'] = opt
|
103
93
|
command_parts << "--wsesslog '#{opt}'"
|
@@ -150,5 +140,6 @@ end
|
|
150
140
|
begin
|
151
141
|
AutoPerf.new(options)
|
152
142
|
rescue => e
|
153
|
-
abort(e.to_s)
|
143
|
+
abort(e.to_s) unless options["verbose"]
|
144
|
+
raise e
|
154
145
|
end
|
data/lib/autograph.rb
CHANGED
@@ -2,15 +2,11 @@ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) ||
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'optparse'
|
5
|
-
require 'ruport'
|
6
5
|
require 'pp'
|
7
6
|
|
8
7
|
require 'autograph/autoperf'
|
8
|
+
require 'autograph/configuration'
|
9
|
+
require 'autograph/graph'
|
9
10
|
require 'autograph/graph_series'
|
10
11
|
require 'autograph/html_report'
|
11
|
-
require 'autograph/
|
12
|
-
|
13
|
-
require 'autograph/graph_renderers/base_renderer'
|
14
|
-
require 'autograph/graph_renderers/gchart_renderer'
|
15
|
-
require 'autograph/graph_renderers/scruffy_renderer'
|
16
|
-
require 'autograph/graph_renderers/flot_renderer'
|
12
|
+
require 'autograph/table'
|
data/lib/autograph/autoperf.rb
CHANGED
@@ -4,17 +4,32 @@ class AutoPerf
|
|
4
4
|
def initialize(opts = {})
|
5
5
|
conf = Configuration.new(opts)
|
6
6
|
|
7
|
-
puts configuration.pretty_print if opts['verbose']
|
8
|
-
|
9
7
|
if conf['use_test_data']
|
10
|
-
|
11
|
-
@reports = load_test_data(conf)
|
8
|
+
reports = load_test_data(conf)
|
12
9
|
else
|
13
|
-
|
10
|
+
reports = run_tests(conf)
|
14
11
|
end
|
15
12
|
|
16
|
-
graphs =
|
17
|
-
HtmlReport.new(
|
13
|
+
graphs = generate_graphs(reports, conf)
|
14
|
+
HtmlReport.new(reports, graphs, conf)
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate_graphs(reports, configuration)
|
18
|
+
graphs = {}
|
19
|
+
graphs[:request_rate] = Graph.new(:title => "Demanded vs. Achieved Request Rate (r/s)")
|
20
|
+
graphs[:response_time] = Graph.new(:title => "Demanded Request Rate (r/s) vs. Response Time")
|
21
|
+
graphs[:max_request_rate] = Graph.new(:title => "Maximum Achieved Request Rate")
|
22
|
+
|
23
|
+
reports.each do |uri, report|
|
24
|
+
graphs[:request_rate].series << GraphSeries.new(report.column('rate'), report.column('conn/s').map{|x| x.to_f}, "Request rate for '#{uri}'", uri)
|
25
|
+
graphs[:response_time].series << GraphSeries.new(report.column('rate'), report.column('reply time'), "Response time for '#{uri}'", uri)
|
26
|
+
end
|
27
|
+
|
28
|
+
reports.keys.each_with_index do |key, index|
|
29
|
+
graphs[:max_request_rate].series << GraphSeries.new([index], [reports[key].max('conn/s')], "Max Request Rate for '#{key}'")
|
30
|
+
end
|
31
|
+
|
32
|
+
graphs
|
18
33
|
end
|
19
34
|
|
20
35
|
def benchmark(conf)
|
@@ -51,7 +66,7 @@ class AutoPerf
|
|
51
66
|
def vary_rate(uri, configuration)
|
52
67
|
puts "Config is #{configuration.inspect}" if configuration['verbose']
|
53
68
|
results = {}
|
54
|
-
report = Table(
|
69
|
+
report = Table.new(COLUMN_NAMES)
|
55
70
|
|
56
71
|
(configuration['low_rate']..configuration['high_rate']).step(configuration['rate_step']) do |rate|
|
57
72
|
results[rate] = benchmark(configuration.merge({'httperf_rate' => rate, 'httperf_uri' => uri}))
|
@@ -69,35 +84,25 @@ class AutoPerf
|
|
69
84
|
configuration['uris'].uniq.each do |uri|
|
70
85
|
reports[uri] = vary_rate(uri, configuration)
|
71
86
|
end
|
72
|
-
|
73
|
-
# TODO: Factor out to create_httperf_wlog
|
74
|
-
if !configuration['httperf_wlog'] && configuration['uris'].length > 1 && configuration['average']
|
75
|
-
replay_log = File.open('tmp_replay_log', 'w')
|
76
|
-
path = replay_log.path
|
77
|
-
puts "Tmp replay log is at #{path}" if configuration['verbose']
|
78
|
-
index = 1
|
79
|
-
configuration['uris'].each do |uri|
|
80
|
-
replay_log.print uri
|
81
|
-
replay_log.putc 0 if index < configuration['uris'].length # ASCII NUL Terminate join paths
|
82
|
-
index = index + 1
|
83
|
-
end
|
84
|
-
replay_log.close
|
85
|
-
puts "Replay log is at #{path}" if configuration['verbose']
|
86
|
-
reports["Avg"] = vary_rate('httperf_wlog' => "y,#{path}")
|
87
|
-
end
|
88
87
|
reports
|
89
88
|
end
|
90
89
|
|
91
90
|
def load_test_data(configuration)
|
92
91
|
reports = {}
|
92
|
+
configuration['host'] = "127.0.0.1"
|
93
|
+
configuration['uris'] = ['/', '/page1', '/page2']
|
93
94
|
configuration['uris'].each do |uri|
|
94
|
-
reports[uri] =
|
95
|
+
reports[uri] = Table.new(COLUMN_NAMES)
|
95
96
|
times = [130.7, 132.7, 180.4, 438.3, 591.9, 686.9, 739.4, 661.3, 727.1, 546.5, 711.1, 893.7, 870.0]
|
96
97
|
conns = [5.0, 21.5, 28.8, 30.6, 26.3, 24.7, 23.0, 25.8, 28.0, 27.4, 27.9, 22.2, 22.7]
|
97
98
|
1.upto(10) do |i|
|
98
99
|
reports[uri] << {'rate' => i*10 - 10,
|
99
|
-
'
|
100
|
-
'
|
100
|
+
'errors' => 0,
|
101
|
+
'conn/s' => conns[((i + rand * 10) % conns.length).to_i],
|
102
|
+
'req/s' => 0,
|
103
|
+
'replies/s avg' => 0,
|
104
|
+
'net io (KB/s)' => 100,
|
105
|
+
'reply time' => times[((i + rand * 10) % times.length).to_i]}
|
101
106
|
end
|
102
107
|
end
|
103
108
|
reports
|
@@ -1,6 +1,6 @@
|
|
1
1
|
class Configuration
|
2
2
|
def initialize(opts={})
|
3
|
-
@conf = {'httperf_timeout' =>
|
3
|
+
@conf = {'httperf_timeout' => 120,
|
4
4
|
'httperf_num-call' => 1,
|
5
5
|
'httperf_num-conns' => 100,
|
6
6
|
'httperf_rate' => 5,
|
@@ -29,6 +29,8 @@ class Configuration
|
|
29
29
|
# TODO: Add AcceptEncoding: gzip,deflate option
|
30
30
|
end
|
31
31
|
|
32
|
+
puts pretty_print if opts['verbose']
|
33
|
+
|
32
34
|
@conf
|
33
35
|
end
|
34
36
|
|
@@ -56,12 +58,4 @@ class Configuration
|
|
56
58
|
io.puts
|
57
59
|
io.read
|
58
60
|
end
|
59
|
-
|
60
|
-
def graph_renderer_class
|
61
|
-
begin
|
62
|
-
Object.const_get(@conf['graph_renderer'].to_s)
|
63
|
-
rescue => e
|
64
|
-
abort("#{@conf['graph_renderer'].to_s} is not one of the available graph renderers (#{BaseRenderer::AVAILABLE_GRAPH_RENDERERS.join(', ')})")
|
65
|
-
end
|
66
|
-
end
|
67
61
|
end
|
@@ -2,11 +2,9 @@ class GraphSeries
|
|
2
2
|
attr :x_values, true
|
3
3
|
attr :y_values, true
|
4
4
|
attr :label, true
|
5
|
-
attr :type, true
|
6
5
|
attr :path, true
|
7
|
-
|
8
|
-
def initialize(
|
9
|
-
@type = t
|
6
|
+
|
7
|
+
def initialize(xs, ys, l, p=nil)
|
10
8
|
@x_values = xs.map{|x| x.to_f}
|
11
9
|
@y_values = ys.map{|x| x.to_f}
|
12
10
|
@label = l
|
@@ -4,12 +4,9 @@ class HtmlReport
|
|
4
4
|
def initialize(reports, graphs, configuration)
|
5
5
|
date = Time.now
|
6
6
|
host = configuration['host']
|
7
|
-
title = "Report for #{host}"
|
8
7
|
uris = configuration['uris']
|
9
8
|
command_run = configuration["command_run"]
|
10
9
|
notes = configuration["notes"]
|
11
|
-
summary_graph = graphs['summary_graph']
|
12
|
-
graph_header_html = configuration.graph_renderer_class.header_html
|
13
10
|
|
14
11
|
output_file = HtmlReport.determine_output_file(configuration['output_file'], configuration['output_dir'])
|
15
12
|
|
@@ -1,175 +1,260 @@
|
|
1
|
-
<!DOCTYPE
|
2
|
-
|
3
|
-
<html lang="en">
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
2
|
+
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
3
|
+
<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
4
4
|
<head>
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
5
|
+
<title>Autograph report for <%= host %></title>
|
6
|
+
<!-- Favicon -->
|
7
|
+
<link rel="shortcut icon" href="http://nstielau.github.com/autograph/stylesheets/images/favicon.ico" />
|
8
|
+
<!--Stylesheets-->
|
9
|
+
<link href="http://nstielau.github.com/autograph/stylesheets/master.css" type="text/css" media="screen" rel="stylesheet" />
|
10
|
+
<!--[if lt IE 8]><link rel="stylesheet" type="text/css" media="screen" href="http://nstielau.github.com/autograph/stylesheets/ie.css" /><![endif]-->
|
11
|
+
<!--[if lte IE 6]><link rel="stylesheet" type="text/css" media="screen" href="http://nstielau.github.com/autograph/stylesheets/ie6.css" /><![endif]-->
|
12
|
+
<script type="text/javascript" src="http://nstielau.github.com/autograph/js/jquery.js"></script>
|
13
|
+
<script type="text/javascript" src="http://nstielau.github.com/autograph/js/jquery.flot.js"></script>
|
14
|
+
</head>
|
15
|
+
<body>
|
16
|
+
<div id="wrapper">
|
17
|
+
<div id="header" class="clear">
|
18
|
+
<h1 id="title">Autograph report for <%= host %></h1>
|
19
|
+
<div id="description">
|
20
|
+
<h2>Created at <%= date %></h2>
|
21
|
+
</div><!--end description-->
|
22
|
+
<div id="nav">
|
23
|
+
<ul>
|
24
|
+
</ul>
|
25
|
+
</div><!--end nav-->
|
26
|
+
</div><!--end header-->
|
27
|
+
<div id="content" class="pad">
|
28
|
+
<div id="post">
|
29
|
+
<div id="main">
|
30
|
+
<div id="graphs" class="section section_graphs">
|
31
|
+
<% graphs.each_pair do |k,g| %>
|
32
|
+
<h2><%= g.title %></h2>
|
33
|
+
<div class="flot-graph" id="<%= k %>" style="width:600px;height:300px;padding-bottom: 25px;"></div>
|
34
|
+
<% end %>
|
35
|
+
</div>
|
36
|
+
|
37
|
+
<div id="graph_discussion" class="section section_discussion" style="display:none;">
|
38
|
+
<h2>Methods</h2>
|
39
|
+
<p>
|
40
|
+
Use <a href="http://www.hpl.hp.com/research/linux/httperf/">httperf</a> to load different pages a fixed number of times, at a fixed rate, and record the response time and rate at which connections are accepted. By varying the rate, we can see how the response time and connection rate are effected by increased load on the server. Unlike log analysis, which relies on extrapolation to guess at maximum requests/second, Autograph actually stresses the system and yields real-world results.
|
41
|
+
</p>
|
42
|
+
<h2>Goals</h2>
|
43
|
+
<p>
|
44
|
+
Autograph will help you
|
45
|
+
<ul style="list-style: disc; font-size: 1.3em; padding-left: 25px; padding-bottom: 25px;">
|
46
|
+
<li>Compare the relative 'weight' of different pages, hopefully pointing you to which pages need more some caching or other form of optimization.</li>
|
47
|
+
<li>Determine rough maximum request rates that either a) block other calls, or b) increase response time beyond an acceptable limit.</li>
|
48
|
+
</ul>
|
49
|
+
</p>
|
50
|
+
<h2>Graphs</h2>
|
51
|
+
<h4>Demanded vs. Achieved request rate</h4>
|
52
|
+
<p>
|
53
|
+
Ideally, this graph would be a straight line at 45 degrees; for every request that is sent to the server, it accepts a connection, regardless of how many concurrent requests are made. Realistically, this graph will start linear and degrade as network or software bottlenecks are hit. The point at which this graph breaks linearity indicates the maximum possible concurrent requests the system can successfully serve.
|
54
|
+
</p>
|
55
|
+
<h4>Demanded Request Rate vs. response time</h4>
|
56
|
+
<p>
|
57
|
+
Ideally, this graph would be flat and low, indicating consistent and fast responses times. Realistically, this graph will either be slightly angled or flat followed by a dramatic uptick. The point at which the response times dramatically increase will coincide with the point at which it cannot serve all requests.
|
58
|
+
</p>
|
59
|
+
</div>
|
60
|
+
<div id="tables" class="section section_tables" style="display:none;">
|
61
|
+
<% reports.each do |uri, report| %>
|
62
|
+
<div id="page_<%= uri %>" class='report' >
|
63
|
+
<h2>Results for <%= "http://#{host}#{uri}" %></h2>
|
64
|
+
<div class="table_data" style="padding-bottom: 25px;">
|
65
|
+
<%= report.to_html %>
|
66
|
+
</div>
|
67
|
+
</div>
|
68
|
+
<% end %>
|
69
|
+
</div>
|
70
|
+
</div>
|
71
|
+
</div><!--end post-->
|
72
|
+
</div><!--end content-->
|
73
|
+
<div id="sidebar">
|
74
|
+
<ul>
|
75
|
+
<li class="widget widget_recent_entries">
|
76
|
+
<h2 class="widgettitle">Report Sections</h2>
|
77
|
+
<ul>
|
78
|
+
<li><a href="#" onclick="show_section('graphs');">Graphs</a></li>
|
79
|
+
<li><a href="#" onclick="show_section('tables');">Tables</a></li>
|
80
|
+
<li><a href="#" onclick="show_section('discussion');">Discussion</a></li>
|
81
|
+
</ul>
|
82
|
+
</li>
|
83
|
+
<li class="section section_graphs">
|
84
|
+
<h2 class="widgettitle">Tested Pages</h2>
|
85
|
+
<div id="choices"></div>
|
86
|
+
</li>
|
87
|
+
<% if notes %>
|
88
|
+
<li>
|
89
|
+
<h2>Report Notes</h2>
|
90
|
+
<p><%= notes %></p>
|
91
|
+
</li>
|
92
|
+
<% end %>
|
93
|
+
</ul>
|
94
|
+
</div><!--end sidebar-->
|
95
|
+
<div id="footer">
|
96
|
+
<p class="right">Generated fearlessly with <a href="http://nstielau.github.com/autograph">Autograph</a></p>
|
97
|
+
<p>Theme modified from "Vigilance Theme" by <a href="http://thethemefoundry.com">The Theme Foundry</a></p>
|
98
|
+
</div><!--end footer-->
|
99
|
+
</div><!--end wrapper-->
|
100
|
+
<script language="javascript" type="text/javascript">
|
101
|
+
function show_section(name) {
|
102
|
+
$('.section').hide();
|
103
|
+
$('.section_' + name).show();
|
67
104
|
}
|
68
105
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
106
|
+
var acolor;
|
107
|
+
var default_graph_options = {
|
108
|
+
series: {
|
109
|
+
lines: { show: true, lineWidth: 3 },
|
110
|
+
points: { show: true, radius: 4 }
|
111
|
+
},
|
112
|
+
legend: {
|
113
|
+
show: true,
|
114
|
+
backgroundColor: '#FFF',
|
115
|
+
backgroundOpacity: 0.9
|
116
|
+
},
|
117
|
+
series: {
|
118
|
+
lines: { show: true, lineWidth: 3 },
|
119
|
+
points: { show: true, fill: false },
|
120
|
+
shadowSize: 0,
|
121
|
+
},
|
122
|
+
xaxis: {},
|
123
|
+
yaxis: {
|
124
|
+
show: true,
|
125
|
+
},
|
126
|
+
grid: {
|
127
|
+
show: true,
|
128
|
+
backgroundColor: null,
|
129
|
+
borderWidth: 2,
|
130
|
+
hoverable: true,
|
131
|
+
tickColor: "#E1E8F0",
|
132
|
+
},
|
133
|
+
colors: ["#5bba47","#d86b6d","#3d8aea","#333333"]
|
134
|
+
};
|
135
|
+
|
136
|
+
// Callback function to show the tooltip
|
137
|
+
function showTooltip(item) {
|
138
|
+
var contents = "(" + item.datapoint[1] + "," + item.datapoint[0] + ")";
|
139
|
+
var x = item.pageX;
|
140
|
+
var y = item.pageY - 10;
|
141
|
+
|
142
|
+
var obj = $('<div id="flot-tooltip">' + contents + '</div>').css( {
|
143
|
+
padding: '5px',
|
144
|
+
position: 'absolute',
|
145
|
+
minWidth: '5em',
|
146
|
+
display: 'block',
|
147
|
+
top: y+5,
|
148
|
+
left: x+5,
|
149
|
+
zIndex: 9999
|
150
|
+
});
|
151
|
+
|
152
|
+
obj.appendTo('body').fadeIn('200');
|
78
153
|
}
|
79
|
-
}
|
80
154
|
|
81
|
-
|
82
|
-
|
83
|
-
document.getElementById(uri).style.display = '';
|
84
|
-
}
|
155
|
+
// Var to hold our previous point
|
156
|
+
var previousPoint = null;
|
85
157
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
158
|
+
// Bind to the plothover so we can show a tooltip
|
159
|
+
$(".flot-graph").bind("plothover", function (event, pos, item) {
|
160
|
+
if (item) {
|
161
|
+
if (previousPoint != item.datapoint) {
|
162
|
+
previousPoint = item.datapoint;
|
163
|
+
$("#flot-tooltip").remove();
|
164
|
+
showTooltip(item);
|
165
|
+
}
|
166
|
+
} else {
|
167
|
+
$('#flot-tooltip').remove().fadeOut('200');
|
168
|
+
previousPoint = null;
|
169
|
+
}
|
170
|
+
});
|
91
171
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
</div>
|
109
|
-
<div id="nav">
|
110
|
-
|
111
|
-
</div>
|
112
|
-
<div id="sidebar">
|
113
|
-
<div style="display:none">
|
114
|
-
<h3>Tested Pages</h3>
|
115
|
-
<ul>
|
116
|
-
<li><a href="#" onclick="show_report('overview');" title="Overview">Overview</a></li>
|
117
|
-
<% uris.each do |uri|%>
|
118
|
-
<li><a href="#" onclick="show_report('page_<%= uri %>');" title="<%= uri %>"><%= uri %></a></li>
|
119
|
-
<% end %>
|
120
|
-
</ul>
|
121
|
-
</div>
|
122
|
-
<% if notes %>
|
123
|
-
<h3>Report Notes</h3>
|
124
|
-
<p><%= notes %></p>
|
125
|
-
<% end %>
|
126
|
-
<h3>Info</h3>
|
127
|
-
<ul>
|
128
|
-
<li><a href="#" onclick="show_report('graph_discussion');" title="Discussion">Discussion</a></li>
|
129
|
-
</ul>
|
172
|
+
var request_rate_datasets = {
|
173
|
+
<%=
|
174
|
+
dataset_id=0;
|
175
|
+
data_string = graphs[:request_rate].series.map do |s|
|
176
|
+
points = []
|
177
|
+
s.x_values.each_with_index do |x,i|
|
178
|
+
points << "[#{s.x_values[i]}, #{s.y_values[i]}]"
|
179
|
+
end
|
180
|
+
"'#{dataset_id=dataset_id+1}' : {label: \"#{s.label}\", data: [#{points.join(", ")}]}"
|
181
|
+
end.join(", \n")
|
182
|
+
# ideal_points = graphs[:request_rate].series[0].x_values.map{|x|"[#{x},#{x}]"}
|
183
|
+
# data_string + ",'#{dataset_id=dataset_id+1}' : {label: \"Ideal\", data: [#{ideal_points.join(", ")}]}"
|
184
|
+
data_string
|
185
|
+
%>
|
186
|
+
};
|
130
187
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
</
|
188
|
+
var response_time_datasets = {
|
189
|
+
<%=
|
190
|
+
dataset_id=0;
|
191
|
+
data_string = graphs[:response_time].series.map do |s|
|
192
|
+
points = []
|
193
|
+
s.x_values.each_with_index do |x,i|
|
194
|
+
points << "[#{s.x_values[i]}, #{s.y_values[i]}]"
|
195
|
+
end
|
196
|
+
"'#{dataset_id=dataset_id+1}' : {label: \"#{s.label}\", data: [#{points.join(", ")}]}"
|
197
|
+
end.join(", \n")
|
198
|
+
data_string
|
199
|
+
%>
|
200
|
+
};
|
201
|
+
|
202
|
+
var max_request_rate_datasets = [
|
203
|
+
<%=
|
204
|
+
data_strings = []
|
205
|
+
data_string = graphs[:max_request_rate].series.each_with_index do |s, index|
|
206
|
+
data_strings << "{label: \"#{s.label}\", data: [[#{index}, #{s.y_values[0]}]]}"
|
207
|
+
end
|
208
|
+
data_strings.join(", \n")
|
209
|
+
%>
|
210
|
+
];
|
211
|
+
|
212
|
+
// hard-code color indices to prevent them from shifting as
|
213
|
+
// countries are turned on/off
|
214
|
+
var i = 0;
|
215
|
+
$.each(request_rate_datasets, function(key, val) {
|
216
|
+
val.color = i;
|
217
|
+
++i;
|
218
|
+
});
|
219
|
+
i = 0;
|
220
|
+
$.each(response_time_datasets, function(key, val) {
|
221
|
+
val.color = i;
|
222
|
+
++i;
|
223
|
+
});
|
224
|
+
|
225
|
+
// insert checkboxes
|
226
|
+
var choiceContainer = $("#choices");
|
227
|
+
$.each(request_rate_datasets, function(key, val) {
|
228
|
+
choiceContainer.append('<br/><input type="checkbox" name="' + key +
|
229
|
+
'" checked="checked" id="id' + key + '">' +
|
230
|
+
'<label style="font-size: 1.5em; padding-left: 10px;" for="id' + key + '">'
|
231
|
+
+ val.label.replace("Request rate for ", "") + '</label>');
|
232
|
+
});
|
233
|
+
choiceContainer.find("input").click(plotAccordingToChoices);
|
175
234
|
|
235
|
+
|
236
|
+
function plotAccordingToChoices() {
|
237
|
+
var data1 = [];
|
238
|
+
var data2 = [];
|
239
|
+
|
240
|
+
choiceContainer.find("input:checked").each(function () {
|
241
|
+
var key = $(this).attr("name");
|
242
|
+
if (key && request_rate_datasets[key])
|
243
|
+
data1.push(request_rate_datasets[key]);
|
244
|
+
if (key && response_time_datasets[key])
|
245
|
+
data2.push(response_time_datasets[key]);
|
246
|
+
});
|
247
|
+
|
248
|
+
if (data1.length > 0)
|
249
|
+
$.plot($("#request_rate"), data1, default_graph_options);
|
250
|
+
|
251
|
+
|
252
|
+
if (data2.length > 0)
|
253
|
+
$.plot($("#response_time"), data2, default_graph_options);
|
254
|
+
}
|
255
|
+
|
256
|
+
plotAccordingToChoices();
|
257
|
+
$.plot($("#max_request_rate"), max_request_rate_datasets, $.merge({series: {lines: {show: false}, bars : {show: true}}}, default_graph_options));
|
258
|
+
</script>
|
259
|
+
</body>
|
260
|
+
</html>
|