stella 0.8.2.002 → 0.8.2.003

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.txt CHANGED
@@ -24,6 +24,8 @@ STELLA, CHANGES
24
24
  * CHANGE: Removed no-logging option
25
25
  * CHANGE: Renamed Testrun#summary to Testrun#stats
26
26
  * CHANGE: Pass entire URI when calling Testplan#new
27
+ * CHANGE: Engine objects are now classes
28
+ * CHANGE: Engine#run methods now return instance of Stella::Testrun
27
29
  * ADDED: JSON statistics log (.stella/logs/latest/stats)
28
30
 
29
31
 
data/VERSION.yml CHANGED
@@ -2,4 +2,4 @@
2
2
  :MAJOR: 0
3
3
  :MINOR: 8
4
4
  :PATCH: 2
5
- :BUILD: '002'
5
+ :BUILD: '003'
data/lib/stella/cli.rb CHANGED
@@ -26,8 +26,9 @@ class Stella::CLI < Drydock::Command
26
26
  opts[opt] = @global.send(opt) unless @global.send(opt).nil?
27
27
  end
28
28
 
29
- ret = Stella::Engine::Functional.run @testplan, opts
30
- @exit_code = (ret ? 0 : 1)
29
+ engine = Stella::Engine::Functional.new opts
30
+ testrun = engine.run @testplan
31
+ @exit_code = (testrun.stats[:summary][:failed].n == 0 ? 0 : 1)
31
32
  end
32
33
 
33
34
  def generate_valid?
@@ -47,11 +48,9 @@ class Stella::CLI < Drydock::Command
47
48
 
48
49
  connect_service if @global.remote
49
50
 
50
- ret = Stella::Engine::Load.run @testplan, opts
51
-
52
- Stella.ld "ENGINE: #{@global.engine}: #{ret.class}"
53
-
54
- @exit_code = (ret ? 0 : 1)
51
+ engine = Stella::Engine::Load.new opts
52
+ testrun = engine.run @testplan
53
+ @exit_code = (testrun.stats[:summary][:failed].n == 0 ? 0 : 1)
55
54
  end
56
55
 
57
56
  def example
@@ -39,7 +39,7 @@ module Stella::Data::HTTP
39
39
  end
40
40
 
41
41
  def initialize (method, uri_str, version="1.1", &definition)
42
- @uri = uri_str
42
+ @uri = uri_str.to_s
43
43
  @http_method, @http_version = method, version
44
44
  @headers, @params, @response_handler = {}, {}, {}
45
45
  @resources = {}
data/lib/stella/engine.rb CHANGED
@@ -21,10 +21,18 @@ module Stella::Engine
21
21
  field :response_headers
22
22
  field :response_body
23
23
  end
24
- module Base
25
- extend self
24
+ class Base
26
25
 
27
- @testrun = nil
26
+ class << self
27
+ attr_accessor :timers, :counts
28
+ end
29
+
30
+ attr_reader :testrun, :logdir, :opts
31
+
32
+ def initialize(opts={})
33
+ @opts = opts
34
+ @logdir = nil
35
+ end
28
36
 
29
37
  @@client_limit = 1000
30
38
 
@@ -84,6 +92,10 @@ module Stella::Engine
84
92
  end
85
93
  end
86
94
 
95
+ opts[:clients] &&= opts[:clients].to_i
96
+ opts[:duration] &&= opts[:duration].to_i
97
+ opts[:arrival] &&= opts[:arrival].to_f
98
+ opts[:repetitions] &&= opts[:repetitions].to_i
87
99
  opts[:clients] = plan.usecases.size if opts[:clients] < plan.usecases.size
88
100
 
89
101
  if opts[:clients] > @@client_limit
@@ -1,11 +1,9 @@
1
1
 
2
2
  module Stella::Engine
3
- module Functional
4
- extend Stella::Engine::Base
5
- extend self
3
+ class Functional < Stella::Engine::Base
6
4
 
7
- def run(plan, opts={})
8
- opts = process_options! plan, opts
5
+ def run(plan)
6
+ opts = process_options! plan, @opts
9
7
 
10
8
  Stella.stdout.info2 "Hosts: " << opts[:hosts].join(', ') if !opts[:hosts].empty?
11
9
 
@@ -59,7 +57,7 @@ module Stella::Engine
59
57
  @testrun.add_sample 1, 1, tt
60
58
  end
61
59
 
62
- @testrun.stats[:summary][:failed].n == 0
60
+ @testrun
63
61
  end
64
62
 
65
63
 
@@ -1,9 +1,100 @@
1
- require 'stella/engine/loadbase'
2
1
 
3
2
  module Stella::Engine
