herokubench 0.0.8 → 0.0.9
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 +6 -14
- data/bin/hbench +3 -0
- data/bin/herokubench +11 -0
- data/lib/herokubench/bencher.rb +1 -0
- data/lib/herokubench/cli.rb +182 -145
- data/lib/herokubench/result.rb +143 -0
- data/lib/herokubench/version.rb +1 -1
- metadata +24 -16
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
ZmQ0NTk5Y2U4YmY4YjRjZGEzYTYwMTFhMjFmOTZhZmMzNDBlNWZkYTM2MWU2
|
10
|
-
ZmYyYTRiZDk0NTljNGM4ZTMzOTU3YTJmNjdmYTc2MWVkNTJlNGU3YzBjZmM4
|
11
|
-
YzQ2NGQ5ZTUwYjYwMDFhMDhhMTk1ZWU3NTMxY2E2ZGFkOGExODY=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
NzVjOTE1NmYzZWE5NDJhZGFjNTJlMDI1ZWQ1MTlkODUzNGZlMzc5MzZhNWU4
|
14
|
-
NGYwNTYwNGQ4NjIxM2E4NjJjYjJhNGI3ZmUwZmM0MjNmYzE0MDNlYmRjYTkz
|
15
|
-
NjI5YzkxOTBmNDFkNGY5Y2RkMzFjNWEyM2VhMmI5NmFiYmM4MTY=
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cb2da4a4361fae8687d6f1000bc956fa668f09ab
|
4
|
+
data.tar.gz: 1468528eb35ebf73b7cb01cf16b0a19f9c7017a1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fbad4b333c5c4e3704634f4cef8481b16928d92efd1f10ffd62b35aec857b581fa654cd4e78c1851555378e170e6f542966bb80593e9bd04b64fe35190e6822e
|
7
|
+
data.tar.gz: 40dc3ab2b9b76868efacbd43ac1f7663fd4467c446ef91fb2cef612e02472d593eb56bdaa0a4ce44add5d86f12c5e95e9eaaff7565504169cfeb49a0ab2ec903
|
data/bin/hbench
CHANGED
data/bin/herokubench
ADDED
data/lib/herokubench/bencher.rb
CHANGED
data/lib/herokubench/cli.rb
CHANGED
@@ -15,7 +15,8 @@ require "pathname"
|
|
15
15
|
require "stringio"
|
16
16
|
require 'tempfile'
|
17
17
|
require 'ruby-progressbar'
|
18
|
-
|
18
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
19
|
+
require 'herokubench/result'
|
19
20
|
|
20
21
|
# This class is based upon Vulcan, and copies heavily.
|
21
22
|
|
@@ -23,18 +24,6 @@ class HerokuBench::CLI < Thor
|
|
23
24
|
class_option "verbose", :type => :boolean
|
24
25
|
check_unknown_options! :except => [:ab, :multi]
|
25
26
|
default_task :ab
|
26
|
-
@@result_type = {
|
27
|
-
:connection_times=>/(.+):\s+\s+([\d|\.]+)\s+([\d|\.]+)\s+([\d|\.]+)\s+([\d|\.]+)\s+([\d|\.]+)/,
|
28
|
-
:generic_result=> /^([\w\s]+):\s*([\d|\.]+)/,
|
29
|
-
:response_time_cdf=>/(\d+%)\s+(\d+)/
|
30
|
-
}
|
31
|
-
@@summable_fields = ["Complete requests","Failed requests", "Write errors", "Requests per second",
|
32
|
-
"Total Transferred", "HTML transferred", "Concurrency Level"
|
33
|
-
]
|
34
|
-
@@medianable_fields = ["Connect", "Processing", "Waiting", "Total", "Time per request",
|
35
|
-
]
|
36
|
-
@@maxable_fields = ["50%", "66%", "75%", "80%", "90%", "95%", "98%", "99%","100%"]
|
37
|
-
|
38
27
|
class_options["verbose"] = false if class_options["verbose"].nil?
|
39
28
|
Heroku.user_agent = "heroku-gem/#{Heroku::VERSION} (#{RUBY_PLATFORM}) ruby/#{RUBY_VERSION}"
|
40
29
|
Heroku::Command.load
|
@@ -68,6 +57,7 @@ class HerokuBench::CLI < Thor
|
|
68
57
|
LONGDESC
|
69
58
|
|
70
59
|
def ab(*args)
|
60
|
+
puts config[:app]
|
71
61
|
error "no app yet, please create first" unless config[:app]
|
72
62
|
puts "Running one-off dyno, please be patient"
|
73
63
|
Heroku::Command.run("run", ["ab #{args.join(' ')}", "--app", "#{config[:app]}"])
|
@@ -88,43 +78,63 @@ class HerokuBench::CLI < Thor
|
|
88
78
|
def multi(dynos, *args)
|
89
79
|
error "no app yet, create first" unless config[:app]
|
90
80
|
error "Number of dynos must be an integer greater than 1" unless dynos.to_i >= 1
|
81
|
+
begin
|
82
|
+
num_requests = args[args.index("-n") + 1].to_i #hack to extract number of requests from the ab arguments.
|
91
83
|
|
92
|
-
|
84
|
+
bencher_path = File.expand_path("../bencher.rb",__FILE__)
|
93
85
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
86
|
+
numdynos = dynos.to_i
|
87
|
+
results = []
|
88
|
+
running_procs = {}
|
89
|
+
progesses = {}
|
90
|
+
ab_command = "ab #{args.join(' ')}"
|
99
91
|
|
100
|
-
|
92
|
+
p_bar = ProgressBar.create(:title=>'Benching',:total=>numdynos*num_requests, :smoothing => 0.6)
|
101
93
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
end
|
94
|
+
numdynos.times do |n|
|
95
|
+
t_file = Tempfile.new("hbench_out_#{n}")
|
96
|
+
pid = spawn( "ruby #{bencher_path} \"#{ab_command} \" --app #{config[:app]}", :out=>t_file.path, :err=>null_dev)
|
97
|
+
running_procs[pid] = t_file
|
98
|
+
progesses[pid] = 0
|
99
|
+
puts t_file.path
|
100
|
+
end
|
110
101
|
|
111
102
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
103
|
+
until running_procs.empty?
|
104
|
+
begin
|
105
|
+
complete_results = Timeout.timeout(1) do
|
106
|
+
pid = Process.wait
|
107
|
+
results.push running_procs.delete(pid)
|
108
|
+
p_bar.progress += num_requests - progesses[pid]
|
109
|
+
progesses[pid] = num_requests
|
110
|
+
end
|
111
|
+
rescue Timeout::Error
|
112
|
+
running_procs.each do |pid, tfile|
|
113
|
+
progress = get_progress(tfile)
|
114
|
+
p_bar.progress += progress - progesses[pid]
|
115
|
+
progesses[pid] = progress
|
116
|
+
end
|
117
|
+
end
|
117
118
|
end
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
119
|
+
|
120
|
+
summary_result = ApacheBenchSummaryResult.new
|
121
|
+
|
122
|
+
puts results
|
123
|
+
results.each do |tfile|
|
124
|
+
summary_result.add_result(ApacheBenchResult.new(tfile))
|
125
|
+
end
|
126
|
+
summary_result.print()
|
127
|
+
|
128
|
+
|
129
|
+
rescue Interrupt
|
130
|
+
say "Exiting...Please be patient"
|
131
|
+
kill_running_procs(running_procs)
|
132
|
+
kill_running_dynos()
|
133
|
+
say "Done"
|
134
|
+
rescue => exception
|
135
|
+
say("HerokuBench ran into an unexpected exception. Please contact @wcdolphin",:red)
|
136
|
+
say(exception,:red)
|
137
|
+
puts exception.backtrace
|
128
138
|
end
|
129
139
|
end
|
130
140
|
|
@@ -138,50 +148,79 @@ class HerokuBench::CLI < Thor
|
|
138
148
|
|
139
149
|
# pretty much the opposite of parse. Returns a string of the best way
|
140
150
|
# to represent a float, int or string value
|
141
|
-
def
|
151
|
+
def serialize(v)
|
142
152
|
((float = v.round(1)) && (float % 1.0 == 0) ? float.to_i.to_s : float.to_s) rescue v.to_s
|
143
153
|
end
|
144
154
|
|
145
|
-
def
|
146
|
-
|
147
|
-
|
148
|
-
results.each do |result|
|
149
|
-
result.each do |type, hash|
|
150
|
-
summary[type] = {} if summary[type].nil?
|
151
|
-
hash.each do |k,v|
|
152
|
-
summary[type][k] = [0.0] * v.length if summary[type][k].nil?
|
153
|
-
v.each_index do |i|
|
154
|
-
if not @@summable_fields.index(k).nil?
|
155
|
-
summary[type][k][i] += v[i]
|
156
|
-
elsif not @@medianable_fields.index(k).nil?
|
157
|
-
summary[type][k][i] += v[i] / results.length.to_f
|
158
|
-
elsif not @@maxable_fields.index(k).nil?
|
159
|
-
summary[type][k][i] = [v[i], summary[type][k][i]].max
|
160
|
-
else
|
161
|
-
summary[type][k][i] = v[i]
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
155
|
+
def get_progress(tfile)
|
156
|
+
tfile.rewind
|
167
157
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
158
|
+
progress = tfile.each_line.collect do |line|
|
159
|
+
group = line.scan(/Completed (\d+) requests/)
|
160
|
+
group = group.empty? ? 0 : group[0][0]
|
161
|
+
end
|
162
|
+
progress = progress.last.to_i
|
163
|
+
end
|
164
|
+
# def summarize(results)
|
165
|
+
# summary = {}
|
166
|
+
|
167
|
+
# results.each do |result|
|
168
|
+
# result.each do |type, hash|
|
169
|
+
# summary[type] = {} if summary[type].nil?
|
170
|
+
# hash.each do |k,v|
|
171
|
+
# summary[type][k] = [0.0] * v.length if summary[type][k].nil?
|
172
|
+
# v.each_index do |i|
|
173
|
+
# if not @@summable_fields.index(k).nil?
|
174
|
+
# summary[type][k][i] += v[i]
|
175
|
+
# elsif not @@medianable_fields.index(k).nil?
|
176
|
+
# summary[type][k][i] += v[i] / results.length.to_f
|
177
|
+
# elsif not @@maxable_fields.index(k).nil?
|
178
|
+
# summary[type][k][i] = [v[i], summary[type][k][i]].max
|
179
|
+
# else
|
180
|
+
# summary[type][k][i] = v[i]
|
181
|
+
# end
|
182
|
+
# end
|
183
|
+
# end
|
184
|
+
# end
|
185
|
+
# end
|
186
|
+
|
187
|
+
# say "\tCumulative results, summed across dynos"
|
188
|
+
# say ""
|
189
|
+
# summary[:generic_result].each{|k,v| say format(k+":",v, 25)}
|
190
|
+
|
191
|
+
# say ""
|
192
|
+
# say "\t Connection Times (ms), median across dynos"
|
193
|
+
# say format("",["min", "mean", "[+/-sd]" ,"median","max"],15)
|
194
|
+
# summary[:connection_times].each{|k,v| say format(k+":",v, 15)}
|
195
|
+
|
196
|
+
# say ""
|
197
|
+
# say "\t Percentage of the requests served within a certain time (ms)"
|
198
|
+
# say "\t across dynos"
|
199
|
+
# summary[:response_time_cdf].each{|k,v| say format(k+"",v, 15)}
|
200
|
+
# end
|
201
|
+
|
202
|
+
def kill_running_procs(running_procs)
|
203
|
+
print "Killing running processes"
|
204
|
+
running_procs.keys.each do |pid|
|
205
|
+
Process.kill("INT", pid)
|
206
|
+
print "."
|
207
|
+
end
|
208
|
+
end
|
176
209
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
210
|
+
def kill_running_dynos()
|
211
|
+
print "\nKilling running dynos"
|
212
|
+
result = capture { Heroku::Command.run("ps", ["--app", "#{config[:app]}"]) }
|
213
|
+
result.split("\n").each do |line|
|
214
|
+
dyno_name = line.scan(/(run.[\d]+)/)
|
215
|
+
unless dyno_name.nil? or dyno_name.empty?
|
216
|
+
capture {Heroku::Command.run("ps:stop", ["#{dyno_name[0][0]}","--app", "#{config[:app]}"])}
|
217
|
+
print '.'
|
218
|
+
end
|
219
|
+
end
|
181
220
|
end
|
182
221
|
|
183
222
|
def format(k,v, pad)
|
184
|
-
"#{fill(k,pad)}#{v.map{|val| fill(
|
223
|
+
"#{fill(k,pad)}#{v.map{|val| fill(serialize val)}.join('')}\r\n"
|
185
224
|
end
|
186
225
|
|
187
226
|
def fill(str, length=12)
|
@@ -194,86 +233,84 @@ class HerokuBench::CLI < Thor
|
|
194
233
|
@@result_type.each do |k,v|
|
195
234
|
group = line.scan(v)
|
196
235
|
if not group.nil? and group.length.equal? 1
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
end
|
203
|
-
end
|
204
|
-
end
|
205
|
-
result_hash
|
206
|
-
end
|
207
|
-
|
208
|
-
def update
|
209
|
-
error "no app yet, create first" unless config[:app]
|
210
|
-
|
211
|
-
Dir.mktmpdir do |dir|
|
212
|
-
Dir.chdir(dir) do
|
213
|
-
system "git init -q"
|
214
|
-
system "git remote add origin git@github.com:wcdolphin/heroku-benchserver.git"
|
215
|
-
system "git remote add heroku git@#{heroku_git_domain}:#{config[:app]}.git"
|
216
|
-
pullres = capture { system "git pull --quiet origin master"}
|
217
|
-
pushres = capture { system "git push heroku master --quiet"}
|
218
|
-
end
|
219
|
-
end
|
236
|
+
capture = group[0].map {|v| parse v} #convert to float/int/etc
|
237
|
+
result_hash[k] = {} unless result_hash.has_key?(k)
|
238
|
+
res_key = capture[0]
|
239
|
+
res_values = capture.slice(1, capture.length)
|
240
|
+
result_hash[k][res_key] = res_values
|
220
241
|
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
result_hash
|
245
|
+
end
|
221
246
|
|
222
|
-
|
223
|
-
|
224
|
-
yield
|
225
|
-
$stdout = STDOUT
|
226
|
-
results.close_write
|
227
|
-
results.rewind
|
228
|
-
return results.read
|
229
|
-
end
|
247
|
+
def update
|
248
|
+
error "no app yet, create first" unless config[:app]
|
230
249
|
|
250
|
+
Dir.mktmpdir do |dir|
|
251
|
+
Dir.chdir(dir) do
|
252
|
+
system "git init -q"
|
253
|
+
system "git remote add origin git@github.com:wcdolphin/heroku-benchserver.git"
|
254
|
+
system "git remote add heroku git@#{heroku_git_domain}:#{config[:app]}.git"
|
255
|
+
pullres = capture { system "git pull --quiet origin master"}
|
256
|
+
pushres = capture { system "git push heroku master --quiet"}
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
231
260
|
|
261
|
+
def capture
|
262
|
+
results = $stdout = StringIO.new
|
263
|
+
yield
|
264
|
+
$stdout = STDOUT
|
265
|
+
results.close_write
|
266
|
+
results.rewind
|
267
|
+
return results.read
|
268
|
+
end
|
232
269
|
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
end
|
270
|
+
#Yeah, we are windows compatible. Because you should be too.
|
271
|
+
def null_dev
|
272
|
+
return test(?e, '/dev/null') ? '/dev/null' : 'NUL:'
|
273
|
+
end
|
237
274
|
|
238
|
-
def action(message)
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
end
|
275
|
+
def action(message)
|
276
|
+
print "#{message}... "
|
277
|
+
yield
|
278
|
+
puts "done"
|
279
|
+
end
|
243
280
|
|
244
|
-
def heroku(command)
|
245
|
-
|
246
|
-
end
|
281
|
+
def heroku(command)
|
282
|
+
%x{ env BUNDLE_GEMFILE= heroku #{command} 2>&1 }
|
283
|
+
end
|
247
284
|
|
248
|
-
def config_file
|
249
|
-
|
250
|
-
end
|
285
|
+
def config_file
|
286
|
+
File.expand_path("~/.herokubench")
|
287
|
+
end
|
251
288
|
|
252
|
-
def config
|
253
|
-
|
254
|
-
end
|
289
|
+
def config
|
290
|
+
read_config
|
291
|
+
end
|
255
292
|
|
256
|
-
def read_config
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
end
|
293
|
+
def read_config
|
294
|
+
return {} unless File.exists?(config_file)
|
295
|
+
config = YAML.load_file(config_file)
|
296
|
+
config.is_a?(Hash) ? config : {}
|
297
|
+
end
|
261
298
|
|
262
|
-
def write_config(config)
|
263
|
-
|
264
|
-
|
265
|
-
|
299
|
+
def write_config(config)
|
300
|
+
full_config = read_config.merge(config)
|
301
|
+
File.open(config_file, "w") do |file|
|
302
|
+
file.puts YAML.dump(full_config)
|
303
|
+
end
|
266
304
|
end
|
267
|
-
end
|
268
305
|
|
269
|
-
def error(message)
|
270
|
-
|
271
|
-
|
272
|
-
end
|
306
|
+
def error(message)
|
307
|
+
puts "!! #{message}"
|
308
|
+
exit 1
|
309
|
+
end
|
273
310
|
|
274
|
-
def server_path
|
275
|
-
|
276
|
-
end
|
311
|
+
def server_path
|
312
|
+
File.expand_path("../../../server", __FILE__)
|
313
|
+
end
|
277
314
|
|
278
315
|
#
|
279
316
|
# heroku_git_domain checks to see if the heroku-accounts plugin is present,
|
@@ -0,0 +1,143 @@
|
|
1
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
2
|
+
require "thor"
|
3
|
+
require "digest/sha1"
|
4
|
+
require "heroku/auth"
|
5
|
+
require "heroku/command"
|
6
|
+
require "heroku/command/base"
|
7
|
+
require "heroku/command/help"
|
8
|
+
require "heroku/command/apps"
|
9
|
+
require "heroku/cli"
|
10
|
+
require "heroku/plugin"
|
11
|
+
require "thor"
|
12
|
+
require "tmpdir"
|
13
|
+
require "uri"
|
14
|
+
require "herokubench"
|
15
|
+
require "yaml"
|
16
|
+
require "pathname"
|
17
|
+
require "stringio"
|
18
|
+
require 'tempfile'
|
19
|
+
require 'ruby-progressbar'
|
20
|
+
|
21
|
+
|
22
|
+
class BaseResult
|
23
|
+
@@summable_fields = []
|
24
|
+
@@maxable_fields = []
|
25
|
+
end
|
26
|
+
|
27
|
+
class ApacheBenchResult < BaseResult
|
28
|
+
attr_accessor :result_hash
|
29
|
+
@@result_regexes = {
|
30
|
+
:connection_times=>/(.+):\s+\s+([\d|\.]+)\s+([\d|\.]+)\s+([\d|\.]+)\s+([\d|\.]+)\s+([\d|\.]+)/,
|
31
|
+
:generic_result=> /^([\w\s]+):\s*([\d|\.]+)/,
|
32
|
+
:response_time_cdf=>/(\d+%)\s+(\d+)/
|
33
|
+
}
|
34
|
+
@@summable_fields = ["Complete requests","Failed requests", "Write errors", "Requests per second", "Total transferred", "HTML transferred", "Concurrency Level"]
|
35
|
+
@@averageable_fields = ["Connect", "Processing", "Waiting", "Total", "Time per request", "Time taken for tests"]
|
36
|
+
@@maxable_fields = ["50%", "66%", "75%", "80%", "90%", "95%", "98%", "99%","100%"]
|
37
|
+
|
38
|
+
def initialize(temp_file)
|
39
|
+
@result_hash = {}
|
40
|
+
|
41
|
+
temp_file.each_line do |line|
|
42
|
+
@@result_regexes.each do |type,v|
|
43
|
+
group = line.scan(v)
|
44
|
+
if not group.nil? and group.length.equal? 1
|
45
|
+
capture = group[0].map {|v| parse v} #convert to float/int/etc
|
46
|
+
res_key = capture[0]
|
47
|
+
res_values = capture.slice(1, capture.length)
|
48
|
+
|
49
|
+
@result_hash[type] = {} unless @result_hash.has_key? type
|
50
|
+
@result_hash[type][res_key] = res_values.length == 1 ? res_values[0] : res_values
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
puts @result_hash
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
class ApacheBenchSummaryResult < ApacheBenchResult
|
60
|
+
|
61
|
+
def initialize()
|
62
|
+
@results = []
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_result(ab_result)
|
66
|
+
@results.push ab_result
|
67
|
+
end
|
68
|
+
|
69
|
+
def get_summary_result()
|
70
|
+
summary_result_hash = {}
|
71
|
+
|
72
|
+
@results.each do |result|
|
73
|
+
result.result_hash.each do |result_type, res_type_hashes|
|
74
|
+
summary_result_hash[result_type] = {} unless summary_result_hash.has_key? result_type
|
75
|
+
res_type_hashes.each do |result_key, value|
|
76
|
+
summary_result_hash[result_type][result_key] = [] unless summary_result_hash[result_type].has_key? result_key
|
77
|
+
summary_result_hash[result_type][result_key].push value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
summary_result_hash.each do |result_type, result_hash|
|
83
|
+
result_hash.each do |result_name, result_values|
|
84
|
+
if @@summable_fields.include? result_name
|
85
|
+
summary_result_hash[result_type][result_name] = result_values.inject(:+)
|
86
|
+
elsif @@averageable_fields.include? result_name
|
87
|
+
summary_result_hash[result_type][result_name] = deep_average(result_values)
|
88
|
+
elsif @@maxable_fields.include? result_name
|
89
|
+
summary_result_hash[result_type][result_name] = result_values.max
|
90
|
+
else
|
91
|
+
summary_result_hash[result_type][result_name] = result_values.first
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
summary_result_hash
|
97
|
+
end
|
98
|
+
def print
|
99
|
+
# puts "-----"
|
100
|
+
summary = self.get_summary_result()
|
101
|
+
# puts summary
|
102
|
+
puts "-----"
|
103
|
+
puts "\t Cumulative results, summed across dynos"
|
104
|
+
puts ""
|
105
|
+
summary[:generic_result].each{|k,v| printf "%-20s %s\n", k + ":",v}
|
106
|
+
|
107
|
+
puts ""
|
108
|
+
puts "\t Connection Times (ms), median across dynos"
|
109
|
+
printf "%-20s %-6s %-6s %-6s %s\n", "","min", "mean", "[+/-sd]" ,"median","max"
|
110
|
+
summary[:connection_times].each do |k,v|
|
111
|
+
printf "%-20s %-6s %-6s %-6s %s\n",k +":", v[0], v[1], v[2], v[3], v[4], v[5]
|
112
|
+
end
|
113
|
+
puts ""
|
114
|
+
puts "\t Percentage of the requests served within a certain time (ms)"
|
115
|
+
puts "\t across dynos"
|
116
|
+
summary[:response_time_cdf].each{|k,v| printf " %-20s %s\n", k,v}
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def parse(v)
|
127
|
+
((float = Float(v)) && (float % 1.0 == 0) ? float.to_i : float) rescue v
|
128
|
+
end
|
129
|
+
|
130
|
+
def deep_average(arr)
|
131
|
+
result = []
|
132
|
+
if not arr.empty? and arr[0].is_a? Array #we need to do a 'deep' average.
|
133
|
+
arr[0].each_index do |i|
|
134
|
+
result[i] = arr.collect {|a| a[i]/arr.length.to_f}.inject(:+)
|
135
|
+
end
|
136
|
+
result
|
137
|
+
else
|
138
|
+
arr.collect {|a| a/arr.length}.inject(:+)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
|
data/lib/herokubench/version.rb
CHANGED
metadata
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: herokubench
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cory Dolphin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: heroku
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 2.26.0
|
20
20
|
- - <
|
@@ -24,7 +24,7 @@ dependencies:
|
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 2.26.0
|
30
30
|
- - <
|
@@ -48,35 +48,43 @@ dependencies:
|
|
48
48
|
name: ruby-progressbar
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- -
|
51
|
+
- - '>='
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: 1.
|
53
|
+
version: 1.2.0
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
|
-
- -
|
58
|
+
- - '>='
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: 1.
|
61
|
-
description:
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
60
|
+
version: 1.2.0
|
61
|
+
description: |2
|
62
|
+
Make it rain on the cloud.
|
63
|
+
|
64
|
+
herokubench, or hb for short, is a simple gem which eanbles you to easily load test websites,
|
65
|
+
using a server hosted by Heroku (on AWS). The gem manages deploying
|
66
|
+
an app with no running dynos (free), and abuses the concept of one-off
|
67
|
+
jobs to run the Apache Benchmark, ab.
|
68
|
+
|
69
|
+
Confused? Checkout `hb help`
|
66
70
|
email: wcdolphin@gmail.com
|
67
71
|
executables:
|
68
72
|
- hbench
|
73
|
+
- herokubench
|
69
74
|
extensions: []
|
70
75
|
extra_rdoc_files: []
|
71
76
|
files:
|
72
77
|
- bin/hbench
|
78
|
+
- bin/herokubench
|
73
79
|
- lib/herokubench/bencher.rb
|
74
80
|
- lib/herokubench/cli.rb
|
81
|
+
- lib/herokubench/result.rb
|
75
82
|
- lib/herokubench/version.rb
|
76
83
|
- lib/herokubench.rb
|
77
84
|
- README.md
|
78
85
|
homepage: https://github.com/wcdolphin/heroku-bench
|
79
|
-
licenses:
|
86
|
+
licenses:
|
87
|
+
- MIT
|
80
88
|
metadata: {}
|
81
89
|
post_install_message: Please run 'hbench create' to create your bench-server.
|
82
90
|
rdoc_options: []
|
@@ -84,12 +92,12 @@ require_paths:
|
|
84
92
|
- lib
|
85
93
|
required_ruby_version: !ruby/object:Gem::Requirement
|
86
94
|
requirements:
|
87
|
-
- -
|
95
|
+
- - '>='
|
88
96
|
- !ruby/object:Gem::Version
|
89
97
|
version: '0'
|
90
98
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
99
|
requirements:
|
92
|
-
- -
|
100
|
+
- - '>='
|
93
101
|
- !ruby/object:Gem::Version
|
94
102
|
version: '0'
|
95
103
|
requirements: []
|