kronk 1.7.8 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +32 -0
- data/Manifest.txt +5 -5
- data/README.rdoc +5 -3
- data/Rakefile +3 -4
- data/TODO.rdoc +22 -6
- data/lib/kronk.rb +13 -7
- data/lib/kronk/buffered_io.rb +26 -0
- data/lib/kronk/cmd.rb +58 -44
- data/lib/kronk/constants.rb +5 -6
- data/lib/kronk/diff.rb +12 -13
- data/lib/kronk/diff/output.rb +1 -1
- data/lib/kronk/http.rb +105 -0
- data/lib/kronk/player.rb +74 -82
- data/lib/kronk/player/benchmark.rb +34 -35
- data/lib/kronk/player/stream.rb +6 -6
- data/lib/kronk/player/suite.rb +17 -16
- data/lib/kronk/player/tsv.rb +51 -0
- data/lib/kronk/queue_runner.rb +126 -102
- data/lib/kronk/request.rb +144 -82
- data/lib/kronk/response.rb +520 -202
- data/lib/kronk/test/helper_methods.rb +1 -1
- data/test/mocks/200_gzip.txt +0 -0
- data/test/mocks/200_inflate.txt +0 -0
- data/test/test_assertions.rb +1 -1
- data/test/test_cmd.rb +18 -11
- data/test/test_diff.rb +39 -0
- data/test/test_helper.rb +18 -14
- data/test/test_helper_methods.rb +5 -4
- data/test/test_kronk.rb +8 -11
- data/test/test_player.rb +67 -123
- data/test/test_request.rb +8 -14
- data/test/test_response.rb +228 -60
- metadata +20 -31
- data/lib/kronk/async.rb +0 -118
- data/lib/kronk/async/em_ext.rb +0 -34
- data/lib/kronk/async/request.rb +0 -73
- data/lib/kronk/async/response.rb +0 -70
- data/lib/kronk/player/output.rb +0 -49
data/lib/kronk/constants.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
class Kronk
|
2
2
|
|
3
3
|
# Generic Request exception.
|
4
|
-
class
|
4
|
+
class Error < ::StandardError; end
|
5
5
|
|
6
6
|
# Raised when parsing fails.
|
7
|
-
class ParserError <
|
7
|
+
class ParserError < Error; end
|
8
8
|
|
9
9
|
# Raised when the URI was not resolvable.
|
10
|
-
class NotFoundError <
|
10
|
+
class NotFoundError < Error; end
|
11
11
|
|
12
12
|
# Raised when HTTP times out.
|
13
|
-
class TimeoutError <
|
13
|
+
class TimeoutError < Error; end
|
14
14
|
|
15
15
|
# Raised when a missing (but non-mandatory) dependency can't be loaded.
|
16
|
-
class MissingDependency <
|
16
|
+
class MissingDependency < Error; end
|
17
17
|
|
18
18
|
|
19
19
|
# Config directory.
|
@@ -80,7 +80,6 @@ class Kronk
|
|
80
80
|
|
81
81
|
# Default config to use.
|
82
82
|
DEFAULT_CONFIG = {
|
83
|
-
:async => 'auto',
|
84
83
|
:content_types => DEFAULT_CONTENT_TYPES.dup,
|
85
84
|
:cache_file => DEFAULT_CACHE_FILE,
|
86
85
|
:context => 3,
|
data/lib/kronk/diff.rb
CHANGED
@@ -96,6 +96,7 @@ class Kronk
|
|
96
96
|
@meta.last[0] = str1.meta[0] if str1.respond_to?(:meta)
|
97
97
|
@meta.last[1] = str2.meta[0] if str2.respond_to?(:meta)
|
98
98
|
end
|
99
|
+
|
99
100
|
diff_ary.concat arr1[c[1], c[0]]
|
100
101
|
|
101
102
|
last_i1 = c[1] + c[0]
|
@@ -161,8 +162,7 @@ class Kronk
|
|
161
162
|
|
162
163
|
arr2_map = {}
|
163
164
|
arr2.each_with_index do |line, j|
|
164
|
-
arr2_map[line] ||= []
|
165
|
-
arr2_map[line] << j
|
165
|
+
(arr2_map[line] ||= []) << j
|
166
166
|
end
|
167
167
|
|
168
168
|
arr1.each_with_index do |line, i|
|
@@ -184,14 +184,7 @@ class Kronk
|
|
184
184
|
end
|
185
185
|
|
186
186
|
len = j - start_j
|
187
|
-
|
188
|
-
sequences[len] ||= []
|
189
|
-
sequences[len] << [len, i, start_j]
|
190
|
-
|
191
|
-
if len > 1
|
192
|
-
sequences[len-1] ||= []
|
193
|
-
sequences[len-1] << [(len-1), i, start_j]
|
194
|
-
end
|
187
|
+
(sequences[len] ||= []) << [len, i, start_j]
|
195
188
|
end
|
196
189
|
end
|
197
190
|
|
@@ -239,11 +232,17 @@ class Kronk
|
|
239
232
|
|
240
233
|
private
|
241
234
|
|
242
|
-
def yield_sequences sequences,
|
243
|
-
while sequences.length >
|
235
|
+
def yield_sequences sequences, &block # :nodoc:
|
236
|
+
while sequences.length > 0
|
244
237
|
item = sequences.pop
|
245
238
|
next unless item
|
246
|
-
item.each
|
239
|
+
item.each do |seq|
|
240
|
+
resp = yield seq
|
241
|
+
if !resp && seq[0] > 1
|
242
|
+
seq[0] = seq[0] - 1
|
243
|
+
(sequences[seq[0]] ||= []) << seq
|
244
|
+
end
|
245
|
+
end
|
247
246
|
end
|
248
247
|
end
|
249
248
|
end
|
data/lib/kronk/diff/output.rb
CHANGED
data/lib/kronk/http.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
class Kronk
|
2
|
+
|
3
|
+
class HTTPBadResponse < Net::HTTPBadResponse; end
|
4
|
+
|
5
|
+
##
|
6
|
+
# Wrapper for Net::HTTP
|
7
|
+
|
8
|
+
class HTTP < Net::HTTP
|
9
|
+
|
10
|
+
def request(req, body=nil, opts={}, &block) # :yield: +response+
|
11
|
+
unless started?
|
12
|
+
start {
|
13
|
+
req['connection'] ||= 'close'
|
14
|
+
return request(req, body, &block)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
if proxy_user()
|
18
|
+
req.proxy_basic_auth proxy_user(), proxy_pass() unless use_ssl?
|
19
|
+
end
|
20
|
+
req.set_body_internal body
|
21
|
+
res = transport_request(req, true, opts, &block)
|
22
|
+
if sspi_auth?(res)
|
23
|
+
sspi_auth(req)
|
24
|
+
res = transport_request(req, true, opts, &block)
|
25
|
+
end
|
26
|
+
res
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def transport_request(req, allow_retry=true, opts={})
|
33
|
+
# Check if previous request was made on same socket and needs
|
34
|
+
# to be completed before we can read the new response.
|
35
|
+
if Kronk::BufferedIO === @socket && !@socket.response.read?
|
36
|
+
@socket.response.body
|
37
|
+
end
|
38
|
+
|
39
|
+
begin_transport req
|
40
|
+
req.exec @socket, @curr_http_version, edit_path(req.path)
|
41
|
+
|
42
|
+
begin
|
43
|
+
opts[:timeout] ||= @socket.read_timeout
|
44
|
+
res = Kronk::Response.new(@socket.io, opts)
|
45
|
+
end while kronk_resp_type(res) == Net::HTTPContinue
|
46
|
+
|
47
|
+
if res.headless?
|
48
|
+
raise HTTPBadResponse, "Invalid HTTP response" unless allow_retry
|
49
|
+
@socket.io.close
|
50
|
+
res = transport_request(req, false, opts)
|
51
|
+
end
|
52
|
+
|
53
|
+
@socket = res.io
|
54
|
+
|
55
|
+
res.body {|chunk|
|
56
|
+
yield res, chunk
|
57
|
+
} if block_given?
|
58
|
+
|
59
|
+
end_transport req, res
|
60
|
+
res
|
61
|
+
|
62
|
+
rescue => exception
|
63
|
+
D "Conn close because of error #{exception}"
|
64
|
+
@socket.close if @socket and not @socket.closed?
|
65
|
+
raise exception
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def end_transport(req, res)
|
70
|
+
@curr_http_version = res.http_version
|
71
|
+
if @socket.closed?
|
72
|
+
D 'Conn socket closed'
|
73
|
+
elsif keep_alive?(req, res)
|
74
|
+
D 'Conn keep-alive'
|
75
|
+
elsif not res.body and @close_on_empty_response
|
76
|
+
D 'Conn close'
|
77
|
+
@socket.close
|
78
|
+
else
|
79
|
+
D 'Conn close'
|
80
|
+
@socket.close
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def sspi_auth?(res)
|
86
|
+
return false unless @sspi_enabled
|
87
|
+
if kronk_resp_type(res) == HTTPProxyAuthenticationRequired and
|
88
|
+
proxy? and res["Proxy-Authenticate"].include?("Negotiate")
|
89
|
+
begin
|
90
|
+
require 'win32/sspi'
|
91
|
+
true
|
92
|
+
rescue LoadError
|
93
|
+
false
|
94
|
+
end
|
95
|
+
else
|
96
|
+
false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
def kronk_resp_type res
|
102
|
+
Net::HTTPResponse::CODE_TO_OBJ[res.code]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/kronk/player.rb
CHANGED
@@ -2,46 +2,70 @@ class Kronk
|
|
2
2
|
|
3
3
|
##
|
4
4
|
# The Player class is used for running multiple requests and comparisons and
|
5
|
-
# providing useful output through Player
|
6
|
-
# Kronk includes a Suite (test-like)
|
5
|
+
# providing useful output through inherited Player classes.
|
6
|
+
# Kronk includes a Suite (test-like), a Stream (chunked),
|
7
7
|
# and a Benchmark output.
|
8
8
|
|
9
9
|
class Player < QueueRunner
|
10
10
|
|
11
|
-
|
11
|
+
##
|
12
|
+
# Instantiate a new type of player, typically :suite, :stream, or
|
13
|
+
# :benchmark.
|
14
|
+
|
15
|
+
def self.new_type type, opts={}
|
16
|
+
klass =
|
17
|
+
case type.to_s
|
18
|
+
when /^(Player::)?benchmark$/i then Benchmark
|
19
|
+
when /^(Player::)?stream$/i then Stream
|
20
|
+
when /^(Player::)?suite$/i then Suite
|
21
|
+
when /^(Player::)?tsv$/i then TSV
|
22
|
+
else
|
23
|
+
Kronk.find_const type.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
klass.new opts
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
attr_accessor :input, :qps, :concurrency, :mutex
|
12
32
|
|
13
33
|
##
|
14
34
|
# Create a new Player for batch diff or response validation.
|
15
35
|
# Supported options are:
|
16
36
|
# :io:: IO - The IO instance to read from
|
17
|
-
# :output:: Class - The output class to use (see Player::Output)
|
18
37
|
# :parser:: Class - The IO parser to use.
|
38
|
+
# :concurrency:: Fixnum - Maximum number of concurrent items to process
|
39
|
+
# :qps:: Fixnum - Number of queries to process per second
|
19
40
|
|
20
41
|
def initialize opts={}
|
21
42
|
super
|
22
|
-
|
43
|
+
|
44
|
+
@concurrency = opts[:concurrency]
|
45
|
+
@concurrency = 1 if !@concurrency || @concurrency <= 0
|
46
|
+
@qps = opts[:qps]
|
23
47
|
|
24
48
|
@input = InputReader.new opts[:io], opts[:parser]
|
25
|
-
@use_output = true
|
26
49
|
@last_req = nil
|
50
|
+
@mutex = Mutex.new
|
27
51
|
|
28
52
|
@on_input = Proc.new do
|
29
53
|
stop_input! if !@number && @input.eof?
|
30
54
|
@last_req = @input.get_next || @queue.last || @last_req || {}
|
31
55
|
end
|
32
56
|
|
33
|
-
@on_result = Proc.new do |kronk, err, mutex|
|
34
|
-
err ? @output.error(err, kronk, mutex) :
|
35
|
-
@output.result(kronk, mutex)
|
36
|
-
end
|
37
|
-
|
38
57
|
on(:input, &@on_input)
|
39
58
|
on(:interrupt){
|
40
|
-
|
59
|
+
interrupt and return if respond_to?(:interrupt)
|
60
|
+
complete if respond_to?(:complete)
|
41
61
|
exit 2
|
42
62
|
}
|
43
|
-
on(:start){
|
44
|
-
|
63
|
+
on(:start){
|
64
|
+
start if respond_to?(:start)
|
65
|
+
}
|
66
|
+
on(:complete){
|
67
|
+
complete if respond_to?(:complete)
|
68
|
+
}
|
45
69
|
end
|
46
70
|
|
47
71
|
|
@@ -58,26 +82,6 @@ class Kronk
|
|
58
82
|
end
|
59
83
|
|
60
84
|
|
61
|
-
##
|
62
|
-
# The kind of output to use. Typically Player::Suite or Player::Stream.
|
63
|
-
# Takes an output class or a string that represents a class constant.
|
64
|
-
|
65
|
-
def output_from new_output
|
66
|
-
return @output = new_output.new(self) if Class === new_output
|
67
|
-
|
68
|
-
klass =
|
69
|
-
case new_output.to_s
|
70
|
-
when /^(Player::)?benchmark$/i then Benchmark
|
71
|
-
when /^(Player::)?stream$/i then Stream
|
72
|
-
when /^(Player::)?suite$/i then Suite
|
73
|
-
else
|
74
|
-
Kronk.find_const new_output
|
75
|
-
end
|
76
|
-
|
77
|
-
@output = klass.new self if klass
|
78
|
-
end
|
79
|
-
|
80
|
-
|
81
85
|
##
|
82
86
|
# Process the queue to compare two uris.
|
83
87
|
# If options are given, they are merged into every request.
|
@@ -85,10 +89,10 @@ class Kronk
|
|
85
89
|
def compare uri1, uri2, opts={}, &block
|
86
90
|
return Cmd.compare uri1, uri2, @queue.shift.merge(opts) if single_request?
|
87
91
|
|
88
|
-
|
92
|
+
on(:result){|(kronk, err)| trigger_result(kronk, err, &block) }
|
89
93
|
|
90
|
-
run do |
|
91
|
-
|
94
|
+
run opts do |kronk|
|
95
|
+
kronk.compare uri1, uri2
|
92
96
|
end
|
93
97
|
end
|
94
98
|
|
@@ -100,55 +104,37 @@ class Kronk
|
|
100
104
|
def request uri, opts={}, &block
|
101
105
|
return Cmd.request uri, @queue.shift.merge(opts) if single_request?
|
102
106
|
|
103
|
-
|
107
|
+
on(:result){|(kronk, err)| trigger_result(kronk, err, &block) }
|
104
108
|
|
105
|
-
run do |
|
106
|
-
|
109
|
+
run opts do |kronk|
|
110
|
+
kronk.request uri
|
107
111
|
end
|
108
112
|
end
|
109
113
|
|
110
114
|
|
111
115
|
##
|
112
|
-
#
|
113
|
-
# Output#error method.
|
114
|
-
#
|
115
|
-
# If given a block, will yield the Kronk instance and error. If
|
116
|
-
# a third argument is given, mutex will also be passed and the
|
117
|
-
# block won't be called from a mutex lock.
|
118
|
-
#
|
119
|
-
# Returns the result of the block or of the called Output method.
|
116
|
+
# Similar to QueueRunner#run but yields a Kronk instance.
|
120
117
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
err = e
|
118
|
+
def run opts={}
|
119
|
+
if @qps
|
120
|
+
method = :periodically
|
121
|
+
arg = 1.0 / @qps.to_f
|
122
|
+
else
|
123
|
+
method = :concurrently
|
124
|
+
arg = @concurrency
|
129
125
|
end
|
130
126
|
|
131
|
-
|
132
|
-
|
127
|
+
send method, arg do |kronk_opts|
|
128
|
+
err = nil
|
129
|
+
kronk = Kronk.new kronk_opts.merge(opts)
|
133
130
|
|
131
|
+
begin
|
132
|
+
yield kronk
|
133
|
+
rescue *Kronk::Cmd::RESCUABLE => e
|
134
|
+
err = e
|
135
|
+
end
|
134
136
|
|
135
|
-
|
136
|
-
# Run a single compare or request and call the Output#result or
|
137
|
-
# Output#error method using EventMachine.
|
138
|
-
#
|
139
|
-
# If given a block, will yield the Kronk instance and error. If
|
140
|
-
# a third argument is given, mutex will also be passed and the
|
141
|
-
# block won't be called from a mutex lock.
|
142
|
-
#
|
143
|
-
# Returns either a EM::MultiRequest or an EM::Connection handler.
|
144
|
-
|
145
|
-
def process_one_async opts={}, *args, &block
|
146
|
-
kronk = Kronk.new opts
|
147
|
-
method = args.shift.to_s + '_async'
|
148
|
-
|
149
|
-
kronk.send(method, *args) do |obj, err|
|
150
|
-
raise err if err && !Kronk::Cmd::RESCUABLE.find{|eclass| eclass === err}
|
151
|
-
trigger_result kronk, err, &block
|
137
|
+
[kronk, err]
|
152
138
|
end
|
153
139
|
end
|
154
140
|
|
@@ -157,14 +143,20 @@ class Kronk
|
|
157
143
|
# Trigger a single kronk result callback.
|
158
144
|
|
159
145
|
def trigger_result kronk, err, &block
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
146
|
+
if block_given?
|
147
|
+
if block.arity > 2 || block.arity < 0
|
148
|
+
block.call kronk, err, @mutex
|
149
|
+
else
|
150
|
+
@mutex.synchronize do
|
151
|
+
block.call kronk, err
|
152
|
+
end
|
167
153
|
end
|
154
|
+
|
155
|
+
elsif err && respond_to?(:error)
|
156
|
+
error err, kronk
|
157
|
+
|
158
|
+
elsif respond_to?(:result)
|
159
|
+
result kronk
|
168
160
|
end
|
169
161
|
end
|
170
162
|
|
@@ -13,18 +13,21 @@ class Kronk
|
|
13
13
|
# * Percentage of requests within a certain time
|
14
14
|
# * Slowest endpoints
|
15
15
|
|
16
|
-
class Player::Benchmark < Player
|
16
|
+
class Player::Benchmark < Player
|
17
17
|
|
18
18
|
class ResultSet
|
19
19
|
|
20
|
+
attr_accessor :total_time
|
21
|
+
|
20
22
|
attr_reader :byterate, :count, :fastest, :precision,
|
21
|
-
:slowest, :total_bytes
|
23
|
+
:slowest, :total_bytes, :err_count
|
22
24
|
|
23
|
-
def initialize
|
24
|
-
@times
|
25
|
-
@count
|
26
|
-
@r5XX
|
27
|
-
@r4XX
|
25
|
+
def initialize
|
26
|
+
@times = Hash.new(0)
|
27
|
+
@count = 0
|
28
|
+
@r5XX = 0
|
29
|
+
@r4XX = 0
|
30
|
+
@err_count = 0
|
28
31
|
|
29
32
|
@precision = 3
|
30
33
|
|
@@ -36,7 +39,6 @@ class Kronk
|
|
36
39
|
@total_bytes = 0
|
37
40
|
@byterate = 0
|
38
41
|
|
39
|
-
@start_time = start_time
|
40
42
|
@total_time = 0
|
41
43
|
end
|
42
44
|
|
@@ -58,8 +60,6 @@ class Kronk
|
|
58
60
|
@total_bytes += resp.raw.bytes.count
|
59
61
|
|
60
62
|
@byterate = (@byterate * (@count-1) + resp.byterate) / @count
|
61
|
-
|
62
|
-
@total_time = (Time.now - @start_time).to_f
|
63
63
|
end
|
64
64
|
|
65
65
|
|
@@ -147,9 +147,11 @@ class Kronk
|
|
147
147
|
|
148
148
|
def to_s
|
149
149
|
out = <<-STR
|
150
|
+
|
150
151
|
Completed: #{@count}
|
151
152
|
400s: #{@r4XX}
|
152
153
|
500s: #{@r5XX}
|
154
|
+
Errors: #{@err_count}
|
153
155
|
Req/Sec: #{self.req_per_sec}
|
154
156
|
Total Bytes: #{@total_bytes}
|
155
157
|
Transfer Rate: #{self.transfer_rate} Kbytes/sec
|
@@ -175,7 +177,7 @@ Request Percentages (ms)
|
|
175
177
|
|
176
178
|
unless slowest_reqs.empty?
|
177
179
|
out << "
|
178
|
-
Avg. Slowest Requests (ms,
|
180
|
+
Avg. Slowest Requests (ms, count)
|
179
181
|
#{slowest_reqs.map{|arr| " #{arr[1].inspect} #{arr[0]}"}.join "\n" }"
|
180
182
|
end
|
181
183
|
|
@@ -184,44 +186,39 @@ Avg. Slowest Requests (ms, #)
|
|
184
186
|
end
|
185
187
|
|
186
188
|
|
187
|
-
def initialize player
|
188
|
-
@player = player
|
189
|
-
@results = []
|
190
|
-
@count = 0
|
191
|
-
|
192
|
-
@div = nil
|
193
|
-
@div = @player.number / 10 if @player.number
|
194
|
-
@div = 100 if !@div || @div < 10
|
195
|
-
end
|
196
|
-
|
197
|
-
|
198
189
|
def start
|
190
|
+
@res_count = 0
|
191
|
+
@results = []
|
192
|
+
@div = @number / 10 if @number
|
193
|
+
@div = 100 if !@div || @div < 10
|
199
194
|
puts "Benchmarking..."
|
200
|
-
super
|
201
195
|
end
|
202
196
|
|
203
197
|
|
204
|
-
def result kronk
|
205
|
-
|
206
|
-
|
207
|
-
@
|
208
|
-
@results[i] ||= ResultSet.new(@start_time)
|
198
|
+
def result kronk
|
199
|
+
@mutex.synchronize do
|
200
|
+
kronk.responses.each_with_index do |resp, i|
|
201
|
+
@results[i] ||= ResultSet.new
|
209
202
|
@results[i].add_result resp
|
210
|
-
|
211
|
-
puts "#{@count} requests" if @count % @div == 0
|
212
203
|
end
|
204
|
+
|
205
|
+
@res_count += 1
|
206
|
+
puts "#{@res_count} requests" if @res_count % @div == 0
|
213
207
|
end
|
214
208
|
end
|
215
209
|
|
216
210
|
|
217
|
-
def error err, kronk
|
218
|
-
mutex.synchronize do
|
219
|
-
@
|
211
|
+
def error err, kronk
|
212
|
+
@mutex.synchronize do
|
213
|
+
@res_count += 1
|
214
|
+
@results.each do |res|
|
215
|
+
res.err_count += 1
|
216
|
+
end
|
220
217
|
end
|
221
218
|
end
|
222
219
|
|
223
220
|
|
224
|
-
def
|
221
|
+
def complete
|
225
222
|
puts "Finished!"
|
226
223
|
|
227
224
|
render_head
|
@@ -232,6 +229,8 @@ Avg. Slowest Requests (ms, #)
|
|
232
229
|
|
233
230
|
|
234
231
|
def render_body
|
232
|
+
@results.each{|res| res.total_time = @stop_time - @start_time }
|
233
|
+
|
235
234
|
if @results.length > 1
|
236
235
|
puts Diff.new(@results[0].to_s, @results[1].to_s).formatted
|
237
236
|
else
|
@@ -245,7 +244,7 @@ Avg. Slowest Requests (ms, #)
|
|
245
244
|
|
246
245
|
Benchmark Time: #{(Time.now - @start_time).to_f} sec
|
247
246
|
Number of Requests: #{@count}
|
248
|
-
Concurrency: #{@
|
247
|
+
Concurrency: #{@qps ? "#{@qps} qps" : @concurrency}
|
249
248
|
STR
|
250
249
|
end
|
251
250
|
end
|