4
- module Load
5
- extend Stella::Engine::Load
6
- extend self
3
+ class Load < Stella::Engine::Base
4
+
5
+ @timers = [:response_time]
6
+ @counts = [:response_content_size]
7
+
8
+ def run(plan)
9
+ opts = process_options! plan, @opts
10
+ @threads, @max_clients, @real_reps = [], 0, 0
11
+
12
+ @logdir = log_dir(plan)
13
+ latest = File.join(File.dirname(@logdir), 'latest')
14
+ Stella.stdout.info "Logging to #{@logdir}", $/
15
+
16
+ if Stella.sysinfo.os == :unix
17
+ File.unlink latest if File.exists? latest
18
+ FileUtils.ln_sf File.basename(@logdir), latest
19
+ end
20
+
21
+ @statlog_path = log_path(plan, 'stats')
22
+ @sumlog = Stella::Logger.new log_path(plan, 'summary')
23
+ @failog = Stella::Logger.new log_path(plan, 'exceptions')
24
+
25
+ Stella.stdout.add_template :head, ' %s: %s'
26
+ Stella.stdout.add_template :status, "#{$/}%s..."
27
+
28
+ if Stella.stdout.lev >= 2
29
+ Load.timers += [:socket_connect, :send_request, :first_byte, :receive_response]
30
+ Load.counts = [:request_header_size, :request_content_size]
31
+ Load.counts += [:response_headers_size, :response_content_size]
32
+ end
33
+
34
+ events = [Load.timers, Load.counts, :failed].flatten
35
+ @testrun = Stella::Testrun.new plan, events, opts
36
+ @testrun.mode = 'l'
37
+
38
+ if Stella::Engine.service
39
+ Stella::Engine.service.testplan_sync plan
40
+ Stella::Engine.service.testrun_create @testrun
41
+ Stella.stdout.head 'Testrun', @testrun.remote_digest
42
+ end
43
+
44
+ @testrun.save(@statlog_path)
45
+
46
+ @dumper = prepare_dumper(plan, opts)
47
+
48
+ counts = calculate_usecase_clients plan, opts
49
+
50
+ packages = build_thread_package plan, opts, counts
51
+
52
+ if opts[:duration] > 0
53
+ timing = "#{opts[:duration].seconds.to_i} seconds"
54
+ else
55
+ timing = "#{opts[:repetitions]} repetitions"
56
+ end
57
+
58
+ Stella.stdout.head 'Plan', "#{plan.desc} (#{plan.digest.shorter})"
59
+ Stella.stdout.head 'Hosts', opts[:hosts].join(', ')
60
+ Stella.stdout.head 'Clients', counts[:total]
61
+ Stella.stdout.head 'Limit', timing
62
+
63
+ Stella.stdout.head 'Arrival', opts[:arrival] if opts[:arrival]
64
+
65
+ @dumper.start
66
+
67
+ begin
68
+ Stella.stdout.status "Running"
69
+ execute_test_plan packages, opts[:repetitions], opts[:duration], opts[:arrival]
70
+ rescue Interrupt
71
+ Stella.stdout.info $/, "Stopping test"
72
+ Stella.abort!
73
+ @threads.each { |t| t.join } unless @threads.nil? || @threads.empty? # wait
74
+ rescue => ex
75
+ STDERR.puts "Unhandled exception: #{ex.message}"
76
+ STDERR.puts ex.backtrace if Stella.debug? || Stella.stdout.lev >= 3
77
+ end
78
+
79
+ Stella.stdout.status "Processing"
80
+
81
+ @dumper.stop
82
+
83
+ bt = Benelux.timeline
84
+ tt = Benelux.thread_timeline
85
+
86
+ # TODO: don't get test time from benelux.
87
+ test_time = tt.stats.group(:execute_test_plan).mean
88
+ generate_report @sumlog, plan, test_time
89
+ #report_time = tt.stats.group(:generate_report).mean
90
+
91
+ Stella.stdout.info File.read(@sumlog.path)
92
+
93
+ Stella.stdout.info $/, "Log dir: #{@logdir}"
94
+
95
+ @testrun
96
+ end
97
+
7
98
  ROTATE_TIMELINE = 5
8
99
  def execute_test_plan(packages, reps=1,duration=0,arrival=nil)
9
100
  time_started = Time.now
@@ -90,7 +181,320 @@ module Stella::Engine
90
181
  Stella.stdout.info2 $/, $/
91
182
  end
92
183
 
93
- Benelux.add_timer Stella::Engine::Load, :execute_test_plan
94
184
 
