stella 0.7.0.015 → 0.7.0.017
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/CHANGES.txt +3 -3
- data/README.rdoc +2 -6
- data/Rudyfile +3 -3
- data/bin/stella +38 -26
- data/examples/cookies/plan.rb +9 -1
- data/examples/csvdata/plan.rb +27 -0
- data/examples/csvdata/search_terms.csv +14 -0
- data/examples/essentials/plan.rb +6 -6
- data/examples/essentials/{search_terms.csv → search_terms.txt} +0 -0
- data/examples/exceptions/plan.rb +9 -2
- data/lib/stella.rb +73 -48
- data/lib/stella/cli.rb +9 -23
- data/lib/stella/client.rb +10 -9
- data/lib/stella/client/container.rb +11 -1
- data/lib/stella/data.rb +114 -61
- data/lib/stella/data/http.rb +1 -1
- data/lib/stella/data/http/request.rb +7 -7
- data/lib/stella/engine.rb +36 -11
- data/lib/stella/engine/functional.rb +9 -8
- data/lib/stella/engine/load.rb +311 -14
- data/lib/stella/exceptions.rb +7 -2
- data/lib/stella/mixins.rb +4 -1
- data/lib/stella/mixins/numeric.rb +11 -10
- data/lib/stella/mixins/string.rb +12 -0
- data/lib/stella/mixins/time.rb +75 -0
- data/lib/stella/testplan.rb +15 -18
- data/lib/stella/testplan/usecase.rb +16 -5
- data/lib/stella/utils.rb +7 -5
- data/lib/stella/version.rb +1 -1
- data/stella.gemspec +11 -7
- data/tryouts/01_numeric_mixins_tryouts.rb +40 -0
- data/tryouts/12_digest_tryouts.rb +42 -0
- metadata +12 -17
- data/lib/stella/engine/stress.rb +0 -293
data/lib/stella/client.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "observer"
|
2
|
-
require "
|
3
|
-
|
2
|
+
require "nokogiri"
|
3
|
+
|
4
|
+
Stella::Utils.require_vendor "httpclient", '2.1.5.2'
|
4
5
|
|
5
6
|
module Stella
|
6
7
|
class Client
|
@@ -21,7 +22,7 @@ module Stella
|
|
21
22
|
end
|
22
23
|
def execute(usecase, &stat_collector)
|
23
24
|
# We need to make sure the gibbler cache has a value
|
24
|
-
self.gibbler if self.
|
25
|
+
self.gibbler if self.digest_cache.nil?
|
25
26
|
|
26
27
|
http_client = create_http_client
|
27
28
|
stats = {}
|
@@ -30,6 +31,8 @@ module Stella
|
|
30
31
|
usecase.requests.each do |req|
|
31
32
|
counter += 1
|
32
33
|
|
34
|
+
container.reset_temp_vars
|
35
|
+
|
33
36
|
stats ||= Benelux::Stats.new
|
34
37
|
update(:prepare_request, usecase, req, counter)
|
35
38
|
uri_obj = URI.parse(req.uri)
|
@@ -37,14 +40,13 @@ module Stella
|
|
37
40
|
headers = prepare_headers(container, req.headers)
|
38
41
|
uri = build_request_uri uri_obj, params, container
|
39
42
|
raise NoHostDefined, uri_obj if uri.host.nil? || uri.host.empty?
|
40
|
-
stella_id = [Time.now, self.
|
43
|
+
stella_id = [Time.now, self.digest_cache, req.digest_cache, params, headers, counter].gibbler
|
41
44
|
|
42
|
-
Benelux.add_thread_tags :request => req.
|
45
|
+
Benelux.add_thread_tags :request => req.digest_cache
|
43
46
|
Benelux.add_thread_tags :retry => counter
|
44
47
|
Benelux.add_thread_tags :stella_id => stella_id
|
45
48
|
|
46
|
-
params['__stella'] = stella_id
|
47
|
-
headers['X-Stella-ID'] = stella_id.short
|
49
|
+
params['__stella'] = headers['X-Stella-ID']= stella_id[0..10]
|
48
50
|
|
49
51
|
meth = req.http_method.to_s.downcase
|
50
52
|
Stella.ld "#{req.http_method}: " << "#{uri_obj.to_s} " << params.inspect
|
@@ -97,7 +99,7 @@ module Stella
|
|
97
99
|
end
|
98
100
|
|
99
101
|
def update(kind, *args)
|
100
|
-
changed and notify_observers(kind, self.
|
102
|
+
changed and notify_observers(kind, self.digest_cache, *args)
|
101
103
|
end
|
102
104
|
|
103
105
|
def run_sleeper(wait)
|
@@ -119,7 +121,6 @@ module Stella
|
|
119
121
|
http_client = HTTPClient.new opts
|
120
122
|
http_client.set_proxy_auth(@proxy.user, @proxy.pass) if @proxy.user
|
121
123
|
http_client.debug_dev = STDOUT if Stella.debug? && Stella.loglev > 3
|
122
|
-
#http_client.set_cookie_store @cookie_file.path
|
123
124
|
http_client.protocol_version = "HTTP/1.1"
|
124
125
|
http_client
|
125
126
|
end
|
@@ -10,6 +10,7 @@ class Stella::Client
|
|
10
10
|
def initialize(usecase)
|
11
11
|
@usecase, @resources = usecase, {}
|
12
12
|
@base_path = usecase.base_path
|
13
|
+
@random_value = {}
|
13
14
|
end
|
14
15
|
|
15
16
|
# This is used to handle custom exception in usecases.
|
@@ -30,12 +31,21 @@ class Stella::Client
|
|
30
31
|
YAML.load(body)
|
31
32
|
end
|
32
33
|
end
|
33
|
-
|
34
|
+
|
35
|
+
# Return a resource from the usecase or from this
|
36
|
+
# container (in that order).
|
34
37
|
def resource(n)
|
35
38
|
return @usecase.resource(n) if @usecase.resources.has_key? n
|
36
39
|
return @resources[n] if @resources.has_key? n
|
40
|
+
nil
|
37
41
|
end
|
38
42
|
|
43
|
+
def reset_temp_vars
|
44
|
+
@random_value = {}
|
45
|
+
@sequential_value = {}
|
46
|
+
@rsequential_value = {}
|
47
|
+
end
|
48
|
+
|
39
49
|
def body; @response.body.content; end
|
40
50
|
def headers; @response.header; end
|
41
51
|
alias_method :header, :headers
|
data/lib/stella/data.rb
CHANGED
@@ -24,84 +24,137 @@ module Stella::Data
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def random(*args)
|
27
|
-
|
27
|
+
if Symbol === args.first
|
28
|
+
input, index = *args
|
29
|
+
elsif Array === args.first || args.size == 1
|
30
|
+
input = args.first
|
31
|
+
else
|
32
|
+
input = args
|
33
|
+
end
|
34
|
+
|
28
35
|
Proc.new do
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
36
|
+
if @random_value[input]
|
37
|
+
value = @random_value[input]
|
38
|
+
else
|
39
|
+
value = case input.class.to_s
|
40
|
+
when "Symbol"
|
41
|
+
resource(input)
|
42
|
+
when "Array"
|
43
|
+
input
|
44
|
+
when "Range"
|
45
|
+
input.to_a
|
46
|
+
when "Proc"
|
47
|
+
input.call
|
48
|
+
when "Fixnum"
|
49
|
+
Stella::Utils.strand( input )
|
50
|
+
when "NilClass"
|
51
|
+
Stella::Utils.strand( rand(100) )
|
52
|
+
end
|
53
|
+
raise Stella::Testplan::Usecase::UnknownResource, input if value.nil?
|
54
|
+
Stella.ld "RANDVALUES: #{input} #{value.class} #{value.inspect}"
|
55
|
+
value = value[ rand(value.size) ] if value.is_a?(Array)
|
56
|
+
Stella.ld "SELECTED: #{value.class} #{value} "
|
57
|
+
@random_value[input] = value
|
42
58
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
59
|
+
|
60
|
+
# The resource may be an Array of Arrays (e.g. a CSV file)
|
61
|
+
if value.is_a?(Array) && !index.nil?
|
62
|
+
value = value[ index ]
|
63
|
+
Stella.ld "SELECTED INDEX: #{index} #{value.inspect} "
|
64
|
+
end
|
65
|
+
|
47
66
|
value
|
48
67
|
end
|
49
68
|
end
|
50
69
|
|
51
70
|
def sequential(*args)
|
52
|
-
|
71
|
+
if Symbol === args.first
|
72
|
+
input, index = *args
|
73
|
+
elsif Array === args.first || args.size == 1
|
74
|
+
input = args.first
|
75
|
+
else
|
76
|
+
input = args
|
77
|
+
end
|
53
78
|
Proc.new do
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
79
|
+
if @sequential_value[input]
|
80
|
+
value = @sequential_value[input]
|
81
|
+
else
|
82
|
+
value = case input.class.to_s
|
83
|
+
when "Symbol"
|
84
|
+
ret = resource(input)
|
85
|
+
ret
|
86
|
+
when "Array"
|
87
|
+
input
|
88
|
+
when "Range"
|
89
|
+
input.to_a
|
90
|
+
when "Proc"
|
91
|
+
input.call
|
92
|
+
end
|
93
|
+
digest = value.gibbler
|
94
|
+
@sequential_offset ||= {}
|
95
|
+
@sequential_offset[digest] ||= 0
|
96
|
+
Stella.ld "SEQVALUES: #{input} #{value.inspect} #{@sequential_offset[digest]}"
|
97
|
+
if value.is_a?(Array)
|
98
|
+
size = value.size
|
99
|
+
@sequential_offset[digest] = 0 if @sequential_offset[digest] >= size
|
100
|
+
value = value[ @sequential_offset[digest] ]
|
101
|
+
@sequential_offset[digest] += 1
|
102
|
+
end
|
103
|
+
Stella.ld "SELECTED: #{value}"
|
104
|
+
@sequential_value[input] = value
|
64
105
|
end
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
if value.is_a?(Array)
|
70
|
-
size = value.size
|
71
|
-
@sequential_offset[digest] = 0 if @sequential_offset[digest] >= size
|
72
|
-
value = value[ @sequential_offset[digest] ]
|
73
|
-
@sequential_offset[digest] += 1
|
106
|
+
# The resource may be an Array of Arrays (e.g. a CSV file)
|
107
|
+
if value.is_a?(Array) && !index.nil?
|
108
|
+
value = value[ index ]
|
109
|
+
Stella.ld "SELECTED INDEX: #{index} #{value.inspect} "
|
74
110
|
end
|
75
|
-
Stella.ld "SELECTED: #{value}"
|
76
111
|
value
|
77
112
|
end
|
78
113
|
end
|
79
114
|
|
80
115
|
def rsequential(*args)
|
81
|
-
|
116
|
+
if Symbol === args.first
|
117
|
+
input, index = *args
|
118
|
+
elsif Array === args.first || args.size == 1
|
119
|
+
input = args.first
|
120
|
+
else
|
121
|
+
input = args
|
122
|
+
end
|
82
123
|
Proc.new do
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
124
|
+
if @rsequential_value[input.digest]
|
125
|
+
value = @rsequential_value[input.digest]
|
126
|
+
else
|
127
|
+
value = case input.class.to_s
|
128
|
+
when "Symbol"
|
129
|
+
ret = resource(input)
|
130
|
+
ret
|
131
|
+
when "Array"
|
132
|
+
input
|
133
|
+
when "Range"
|
134
|
+
input.to_a
|
135
|
+
when "Proc"
|
136
|
+
input.call
|
137
|
+
end
|
138
|
+
digest = value.gibbler
|
139
|
+
@rsequential_offset ||= {}
|
140
|
+
@rsequential_offset[digest] ||= value.size-1 rescue 1
|
141
|
+
Stella.ld "RSEQVALUES: #{input} #{value.inspect} #{@rsequential_offset[digest]}"
|
142
|
+
if value.is_a?(Array)
|
143
|
+
size = value.size
|
144
|
+
@rsequential_offset[digest] = size-1 if @rsequential_offset[digest] < 0
|
145
|
+
value = value[ @rsequential_offset[digest] ]
|
146
|
+
@rsequential_offset[digest] -= 1
|
147
|
+
end
|
148
|
+
Stella.ld "SELECTED: #{value}"
|
149
|
+
@rsequential_value[input.digest] = value
|
93
150
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
size = value.size
|
100
|
-
@rsequential_offset[digest] = size-1 if @rsequential_offset[digest] < 0
|
101
|
-
value = value[ @rsequential_offset[digest] ]
|
102
|
-
@rsequential_offset[digest] -= 1
|
151
|
+
|
152
|
+
# The resource may be an Array of Arrays (e.g. a CSV file)
|
153
|
+
if value.is_a?(Array) && !index.nil?
|
154
|
+
value = value[ index ]
|
155
|
+
Stella.ld "SELECTED INDEX: #{index} #{value.inspect} "
|
103
156
|
end
|
104
|
-
|
157
|
+
|
105
158
|
value
|
106
159
|
end
|
107
160
|
end
|
@@ -110,4 +163,4 @@ module Stella::Data
|
|
110
163
|
|
111
164
|
end
|
112
165
|
|
113
|
-
Stella::Utils.require_glob(
|
166
|
+
Stella::Utils.require_glob(STELLA_LIB_HOME, 'stella', 'data', '*.rb')
|
data/lib/stella/data/http.rb
CHANGED
@@ -5,13 +5,13 @@ module Stella::Data::HTTP
|
|
5
5
|
include Gibbler::Complex
|
6
6
|
include Stella::Data::Helpers
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
# A hash containing blocks to be executed depending on the HTTP response status.
|
9
|
+
# The hash keys are numeric HTTP Status Codes.
|
10
|
+
#
|
11
|
+
# 200 => { ... }
|
12
|
+
# 304 => { ... }
|
13
|
+
# 500 => { ... }
|
14
|
+
#
|
15
15
|
attr_accessor :response_handler
|
16
16
|
|
17
17
|
field :desc
|
data/lib/stella/engine.rb
CHANGED
@@ -5,6 +5,8 @@ module Stella::Engine
|
|
5
5
|
module Base
|
6
6
|
extend self
|
7
7
|
|
8
|
+
@@client_limit = 200
|
9
|
+
|
8
10
|
def update(*args)
|
9
11
|
what, *args = args
|
10
12
|
if respond_to?("update_#{what}")
|
@@ -15,18 +17,41 @@ module Stella::Engine
|
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
20
|
+
def process_options!(plan, opts={})
|
21
|
+
opts = {
|
22
|
+
:hosts => [],
|
23
|
+
:clients => 1,
|
24
|
+
:duration => 0,
|
25
|
+
:nowait => false,
|
26
|
+
:arrival => nil,
|
27
|
+
:repetitions => 1
|
28
|
+
}.merge! opts
|
29
|
+
|
30
|
+
Stella.li3 " Options: #{opts.inspect}"
|
31
|
+
Stella.lflush
|
32
|
+
|
33
|
+
opts[:clients] = plan.usecases.size if opts[:clients] < plan.usecases.size
|
34
|
+
|
35
|
+
if opts[:clients] > @@client_limit
|
36
|
+
Stella.li3 "Client limit is #{@@client_limit}"
|
37
|
+
opts[:clients] = @@client_limit
|
38
|
+
end
|
39
|
+
|
40
|
+
# Parses 60m -> 3600. See mixins.
|
41
|
+
if opts[:duration].in_seconds.nil?
|
42
|
+
raise Stella::WackyDuration, opts[:duration]
|
43
|
+
end
|
44
|
+
|
45
|
+
opts[:duration] = opts[:duration].in_seconds
|
46
|
+
|
47
|
+
Stella.li3 " Hosts: " << opts[:hosts].join(', ')
|
48
|
+
opts
|
28
49
|
end
|
29
50
|
|
51
|
+
def run; raise; end
|
52
|
+
def update_quit_usecase(client_id, msg) raise end
|
53
|
+
def update_repeat_request(client_id, counter, total) raise end
|
54
|
+
def update_stats(client_id, http_client, usecase, req) raise end
|
30
55
|
def update_prepare_request(*args) raise end
|
31
56
|
def update_send_request(*args) raise end
|
32
57
|
def update_receive_response(*args) raise end
|
@@ -36,7 +61,7 @@ module Stella::Engine
|
|
36
61
|
|
37
62
|
end
|
38
63
|
|
39
|
-
Stella::Utils.require_glob(
|
64
|
+
Stella::Utils.require_glob(STELLA_LIB_HOME, 'stella', 'engine', '*.rb')
|
40
65
|
|
41
66
|
|
42
67
|
# These timers are interesting from a reporting perspective.
|
@@ -5,11 +5,8 @@ module Stella::Engine
|
|
5
5
|
extend self
|
6
6
|
|
7
7
|
def run(plan, opts={})
|
8
|
-
opts =
|
9
|
-
|
10
|
-
:nowait => false,
|
11
|
-
:repetitions => 1
|
12
|
-
}.merge! opts
|
8
|
+
opts = process_options! plan, opts
|
9
|
+
|
13
10
|
Stella.ld "OPTIONS: #{opts.inspect}"
|
14
11
|
Stella.li2 "Hosts: " << opts[:hosts].join(', ') if !opts[:hosts].empty?
|
15
12
|
|
@@ -18,13 +15,16 @@ module Stella::Engine
|
|
18
15
|
|
19
16
|
client.enable_nowait_mode if opts[:nowait]
|
20
17
|
|
21
|
-
Stella.
|
18
|
+
Stella.li2 $/, "Starting test...", $/
|
22
19
|
Stella.lflush
|
23
20
|
sleep 0.3
|
24
21
|
|
22
|
+
dig = Stella.loglev > 1 ? plan.digest_cache : plan.digest_cache.shorter
|
23
|
+
Stella.li " %-65s ".att(:reverse) % ["#{plan.desc} (#{dig})"]
|
25
24
|
plan.usecases.each_with_index do |uc,i|
|
26
25
|
desc = (uc.desc || "Usecase ##{i+1}")
|
27
|
-
Stella.
|
26
|
+
dig = Stella.loglev > 1 ? uc.digest_cache : uc.digest_cache.shorter
|
27
|
+
Stella.li ' %-65s '.att(:reverse).bright % ["#{desc} (#{dig}) "]
|
28
28
|
Stella.rescue { client.execute uc }
|
29
29
|
end
|
30
30
|
|
@@ -38,7 +38,8 @@ module Stella::Engine
|
|
38
38
|
|
39
39
|
def update_prepare_request(client_id, usecase, req, counter)
|
40
40
|
notice = "repeat: #{counter-1}" if counter > 1
|
41
|
-
|
41
|
+
dig = Stella.loglev > 1 ? req.digest_cache : req.digest_cache.shorter
|
42
|
+
desc = "#{req.desc} (#{dig}) "
|
42
43
|
Stella.li2 " %-46s %16s ".bright % [desc, notice]
|
43
44
|
end
|
44
45
|
|
data/lib/stella/engine/load.rb
CHANGED
@@ -12,23 +12,59 @@ module Stella::Engine
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def run(plan, opts={})
|
15
|
-
opts =
|
16
|
-
:hosts => [],
|
17
|
-
:clients => 1,
|
18
|
-
:duration => nil,
|
19
|
-
:nowait => false,
|
20
|
-
:repetitions => 1
|
21
|
-
}.merge! opts
|
22
|
-
opts[:clients] = plan.usecases.size if opts[:clients] < plan.usecases.size
|
23
|
-
opts[:clients] = 1000 if opts[:clients] > 1000
|
15
|
+
opts = process_options! plan, opts
|
24
16
|
|
25
|
-
Stella.
|
26
|
-
|
17
|
+
if Stella.loglev > 1
|
18
|
+
Load.timers += [:connect, :create_socket, :query, :socket_gets_first_byte, :get_body]
|
19
|
+
Load.counts = [:request_header_size, :request_content_size]
|
20
|
+
Load.counts += [:response_headers_size, :response_content_size]
|
21
|
+
end
|
27
22
|
|
28
|
-
|
23
|
+
counts = calculate_usecase_clients plan, opts
|
29
24
|
|
30
|
-
#
|
25
|
+
Stella.li $/, "Preparing #{counts[:total]} virtual clients...", $/
|
26
|
+
Stella.lflush
|
27
|
+
packages = build_thread_package plan, opts, counts
|
28
|
+
|
29
|
+
if opts[:duration] > 0
|
30
|
+
msg = "for #{opts[:duration].seconds}s"
|
31
|
+
else
|
32
|
+
msg = "for #{opts[:repetitions]} reps"
|
33
|
+
end
|
34
|
+
|
35
|
+
Stella.li "Generating requests #{msg}...", $/
|
36
|
+
Stella.lflush
|
31
37
|
|
38
|
+
@mode = :rolling
|
39
|
+
|
40
|
+
begin
|
41
|
+
execute_test_plan packages, opts[:repetitions], opts[:duration]
|
42
|
+
rescue Interrupt
|
43
|
+
Stella.li "Stopping test...", $/
|
44
|
+
Stella.abort!
|
45
|
+
ensure
|
46
|
+
Stella.li "Processing statistics...", $/
|
47
|
+
Stella.lflush
|
48
|
+
|
49
|
+
wait_for_reporter
|
50
|
+
|
51
|
+
tt = Benelux.thread_timeline
|
52
|
+
test_time = tt.stats.group(:execute_test_plan).mean
|
53
|
+
|
54
|
+
generate_report plan, test_time
|
55
|
+
|
56
|
+
#p Benelux.timeline
|
57
|
+
|
58
|
+
Stella.li "Summary: "
|
59
|
+
Stella.li " max clients: %d" % @max_clients
|
60
|
+
Stella.li " repetitions: %d" % @real_reps
|
61
|
+
Stella.li " test time: %10.2fs" % test_time
|
62
|
+
Stella.li " wait time: %10.2fs" % tt.stats.group(:wait_for_reporter).mean
|
63
|
+
Stella.li " post time: %10.2fs" % tt.stats.group(:generate_report).mean
|
64
|
+
Stella.li $/
|
65
|
+
end
|
66
|
+
|
67
|
+
# errors?
|
32
68
|
end
|
33
69
|
|
34
70
|
def wait_for_reporter
|
@@ -36,8 +72,269 @@ module Stella::Engine
|
|
36
72
|
end
|
37
73
|
|
38
74
|
protected
|
75
|
+
class ThreadPackage
|
76
|
+
attr_accessor :index
|
77
|
+
attr_accessor :client
|
78
|
+
attr_accessor :usecase
|
79
|
+
def initialize(i, c, u)
|
80
|
+
@index, @client, @usecase = i, c, u
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def calculate_usecase_clients(plan, opts)
|
85
|
+
counts = { :total => 0 }
|
86
|
+
plan.usecases.each_with_index do |usecase,i|
|
87
|
+
count = case opts[:clients]
|
88
|
+
when 0..9
|
89
|
+
if (opts[:clients] % plan.usecases.size > 0)
|
90
|
+
msg = "Client count does not evenly match usecase count"
|
91
|
+
raise Stella::WackyRatio, msg
|
92
|
+
else
|
93
|
+
(opts[:clients] / plan.usecases.size)
|
94
|
+
end
|
95
|
+
else
|
96
|
+
(opts[:clients] * usecase.ratio).to_i
|
97
|
+
end
|
98
|
+
counts[usecase.digest_cache] = count
|
99
|
+
counts[:total] += count
|
100
|
+
end
|
101
|
+
counts
|
102
|
+
end
|
103
|
+
|
104
|
+
def build_thread_package(plan, opts, counts)
|
105
|
+
packages, pointer = Array.new(counts[:total]), 0
|
106
|
+
plan.usecases.each do |usecase|
|
107
|
+
count = counts[usecase.digest_cache]
|
108
|
+
Stella.ld "THREAD PACKAGE: #{usecase.desc} (#{pointer} + #{count})"
|
109
|
+
# Fill the thread_package with the contents of the block
|
110
|
+
packages.fill(pointer, count) do |index|
|
111
|
+
client = Stella::Client.new opts[:hosts].first, index+1
|
112
|
+
client.add_observer(self)
|
113
|
+
client.enable_nowait_mode if opts[:nowait]
|
114
|
+
Stella.li4 "Created client #{client.digest.short}"
|
115
|
+
ThreadPackage.new(index+1, client, usecase)
|
116
|
+
end
|
117
|
+
pointer += count
|
118
|
+
end
|
119
|
+
packages.compact # TODO: Why one nil element sometimes?
|
120
|
+
end
|
121
|
+
|
122
|
+
def execute_test_plan(packages, reps=1, duration=0)
|
123
|
+
time_started = Time.now
|
124
|
+
|
125
|
+
@threads, @max_clients, @real_reps = [], 0, 0
|
126
|
+
(1..reps).to_a.each { |rep|
|
127
|
+
@real_reps += 1 # Increments when duration is specified.
|
128
|
+
Stella.li3 "*** REPETITION #{@real_reps} of #{reps} ***"
|
129
|
+
packages.each { |package|
|
130
|
+
if running_threads.size <= packages.size
|
131
|
+
@threads << Thread.new do
|
132
|
+
c, uc = package.client, package.usecase
|
133
|
+
msg = "THREAD START: client %s: " % [c.digest.short]
|
134
|
+
msg << "%s:%s (rep: %d)" % [uc.desc, uc.digest.short, @real_reps]
|
135
|
+
Stella.li4 $/, "======== " << msg
|
136
|
+
# This thread will stay on this one track.
|
137
|
+
Benelux.current_track c.digest
|
138
|
+
Benelux.add_thread_tags :usecase => uc.digest_cache
|
139
|
+
|
140
|
+
Benelux.add_thread_tags :rep => @real_reps
|
141
|
+
Stella::Engine::Load.rescue(c.digest_cache) {
|
142
|
+
break if Stella.abort?
|
143
|
+
print '.' if Stella.loglev == 2
|
144
|
+
stats = c.execute uc
|
145
|
+
}
|
146
|
+
Benelux.remove_thread_tags :rep
|
147
|
+
Benelux.remove_thread_tags :usecase
|
148
|
+
|
149
|
+
end
|
150
|
+
Stella.sleep :create_thread
|
151
|
+
end
|
152
|
+
|
153
|
+
if running_threads.size > @max_clients
|
154
|
+
@max_clients = running_threads.size
|
155
|
+
end
|
156
|
+
|
157
|
+
if @mode == :rolling
|
158
|
+
tries = 0
|
159
|
+
while (reps > 1 || duration > 0) && running_threads.size >= packages.size
|
160
|
+
Stella.sleep :check_threads
|
161
|
+
msg = "#{running_threads.size} (max: #{@max_clients})"
|
162
|
+
Stella.li3 "*** RUNNING THREADS: #{msg} ***"
|
163
|
+
(tries += 1)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
}
|
167
|
+
|
168
|
+
if @mode != :rolling && running_threads.size > 0
|
169
|
+
args = [running_threads.size, @max_clients]
|
170
|
+
Stella.li3 "*** WAITING FOR %d THREADS TO FINISH (max: %d) ***" % args
|
171
|
+
@threads.each { |t| t.join } # wait
|
172
|
+
end
|
173
|
+
|
174
|
+
# If a duration was given, we make sure
|
175
|
+
# to run for only that amount of time.
|
176
|
+
# TODO: do not redo if
|
177
|
+
# time_elapsed + usecase.mean > duration
|
178
|
+
if duration > 0
|
179
|
+
time_elapsed = (Time.now - time_started).to_i
|
180
|
+
msg = "#{time_elapsed} of #{duration} (threads: %d)" % running_threads.size
|
181
|
+
Stella.li3 "*** TIME ELAPSED: #{msg} ***"
|
182
|
+
sleep 0.5 and redo if time_elapsed <= duration
|
183
|
+
break if time_elapsed >= duration
|
184
|
+
end
|
185
|
+
|
186
|
+
}
|
187
|
+
|
188
|
+
if @mode == :rolling && running_threads.size > 0
|
189
|
+
Stella.li3 "*** WAITING FOR THREADS TO FINISH ***"
|
190
|
+
@threads.each { |t| t.join } # wait
|
191
|
+
end
|
192
|
+
Stella.li2 $/, $/
|
193
|
+
end
|
194
|
+
def running_threads
|
195
|
+
@threads.select { |t| t.status } # non-false status are still running
|
196
|
+
end
|
197
|
+
def generate_report(plan,test_time)
|
198
|
+
#Benelux.update_all_track_timelines
|
199
|
+
global_timeline = Benelux.timeline
|
200
|
+
|
201
|
+
Stella.li $/, " %-72s ".att(:reverse) % ["#{plan.desc} (#{plan.digest_cache.shorter})"]
|
202
|
+
plan.usecases.uniq.each_with_index do |uc,i|
|
203
|
+
|
204
|
+
# TODO: Create Ranges object, like Stats object
|
205
|
+
# global_timeline.ranges(:do_request)[:usecase => '1111']
|
206
|
+
# The following returns globl do_request ranges.
|
207
|
+
requests = 0 #global_timeline.ranges(:do_request).size
|
208
|
+
|
209
|
+
desc = uc.desc || "Usecase ##{i+1} "
|
210
|
+
desc << " (#{uc.digest_cache.shorter}) "
|
211
|
+
str = ' ' << " %-66s %s %d%% ".bright.att(:reverse)
|
212
|
+
Stella.li str % [desc, '', uc.ratio_pretty]
|
213
|
+
|
214
|
+
uc.requests.each do |req|
|
215
|
+
filter = [uc.digest_cache, req.digest_cache]
|
216
|
+
desc = req.desc
|
217
|
+
Stella.li " %-72s ".bright % ["#{req.desc} (#{req.digest_cache.shorter})"]
|
218
|
+
Stella.li " %s" % [req.to_s]
|
219
|
+
Load.timers.each do |sname|
|
220
|
+
stats = global_timeline.stats.group(sname)[filter].merge
|
221
|
+
# Stella.li stats.inspect
|
222
|
+
str = ' %-30s %.3f <= ' << '%.3fs' << ' >= %.3f; %.3f(SD) %d(N)'
|
223
|
+
Stella.li str % [sname, stats.min, stats.mean, stats.max, stats.sd, stats.n]
|
224
|
+
Stella.lflush
|
225
|
+
end
|
226
|
+
Stella.li $/
|
227
|
+
end
|
228
|
+
|
229
|
+
Stella.li " Sub Total:".bright
|
230
|
+
|
231
|
+
stats = global_timeline.stats.group(:do_request)[uc.digest_cache].merge
|
232
|
+
failed = global_timeline.stats.group(:failed)[uc.digest_cache].merge
|
233
|
+
respgrp = global_timeline.stats.group(:execute_response_handler)[uc.digest_cache]
|
234
|
+
resst = respgrp.tag_values(:status)
|
235
|
+
statusi = []
|
236
|
+
resst.each do |status|
|
237
|
+
size = respgrp[:status => status].size
|
238
|
+
statusi << "#{status}: #{size}"
|
239
|
+
end
|
240
|
+
Stella.li ' %-30s %d (%s)' % ['Total requests', stats.n, statusi.join(', ')]
|
241
|
+
Stella.li ' %-29s %d' % [:success, stats.n - failed.n]
|
242
|
+
Stella.li ' %-29s %d' % [:failed, failed.n]
|
243
|
+
|
244
|
+
Load.timers.each do |sname|
|
245
|
+
stats = global_timeline.stats.group(sname)[uc.digest_cache].merge
|
246
|
+
Stella.li ' %-30s %.3fs %.3f(SD)' % [sname, stats.mean, stats.sd]
|
247
|
+
Stella.lflush
|
248
|
+
end
|
249
|
+
|
250
|
+
Load.counts.each do |sname|
|
251
|
+
stats = global_timeline.stats.group(sname)[uc.digest_cache].merge
|
252
|
+
Stella.li ' %-30s %-12s (avg:%s)' % [sname, stats.sum.to_bytes, stats.mean.to_bytes]
|
253
|
+
Stella.lflush
|
254
|
+
end
|
255
|
+
Stella.li $/
|
256
|
+
end
|
257
|
+
|
258
|
+
Stella.li ' ' << " %-66s ".att(:reverse) % 'Total:'
|
259
|
+
stats = global_timeline.stats.group(:do_request).merge
|
260
|
+
failed = global_timeline.stats.group(:failed)
|
261
|
+
respgrp = global_timeline.stats.group(:execute_response_handler)
|
262
|
+
resst = respgrp.tag_values(:status)
|
263
|
+
statusi = []
|
264
|
+
resst.each do |status|
|
265
|
+
size = respgrp[:status => status].size
|
266
|
+
statusi << [status, size]
|
267
|
+
end
|
268
|
+
Stella.li ' %-30s %d' % ['Total requests', stats.n]
|
269
|
+
success = stats.n - failed.n
|
270
|
+
Stella.li ' %-29s %d (req/s: %.2f)' % [:success, success, success/test_time]
|
271
|
+
statusi.each do |pair|
|
272
|
+
Stella.li2 ' %-28s %s: %d' % ['', *pair]
|
273
|
+
end
|
274
|
+
Stella.li ' %-29s %d' % [:failed, failed.n]
|
275
|
+
|
276
|
+
Load.timers.each do |sname|
|
277
|
+
stats = global_timeline.stats.group(sname).merge
|
278
|
+
Stella.li ' %-30s %-.3fs %-.3f(SD)' % [sname, stats.mean, stats.sd]
|
279
|
+
Stella.lflush
|
280
|
+
end
|
281
|
+
|
282
|
+
Load.counts.each do |sname|
|
283
|
+
stats = global_timeline.stats.group(sname).merge
|
284
|
+
Stella.li ' %-30s %-12s (avg:%s)' % [sname, stats.sum.to_bytes, stats.mean.to_bytes]
|
285
|
+
Stella.lflush
|
286
|
+
end
|
287
|
+
Stella.li $/
|
288
|
+
end
|
289
|
+
|
290
|
+
|
291
|
+
def update_prepare_request(client_id, usecase, req, counter)
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
def update_receive_response(client_id, usecase, uri, req, counter, container)
|
296
|
+
desc = "#{usecase.desc} > #{req.desc}"
|
297
|
+
Stella.li3 ' Client-%s %3d %-6s %-45s' % [client_id.shorter, container.status, req.http_method, uri]
|
298
|
+
end
|
299
|
+
|
300
|
+
def update_execute_response_handler(client_id, req, container)
|
301
|
+
end
|
302
|
+
|
303
|
+
def update_error_execute_response_handler(client_id, ex, req, container)
|
304
|
+
desc = "#{container.usecase.desc} > #{req.desc}"
|
305
|
+
Stella.li $/ if Stella.loglev == 1
|
306
|
+
Stella.le ' Client-%s %-45s %s' % [client_id.shorter, desc, ex.message]
|
307
|
+
Stella.li3 ex.backtrace
|
308
|
+
end
|
309
|
+
|
310
|
+
def update_request_error(client_id, usecase, uri, req, params, ex)
|
311
|
+
desc = "#{usecase.desc} > #{req.desc}"
|
312
|
+
Stella.li $/ if Stella.loglev == 1
|
313
|
+
Stella.le ' Client-%s %-45s %s' % [client_id.shorter, desc, ex.message]
|
314
|
+
Stella.li3 ex.backtrace
|
315
|
+
end
|
316
|
+
|
317
|
+
def update_quit_usecase client_id, msg
|
318
|
+
Stella.li3 " Client-%s QUIT %s" % [client_id.shorter, msg]
|
319
|
+
end
|
320
|
+
|
321
|
+
|
322
|
+
def update_repeat_request client_id, counter, total
|
323
|
+
Stella.li3 " Client-%s REPEAT %d of %d" % [client_id.shorter, counter, total]
|
324
|
+
end
|
325
|
+
|
326
|
+
def self.rescue(client_id, &blk)
|
327
|
+
blk.call
|
328
|
+
rescue => ex
|
329
|
+
Stella.le ' Error in Client-%s: %s' % [client_id.shorter, ex.message]
|
330
|
+
Stella.li3 ex.backtrace
|
331
|
+
end
|
332
|
+
|
39
333
|
|
40
|
-
|
334
|
+
Benelux.add_timer Stella::Engine::Load, :build_thread_package
|
335
|
+
Benelux.add_timer Stella::Engine::Load, :execute_test_plan
|
336
|
+
Benelux.add_timer Stella::Engine::Load, :generate_report
|
337
|
+
Benelux.add_timer Stella::Engine::Load, :wait_for_reporter
|
41
338
|
|
42
339
|
end
|
43
340
|
end
|