stella 0.8.2.002 → 0.8.2.003

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 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
-