185
+ protected
186
+ class ThreadPackage
187
+ attr_accessor :index
188
+ attr_accessor :client
189
+ attr_accessor :usecase
190
+ def initialize(i, c, u)
191
+ @index, @client, @usecase = i, c, u
192
+ end
193
+ end
194
+
195
+ def prepare_dumper(plan, opts)
196
+ hand = Stella::Hand.new(Load::ROTATE_TIMELINE, 2.seconds) do
197
+ Benelux.update_global_timeline
198
+ # @threads contains only stella clients
199
+ concurrency = @threads.select { |t| !t.status.nil? }.size
200
+ batch, timeline = Benelux.timeline_updates, Benelux.timeline_chunk
201
+ @testrun.add_sample batch, concurrency, timeline
202
+ @testrun.save(@statlog_path)
203
+ @failog.info Benelux.timeline.messages.filter(:kind => :exception)
204
+ @failog.info Benelux.timeline.messages.filter(:kind => :timeout)
205
+ Benelux.timeline.clear if opts[:"no-stats"]
206
+ end
207
+ hand.finally do
208
+ @testrun.save(@statlog_path)
209
+ end
210
+ hand
211
+ end
212
+
213
+ def generate_report(sumlog,plan,test_time)
214
+ global_timeline = Benelux.timeline
215
+ global_stats = global_timeline.stats.group(:response_time).merge
216
+ if global_stats.n == 0
217
+ Stella.ld "No stats"
218
+ return
219
+ end
220
+
221
+ @sumlog.info " %-72s ".att(:reverse) % ["#{plan.desc} (#{plan.digest_cache.shorter})"]
222
+ plan.usecases.uniq.each_with_index do |uc,i|
223
+
224
+ # TODO: Create Ranges object, like Stats object
225
+ # global_timeline.ranges(:response_time)[:usecase => '1111']
226
+ # The following returns global response_time ranges.
227
+ requests = 0 #global_timeline.ranges(:response_time).size
228
+
229
+ desc = uc.desc || "Usecase ##{i+1} "
230
+ desc << " (#{uc.digest_cache.shorter}) "
231
+ str = ' ' << " %-66s %s %d%% ".bright.att(:reverse)
232
+ @sumlog.info str % [desc, '', uc.ratio_pretty]
233
+ uc.requests.each do |req|
234
+ filter = [uc.digest_cache, req.digest_cache]
235
+ desc = req.desc
236
+ @sumlog.info " %-72s ".bright % ["#{req.desc} (#{req.digest_cache.shorter})"]
237
+ @sumlog.info " %s" % [req.to_s]
238
+
239
+ Load.timers.each do |sname|
240
+ stats = global_timeline.stats.group(sname)[filter].merge
241
+ # Stella.stdout.info stats.inspect
242
+ str = ' %-30s %.3f <= ' << '%.3fs' << ' >= %.3f; %.3f(SD) %d(N)'
243
+ msg = str % [sname, stats.min, stats.mean, stats.max, stats.sd, stats.n]
244
+ @sumlog.info msg
245
+ @sumlog.flush
246
+ end
247
+ @sumlog.info $/
248
+ end
249
+
250
+ @sumlog.info " Sub Total:".bright
251
+
252
+ stats = global_timeline.stats.group(:response_time)[uc.digest_cache].merge
253
+ failed = global_timeline.stats.group(:failed)[uc.digest_cache].merge
254
+ respgrp = global_timeline.stats.group(:execute_response_handler)[uc.digest_cache]
255
+ resst = respgrp.tag_values(:status)
256
+
257
+ Load.timers.each do |sname|
258
+ stats = global_timeline.stats.group(sname)[uc.digest_cache].merge
259
+ @sumlog.info ' %-30s %.3fs %.3f(SD)' % [sname, stats.mean, stats.sd]
260
+ @sumlog.flush
261
+ end
262
+
263
+ Load.counts.each do |sname|
264
+ stats = global_timeline.stats.group(sname)[uc.digest_cache].merge
265
+ @sumlog.info ' %-30s %-12s (avg:%s)' % [sname, stats.sum.to_bytes, stats.mean.to_bytes]
266
+ @sumlog.flush
267
+ end
268
+ @sumlog.info $/
269
+ statusi = []
270
+ resst.each do |status|
271
+ size = respgrp[:status => status].size
272
+ statusi << "#{status}: #{size}"
273
+ end
274
+ @sumlog.info ' %-30s %d (%s)' % ['Total requests', stats.n, statusi.join(', ')]
275
+ @sumlog.info ' %-29s %d' % [:success, stats.n - failed.n]
276
+ @sumlog.info ' %-29s %d' % [:failed, failed.n]
277
+
278
+ @sumlog.info $/
279
+ end
280
+
281
+ @sumlog.info ' ' << " %-66s ".att(:reverse) % 'Total:'
282
+ @sumlog.flush
283
+
284
+ failed = global_timeline.stats.group(:failed)
285
+ respgrp = global_timeline.stats.group(:execute_response_handler)
286
+ resst = respgrp.tag_values(:status)
287
+ statusi = []
288
+ resst.each do |status|
289
+ size = respgrp[:status => status].size
290
+ statusi << [status, size]
291
+ end
292
+
293
+ Load.timers.each do |sname|
294
+ stats = global_timeline.stats.group(sname).merge
295
+ @sumlog.info ' %-30s %-.3fs %-.3f(SD)' % [sname, stats.mean, stats.sd]
296
+ @sumlog.flush
297
+ end
298
+
299
+ Load.counts.each do |sname|
300
+ stats = global_timeline.stats.group(sname).merge
301
+ @sumlog.info ' %-30s %-12s (avg:%s)' % [sname, stats.sum.to_bytes, stats.mean.to_bytes]
302
+ @sumlog.flush
303
+ end
304
+
305
+ @sumlog.info $/
306
+ @sumlog.info ' %-30s %d' % ['Total requests', global_stats.n]
307
+
308
+ success = global_stats.n - failed.n
309
+ @sumlog.info ' %-29s %d (req/s: %.2f)' % [:success, success, success/test_time]
310
+ statusi.each do |pair|
311
+ @sumlog.info3 ' %-28s %s: %d' % ['', *pair]
312
+ end
313
+ @sumlog.info ' %-29s %d' % [:failed, failed.n]
314
+
315
+ @sumlog.flush
316
+
317
+ end
318
+
319
+
320
+ def calculate_usecase_clients(plan, opts)
321
+ counts = { :total => 0 }
322
+ plan.usecases.each_with_index do |usecase,i|
323
+ count = case opts[:clients]
324
+ when 0..9
325
+ if (opts[:clients] % plan.usecases.size > 0)
326
+ msg = "Client count does not evenly match usecase count"
327
+ raise Stella::WackyRatio, msg
328
+ else
329
+ (opts[:clients] / plan.usecases.size)
330
+ end
331
+ else
332
+ (opts[:clients] * usecase.ratio).to_i
333
+ end
334
+ counts[usecase.digest_cache] = count
335
+ counts[:total] += count
336
+ end
337
+ counts
338
+ end
339
+
340
+
341
+ def build_thread_package(plan, opts, counts)
342
+ packages, pointer = Array.new(counts[:total]), 0
343
+ plan.usecases.each do |usecase|
344
+ count = counts[usecase.digest_cache]
345
+ Stella.ld "THREAD PACKAGE: #{usecase.desc} (#{pointer} + #{count})"
346
+ # Fill the thread_package with the contents of the block
347
+ packages.fill(pointer, count) do |index|
348
+ client = Stella::Client.new opts[:hosts].first, index+1, opts
349
+ client.add_observer(self)
350
+ client.enable_nowait_mode if opts[:nowait]
351
+ Stella.stdout.info4 "Created client #{client.digest.short}"
352
+ ThreadPackage.new(index+1, client, usecase)
353
+ end
354
+ pointer += count
355
+ end
356
+ packages.compact # TODO: Why one nil element sometimes?
357
+ # Randomize so when ramping up load
358
+ # we get a mix of usecases.
359
+ packages.sort_by {rand}
360
+ end
361
+
362
+
363
+ def running_threads
364
+ @threads.select { |t| t.status } # non-false status are still running
365
+ end
366
+
367
+ def generate_runtime_report(plan)
368
+ gt = Benelux.timeline
369
+ gstats = gt.stats.group(:response_time).merge
370
+
371
+ plan.usecases.uniq.each_with_index do |uc,i|
372
+ uc.requests.each do |req|
373
+ filter = [uc.digest_cache, req.digest_cache]
374
+
375
+ Load.timers.each do |sname|
376
+ stats = gt.stats.group(sname)[filter].merge
377
+ #Stella.stdout.info stats.inspect
378
+ puts [sname, stats.min, stats.mean, stats.max, stats.sd, stats.n].join('; ')
379
+ end
380
+
381
+ end
382
+ end
383
+
384
+ end
385
+
386
+ def update_prepare_request(client_id, usecase, req, counter)
387
+
388
+ end
389
+
390
+ def update_receive_response(client_id, usecase, uri, req, params, headers, counter, container)
391
+ args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
392
+ args.push usecase.digest.shorter, req.digest.shorter
393
+ args.push req.http_method, container.status, uri
394
+ args << params.to_a.collect { |el|
395
+ next if el[0].to_s == '__stella'
396
+ '%s=%s' % [el[0], el[1].to_s]
397
+ }.compact.join('&') # remove skipped params
398
+ args << headers.to_a.collect { |el|
399
+ next if el[0].to_s == 'X-Stella-ID'
400
+ '%s=%s' % el
401
+ }.compact.join('&') # remove skipped params
402
+ args << container.unique_id[0,10]
403
+ #Benelux.thread_timeline.add_message args.join('; '),
404
+ # :status => container.status,
405
+ # :kind => :request
406
+ args = [client_id.shorter, container.status, req.http_method, uri, params.inspect]
407
+ Stella.stdout.info3 ' Client-%s %3d %-6s %s %s' % args
408
+
409
+ end
410
+
411
+ def update_execute_response_handler(client_id, req, container)
412
+ end
413
+
414
+ def update_error_execute_response_handler(client_id, ex, req, container)
415
+ desc = "#{container.usecase.desc} > #{req.desc}"
416
+ if Stella.stdout.lev == 2
417
+ Stella.stdout.print 2, '.'.color(:red)
418
+ else
419
+ Stella.le ' Client-%s %-45s %s' % [client_id.shorter, desc, ex.message]
420
+ Stella.ld ex.backtrace
421
+ end
422
+ end
423
+
424
+ def update_request_unhandled_exception(client_id, usecase, uri, req, params, ex)
425
+ desc = "#{usecase.desc} > #{req.desc}"
426
+ if Stella.stdout.lev == 2
427
+ Stella.stdout.print 2, '.'.color(:red)
428
+ else
429
+ Stella.le ' Client-%s %-45s %s' % [client_id.shorter, desc, ex.message]
430
+ Stella.ld ex.backtrace
431
+ end
432
+ end
433
+
434
+ def update_usecase_quit client_id, msg, req, container
435
+ args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
436
+ Benelux.thread_timeline.add_count :quit, 1
437
+ args.push [req, container.status, 'QUIT', msg, container.unique_id[0,10]]
438
+ Benelux.thread_timeline.add_message args.join('; '), :kind => :exception
439
+ Stella.stdout.info3 " Client-%s QUIT %s" % [client_id.shorter, msg]
440
+ end
441
+
442
+ def update_request_fail client_id, msg, req, container
443
+ args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
444
+ Benelux.thread_timeline.add_count :failed, 1
445
+ args.push [req, container.status, 'FAIL', msg, container.unique_id[0,10]]
446
+ Benelux.thread_timeline.add_message args.join('; '), :kind => :exception
447
+ Stella.stdout.info3 " Client-%s FAILED %s" % [client_id.shorter, msg]
448
+ end
449
+
450
+ def update_request_error client_id, msg, req, container
451
+ args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
452
+ Benelux.thread_timeline.add_count :error, 1
453
+ args.push [req, container.status, 'ERROR', msg, container.unique_id[0,10]]
454
+ Benelux.thread_timeline.add_message args.join('; '), :kind => :exception
455
+ if Stella.stdout.lev >= 3
456
+ Stella.le ' Client-%s %-45s %s' % [client_id.shorter, desc, ex.message]
457
+ end
458
+ end
459
+
460
+ def update_request_repeat client_id, counter, total, req, container
461
+ Stella.stdout.info3 " Client-%s REPEAT %d of %d" % [client_id.shorter, counter, total]
462
+ end
463
+
464
+ def update_follow_redirect client_id, ret, req, container
465
+ Stella.stdout.info3 " Client-%s FOLLOW %-53s" % [client_id.shorter, ret.uri]
466
+ end
467
+
468
+ def update_max_redirects client_id, counter, ret, req, container
469
+ Stella.stdout.info3 " Client-%s MAX REDIRECTS %s " % [client_id.shorter, counter]
470
+ end
471
+
472
+ def update_authenticate client_id, usecase, req, domain, user, pass
473
+ args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
474
+ args.push usecase.digest.shorter, req.digest.shorter
475
+ args.push 'AUTH', domain, user, pass
476
+ Benelux.thread_timeline.add_message args.join('; '), :kind => :authentication
477
+ end
478
+
479
+ def update_request_timeout(client_id, usecase, uri, req, params, headers, counter, container)
480
+ Stella.stdout.info3 " Client-%s TIMEOUT %-53s" % [client_id.shorter, uri]
481
+ Benelux.thread_timeline.add_count :failed, 1
482
+ args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
483
+ args.push [uri, 'TOUT', container.unique_id[0,10]]
484
+ Benelux.thread_timeline.add_message args.join('; '), :kind => :timeout
485
+ end
486
+
487
+ def self.rescue(client_id, &blk)
488
+ blk.call
489
+ rescue => ex
490
+ Stella.le ' Error in Client-%s: %s' % [client_id.shorter, ex.message]
491
+ Stella.ld ex.backtrace
492
+ end
493
+
494
+ Benelux.add_timer Stella::Engine::Load, :build_thread_package
495
+ Benelux.add_timer Stella::Engine::Load, :generate_report
496
+ Benelux.add_timer Stella::Engine::Load, :execute_test_plan
497
+
95
498
  end
96
- end
499
+ end
500
+
data/stella.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{stella}
8
- s.version = "0.8.2.002"
8
+ s.version = "0.8.2.003"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Delano Mandelbaum"]
12
- s.date = %q{2010-03-05}
12
+ s.date = %q{2010-03-06}
13
13
  s.default_executable = %q{stella}
14
14
  s.description = %q{Blame Stella for breaking your web application!}
15
15
  s.email = %q{delano@solutious.com}
@@ -49,7 +49,6 @@ Gem::Specification.new do |s|
49
49
  "lib/stella/engine.rb",
50
50
  "lib/stella/engine/functional.rb",
51
51
  "lib/stella/engine/load.rb",
52
- "lib/stella/engine/loadbase.rb",
53
52
  "lib/stella/guidelines.rb",
54
53
  "lib/stella/logger.rb",
55
54
  "lib/stella/service.rb",
@@ -2,14 +2,19 @@
2
2
 
3
3
  require 'stella'
4
4
 
5
- #Stella.enable_quiet
6
- Stella.stdout.lev = 3
5
+ Benelux.enable_debug
6
+
7
+ Stella.enable_quiet
8
+ #Stella.stdout.lev = 3
7
9
  plan = Stella::Testplan.new('http://localhost:3114/search')
8
10
  opts = {
9
11
  :hosts => '',
10
12
  :clients => 100,
11
13
  #:duration => 10
12
14
  }
13
- Stella::Engine::Load.run plan, opts
14
15
 
15
- puts Stella::Engine::Load.testrun.stats[:summary].to_json
16
+ #engine = Stella::Engine::Functional.new opts
17
+ engine = Stella::Engine::Load.new opts
18
+ engine.run plan
19
+
20
+ puts engine.testrun.stats[:summary].to_json
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stella
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2.002
4
+ version: 0.8.2.003
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-03-05 00:00:00 -05:00
12
+ date: 2010-03-06 00:00:00 -05:00
13
13
  default_executable: stella
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -112,7 +112,6 @@ files:
112
112
  - lib/stella/engine.rb
113
113
  - lib/stella/engine/functional.rb
114
114
  - lib/stella/engine/load.rb
115
- - lib/stella/engine/loadbase.rb
116
115
  - lib/stella/guidelines.rb
117
116
  - lib/stella/logger.rb
118
117
  - lib/stella/service.rb
@@ -1,423 +0,0 @@
1
-
2
- module Stella::Engine
3
- module Load
4
- extend Stella::Engine::Base
5
- extend self
6
-
7
- @timers = [:response_time]
8
- @counts = [:response_content_size]
9
- @reqlog = nil
10
- @logdir = nil
11
-
12
- class << self
13
- attr_accessor :timers, :counts, :logdir, :testrun
14
- end
15
-
16
- def run(plan, opts={})
17
- opts = process_options! plan, opts
18
- @threads, @max_clients, @real_reps = [], 0, 0
19
-
20
- @logdir = log_dir(plan)
21
- latest = File.join(File.dirname(@logdir), 'latest')
22
- Stella.stdout.info "Logging to #{@logdir}", $/
23
-
24
- if Stella.sysinfo.os == :unix
25
- File.unlink latest if File.exists? latest
26
- FileUtils.ln_sf File.basename(@logdir), latest
27
- end
28
-
29
- @statlog_path = log_path(plan, 'stats')
30
- @sumlog = Stella::Logger.new log_path(plan, 'summary')
31
- @failog = Stella::Logger.new log_path(plan, 'exceptions')
32
-
33
- Stella.stdout.add_template :head, ' %s: %s'
34
- Stella.stdout.add_template :status, "#{$/}%s..."
35
-
36
- if Stella.stdout.lev >= 2
37
- Load.timers += [:socket_connect, :send_request, :first_byte, :receive_response]
38
- Load.counts = [:request_header_size, :request_content_size]
39
- Load.counts += [:response_headers_size, :response_content_size]
40
- end
41
-
42
- events = [Load.timers, Load.counts, :failed].flatten
43
- @testrun = Stella::Testrun.new plan, events, opts
44
- @testrun.mode = 'l'
45
-
46
- if Stella::Engine.service
47
- Stella::Engine.service.testplan_sync plan
48
- Stella::Engine.service.testrun_create @testrun
49
- Stella.stdout.head 'Testrun', @testrun.remote_digest
50
- end
51
-
52
- @testrun.save(@statlog_path)
53
-
54
- @dumper = prepare_dumper(plan, opts)
55
-
56
- counts = calculate_usecase_clients plan, opts
57
-
58
- packages = build_thread_package plan, opts, counts
59
-
60
- if opts[:duration] > 0
61
- timing = "#{opts[:duration].seconds.to_i} seconds"
62
- else
63
- timing = "#{opts[:repetitions]} repetitions"
64
- end
65
-
66
- Stella.stdout.head 'Plan', "#{plan.desc} (#{plan.digest.shorter})"
67
- Stella.stdout.head 'Hosts', opts[:hosts].join(', ')
68
- Stella.stdout.head 'Clients', counts[:total]
69
- Stella.stdout.head 'Limit', timing
70
-
71
- Stella.stdout.head 'Arrival', opts[:arrival] if opts[:arrival]
72
-
73
- @dumper.start
74
-
75
- begin
76
- Stella.stdout.status "Running"
77
- execute_test_plan packages, opts[:repetitions], opts[:duration], opts[:arrival]
78
- rescue Interrupt
79
- Stella.stdout.info $/, "Stopping test"
80
- Stella.abort!
81
- @threads.each { |t| t.join } unless @threads.nil? || @threads.empty? # wait
82
- rescue => ex
83
- STDERR.puts "Unhandled exception: #{ex.message}"
84
- STDERR.puts ex.backtrace if Stella.debug? || Stella.stdout.lev >= 3
85
- end
86
-
87
- Stella.stdout.status "Processing"
88
-
89
- @dumper.stop
90
-
91
- bt = Benelux.timeline
92
- tt = Benelux.thread_timeline
93
-
94
- # TODO: don't get test time from benelux.
95
- test_time = tt.stats.group(:execute_test_plan).mean
96
- generate_report @sumlog, plan, test_time
97
- #report_time = tt.stats.group(:generate_report).mean
98
-
99
- Stella.stdout.info File.read(@sumlog.path)
100
-
101
- Stella.stdout.info $/, "Log dir: #{@logdir}"
102
-
103
- @testrun.stats[:summary][:failed].n == 0
104
- end
105
-
106
- protected
107
- class ThreadPackage
108
- attr_accessor :index
109
- attr_accessor :client
110
- attr_accessor :usecase
111
- def initialize(i, c, u)
112
- @index, @client, @usecase = i, c, u
113
- end
114
- end
115
-
116
- def prepare_dumper(plan, opts)
117
- hand = Stella::Hand.new(Load::ROTATE_TIMELINE, 2.seconds) do
118
- Benelux.update_global_timeline
119
- # @threads contains only stella clients
120
- concurrency = @threads.select { |t| !t.status.nil? }.size
121
- batch, timeline = Benelux.timeline_updates, Benelux.timeline_chunk
122
- @testrun.add_sample batch, concurrency, timeline
123
- @testrun.save(@statlog_path)
124
- @failog.info Benelux.timeline.messages.filter(:kind => :exception)
125
- @failog.info Benelux.timeline.messages.filter(:kind => :timeout)
126
- Benelux.timeline.clear if opts[:"no-stats"]
127
- end
128
- hand.finally do
129
- @testrun.save(@statlog_path)
130
- end
131
- hand
132
- end
133
-
134
- def generate_report(sumlog,plan,test_time)
135
- global_timeline = Benelux.timeline
136
- global_stats = global_timeline.stats.group(:response_time).merge
137
- if global_stats.n == 0
138
- Stella.ld "No stats"
139
- return
140
- end
141
-
142
- @sumlog.info " %-72s ".att(:reverse) % ["#{plan.desc} (#{plan.digest_cache.shorter})"]
143
- plan.usecases.uniq.each_with_index do |uc,i|
144
-
145
- # TODO: Create Ranges object, like Stats object
146
- # global_timeline.ranges(:response_time)[:usecase => '1111']
147
- # The following returns global response_time ranges.
148
- requests = 0 #global_timeline.ranges(:response_time).size
149
-
150
- desc = uc.desc || "Usecase ##{i+1} "
151
- desc << " (#{uc.digest_cache.shorter}) "
152
- str = ' ' << " %-66s %s %d%% ".bright.att(:reverse)
153
- @sumlog.info str % [desc, '', uc.ratio_pretty]
154
- uc.requests.each do |req|
155
- filter = [uc.digest_cache, req.digest_cache]
156
- desc = req.desc
157
- @sumlog.info " %-72s ".bright % ["#{req.desc} (#{req.digest_cache.shorter})"]
158
- @sumlog.info " %s" % [req.to_s]
159
-
160
- Load.timers.each do |sname|
161
- stats = global_timeline.stats.group(sname)[filter].merge
162
- # Stella.stdout.info stats.inspect
163
- str = ' %-30s %.3f <= ' << '%.3fs' << ' >= %.3f; %.3f(SD) %d(N)'
164
- msg = str % [sname, stats.min, stats.mean, stats.max, stats.sd, stats.n]
165
- @sumlog.info msg
166
- @sumlog.flush
167
- end
168
- @sumlog.info $/
169
- end
170
-
171
- @sumlog.info " Sub Total:".bright
172
-
173
- stats = global_timeline.stats.group(:response_time)[uc.digest_cache].merge
174
- failed = global_timeline.stats.group(:failed)[uc.digest_cache].merge
175
- respgrp = global_timeline.stats.group(:execute_response_handler)[uc.digest_cache]
176
- resst = respgrp.tag_values(:status)
177
-
178
- Load.timers.each do |sname|
179
- stats = global_timeline.stats.group(sname)[uc.digest_cache].merge
180
- @sumlog.info ' %-30s %.3fs %.3f(SD)' % [sname, stats.mean, stats.sd]
181
- @sumlog.flush
182
- end
183
-
184
- Load.counts.each do |sname|
185
- stats = global_timeline.stats.group(sname)[uc.digest_cache].merge
186
- @sumlog.info ' %-30s %-12s (avg:%s)' % [sname, stats.sum.to_bytes, stats.mean.to_bytes]
187
- @sumlog.flush
188
- end
189
- @sumlog.info $/
190
- statusi = []
191
- resst.each do |status|
192
- size = respgrp[:status => status].size
193
- statusi << "#{status}: #{size}"
194
- end
195
- @sumlog.info ' %-30s %d (%s)' % ['Total requests', stats.n, statusi.join(', ')]
196
- @sumlog.info ' %-29s %d' % [:success, stats.n - failed.n]
197
- @sumlog.info ' %-29s %d' % [:failed, failed.n]
198
-
199
- @sumlog.info $/
200
- end
201
-
202
- @sumlog.info ' ' << " %-66s ".att(:reverse) % 'Total:'
203
- @sumlog.flush
204
-
205
- failed = global_timeline.stats.group(:failed)
206
- respgrp = global_timeline.stats.group(:execute_response_handler)
207
- resst = respgrp.tag_values(:status)
208
- statusi = []
209
- resst.each do |status|
210
- size = respgrp[:status => status].size
211
- statusi << [status, size]
212
- end
213
-
214
- Load.timers.each do |sname|
215
- stats = global_timeline.stats.group(sname).merge
216
- @sumlog.info ' %-30s %-.3fs %-.3f(SD)' % [sname, stats.mean, stats.sd]
217
- @sumlog.flush
218
- end
219
-
220
- Load.counts.each do |sname|
221
- stats = global_timeline.stats.group(sname).merge
222
- @sumlog.info ' %-30s %-12s (avg:%s)' % [sname, stats.sum.to_bytes, stats.mean.to_bytes]
223
- @sumlog.flush
224
- end
225
-
226
- @sumlog.info $/
227
- @sumlog.info ' %-30s %d' % ['Total requests', global_stats.n]
228
-
229
- success = global_stats.n - failed.n
230
- @sumlog.info ' %-29s %d (req/s: %.2f)' % [:success, success, success/test_time]
231
- statusi.each do |pair|
232
- @sumlog.info3 ' %-28s %s: %d' % ['', *pair]
233
- end
234
- @sumlog.info ' %-29s %d' % [:failed, failed.n]
235
-
236
- @sumlog.flush
237
-
238
- end
239
-
240
-
241
- def calculate_usecase_clients(plan, opts)
242
- counts = { :total => 0 }
243
- plan.usecases.each_with_index do |usecase,i|
244
- count = case opts[:clients]
245
- when 0..9
246
- if (opts[:clients] % plan.usecases.size > 0)
247
- msg = "Client count does not evenly match usecase count"
248
- raise Stella::WackyRatio, msg
249
- else
250
- (opts[:clients] / plan.usecases.size)
251
- end
252
- else
253
- (opts[:clients] * usecase.ratio).to_i
254
- end
255
- counts[usecase.digest_cache] = count
256
- counts[:total] += count
257
- end
258
- counts
259
- end
260
-
261
-
262
- def build_thread_package(plan, opts, counts)
263
- packages, pointer = Array.new(counts[:total]), 0
264
- plan.usecases.each do |usecase|
265
- count = counts[usecase.digest_cache]
266
- Stella.ld "THREAD PACKAGE: #{usecase.desc} (#{pointer} + #{count})"
267
- # Fill the thread_package with the contents of the block
268
- packages.fill(pointer, count) do |index|
269
- client = Stella::Client.new opts[:hosts].first, index+1, opts
270
- client.add_observer(self)
271
- client.enable_nowait_mode if opts[:nowait]
272
- Stella.stdout.info4 "Created client #{client.digest.short}"
273
- ThreadPackage.new(index+1, client, usecase)
274
- end
275
- pointer += count
276
- end
277
- packages.compact # TODO: Why one nil element sometimes?
278
- # Randomize so when ramping up load
279
- # we get a mix of usecases.
280
- packages.sort_by {rand}
281
- end
282
-
283
- def execute_test_plan(*args)
284
- raise "Override execute_test_plan method in #{self}"
285
- end
286
-
287
- def running_threads
288
- @threads.select { |t| t.status } # non-false status are still running
289
- end
290
-
291
- def generate_runtime_report(plan)
292
- gt = Benelux.timeline
293
- gstats = gt.stats.group(:response_time).merge
294
-
295
- plan.usecases.uniq.each_with_index do |uc,i|
296
- uc.requests.each do |req|
297
- filter = [uc.digest_cache, req.digest_cache]
298
-
299
- Load.timers.each do |sname|
300
- stats = gt.stats.group(sname)[filter].merge
301
- #Stella.stdout.info stats.inspect
302
- puts [sname, stats.min, stats.mean, stats.max, stats.sd, stats.n].join('; ')
303
- end
304
-
305
- end
306
- end
307
-
308
- end
309
-
310
- def update_prepare_request(client_id, usecase, req, counter)
311
-
312
- end
313
-
314
- def update_receive_response(client_id, usecase, uri, req, params, headers, counter, container)
315
- args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
316
- args.push usecase.digest.shorter, req.digest.shorter
317
- args.push req.http_method, container.status, uri
318
- args << params.to_a.collect { |el|
319
- next if el[0].to_s == '__stella'
320
- '%s=%s' % [el[0], el[1].to_s]
321
- }.compact.join('&') # remove skipped params
322
- args << headers.to_a.collect { |el|
323
- next if el[0].to_s == 'X-Stella-ID'
324
- '%s=%s' % el
325
- }.compact.join('&') # remove skipped params
326
- args << container.unique_id[0,10]
327
- #Benelux.thread_timeline.add_message args.join('; '),
328
- # :status => container.status,
329
- # :kind => :request
330
- args = [client_id.shorter, container.status, req.http_method, uri, params.inspect]
331
- Stella.stdout.info3 ' Client-%s %3d %-6s %s %s' % args
332
-
333
- end
334
-
335
- def update_execute_response_handler(client_id, req, container)
336
- end
337
-
338
- def update_error_execute_response_handler(client_id, ex, req, container)
339
- desc = "#{container.usecase.desc} > #{req.desc}"
340
- if Stella.stdout.lev == 2
341
- Stella.stdout.print 2, '.'.color(:red)
342
- else
343
- Stella.le ' Client-%s %-45s %s' % [client_id.shorter, desc, ex.message]
344
- Stella.ld ex.backtrace
345
- end
346
- end
347
-
348
- def update_request_unhandled_exception(client_id, usecase, uri, req, params, ex)
349
- desc = "#{usecase.desc} > #{req.desc}"
350
- if Stella.stdout.lev == 2
351
- Stella.stdout.print 2, '.'.color(:red)
352
- else
353
- Stella.le ' Client-%s %-45s %s' % [client_id.shorter, desc, ex.message]
354
- Stella.ld ex.backtrace
355
- end
356
- end
357
-
358
- def update_usecase_quit client_id, msg, req, container
359
- args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
360
- Benelux.thread_timeline.add_count :quit, 1
361
- args.push [req, container.status, 'QUIT', msg, container.unique_id[0,10]]
362
- Benelux.thread_timeline.add_message args.join('; '), :kind => :exception
363
- Stella.stdout.info3 " Client-%s QUIT %s" % [client_id.shorter, msg]
364
- end
365
-
366
- def update_request_fail client_id, msg, req, container
367
- args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
368
- Benelux.thread_timeline.add_count :failed, 1
369
- args.push [req, container.status, 'FAIL', msg, container.unique_id[0,10]]
370
- Benelux.thread_timeline.add_message args.join('; '), :kind => :exception
371
- Stella.stdout.info3 " Client-%s FAILED %s" % [client_id.shorter, msg]
372
- end
373
-
374
- def update_request_error client_id, msg, req, container
375
- args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
376
- Benelux.thread_timeline.add_count :error, 1
377
- args.push [req, container.status, 'ERROR', msg, container.unique_id[0,10]]
378
- Benelux.thread_timeline.add_message args.join('; '), :kind => :exception
379
- if Stella.stdout.lev >= 3
380
- Stella.le ' Client-%s %-45s %s' % [client_id.shorter, desc, ex.message]
381
- end
382
- end
383
-
384
- def update_request_repeat client_id, counter, total, req, container
385
- Stella.stdout.info3 " Client-%s REPEAT %d of %d" % [client_id.shorter, counter, total]
386
- end
387
-
388
- def update_follow_redirect client_id, ret, req, container
389
- Stella.stdout.info3 " Client-%s FOLLOW %-53s" % [client_id.shorter, ret.uri]
390
- end
391
-
392
- def update_max_redirects client_id, counter, ret, req, container
393
- Stella.stdout.info3 " Client-%s MAX REDIRECTS %s " % [client_id.shorter, counter]
394
- end
395
-
396
- def update_authenticate client_id, usecase, req, domain, user, pass
397
- args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
398
- args.push usecase.digest.shorter, req.digest.shorter
399
- args.push 'AUTH', domain, user, pass
400
- Benelux.thread_timeline.add_message args.join('; '), :kind => :authentication
401
- end
402
-
403
- def update_request_timeout(client_id, usecase, uri, req, params, headers, counter, container)
404
- Stella.stdout.info3 " Client-%s TIMEOUT %-53s" % [client_id.shorter, uri]
405
- Benelux.thread_timeline.add_count :failed, 1
406
- args = [Time.now.to_f, Stella.sysinfo.hostname, client_id.short]
407
- args.push [uri, 'TOUT', container.unique_id[0,10]]
408
- Benelux.thread_timeline.add_message args.join('; '), :kind => :timeout
409
- end
410
-
411
- def self.rescue(client_id, &blk)
412
- blk.call
413
- rescue => ex
414
- Stella.le ' Error in Client-%s: %s' % [client_id.shorter, ex.message]
415
- Stella.ld ex.backtrace
416
- end
417
-
418
- Benelux.add_timer Stella::Engine::Load, :build_thread_package
419
- Benelux.add_timer Stella::Engine::Load, :generate_report
420
-
421
- end
422
- end
423
-