perf_check 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ad9c18545aaee3bdcba0a6bb28eb307b341ba78a
4
- data.tar.gz: 89809449598f93009f78b0c353ab432f7da341f5
3
+ metadata.gz: 3c41d74ec42bbf613535e0a755f07d509c061763
4
+ data.tar.gz: baa5b5b5aa29175b800e5356e90caec667a98863
5
5
  SHA512:
6
- metadata.gz: 51b79c492435781ae275d452045cc34ce6d7468b0ce756a43b1ead8692a3c15a42ca6f718854718aea189c4f9aafaf089da281767d44f34eb89a441ffddb1e41
7
- data.tar.gz: 6963d6cf9bf01766168a87fc3962cbdd85b9a42b46b63128c9f9defb18d2c206219d0eb359c0da8c6dc3ef066c268023ceb2a3dba7f810f245824cf4367caac8
6
+ metadata.gz: 699c58295c4aac2949b7d764e55cff7160080187afbc2430200016017ba21013ca710d04e7c3cbb7ea768dac378e28daf82f9cdca36e367c6f297f379d8758a1
7
+ data.tar.gz: b935d9879a07bc679343f5e49c5a2e549b38158f3e2b849a3e5a3655d7244d071794f1fa074b881922649c7324ab2d859eb5b550d4ebee318846d2d06d70032f
@@ -2,13 +2,26 @@
2
2
 
3
3
  require 'perf_check'
4
4
 
5
- ORIGINAL_ARGV = ARGV.clone
5
+ app_root =
6
+ begin
7
+ dir = Dir.pwd
8
+ until dir == '/' || File.exist?("#{dir}/config/application.rb")
9
+ dir = File.dirname(dir)
10
+ end
6
11
 
7
- if File.exists?("#{PerfCheck.app_root}/tmp/pids/server.pid")
12
+ unless File.exist?("#{dir}/config/application.rb")
13
+ abort("perf_check should be run from a rails directory")
14
+ end
15
+
16
+ dir
17
+ end
18
+
19
+
20
+ if File.exists?("#{app_root}/tmp/pids/server.pid")
8
21
  abort("It looks like a rails server is already running. Shut it down before continuing with perf_check.")
9
22
  end
10
23
 
11
- perf_check = PerfCheck.new
24
+ perf_check = PerfCheck.new(app_root).tap(&:load_config)
12
25
 
13
26
  at_exit do
14
27
  cbdata = {}
@@ -21,20 +34,19 @@ at_exit do
21
34
  perf_check.trigger_when_finished_callbacks(cbdata)
22
35
  end
23
36
 
24
- PerfCheck.load_config
25
- PerfCheck::Options.parse!
26
-
27
- ARGV.each{ |route| perf_check.add_test_case(route) }
37
+ perf_check.option_parser.parse(ARGV).each do |route|
38
+ perf_check.add_test_case(route)
39
+ end
28
40
 
29
41
  if perf_check.test_cases.empty?
30
- abort(PerfCheck::Options.help)
42
+ abort(perf_check.option_parser.help)
31
43
  end
32
44
 
33
45
  perf_check.run
34
46
 
35
- if PerfCheck.config.brief
47
+ if perf_check.options.brief
36
48
  perf_check.print_brief_results
37
- elsif PerfCheck.config.json
49
+ elsif perf_check.options.json
38
50
  perf_check.print_json_results
39
51
  else
40
52
  perf_check.print_full_results
@@ -5,44 +5,69 @@ require 'benchmark'
5
5
  require 'ostruct'
6
6
  require 'colorize'
7
7
  require 'json'
8
+ require 'logger'
8
9
 
9
10
  class PerfCheck
10
- attr_accessor :options, :server, :test_cases
11
-
12
- def self.app_root
13
- @app_root ||= begin
14
- dir = Dir.pwd
15
- until dir == '/' || File.exist?("#{dir}/config/application.rb")
16
- dir = File.dirname(dir)
17
- end
18
-
19
- unless File.exist?("#{dir}/config/application.rb")
20
- abort("perf_check should be run from a rails directory")
11
+ class Exception < ::Exception; end
12
+
13
+ attr_reader :app_root, :options, :git, :server, :test_cases
14
+ attr_accessor :logger
15
+
16
+ def initialize(app_root)
17
+ @app_root = app_root
18
+
19
+ @options = OpenStruct.new(
20
+ number_of_requests: 20,
21
+ reference: 'master',
22
+ cookie: nil,
23
+ headers: {},
24
+ http_statuses: [200],
25
+ verify_responses: false,
26
+ caching: true,
27
+ json: false
28
+ )
29
+
30
+ @logger = Logger.new(STDERR).tap do |logger|
31
+ logger.formatter = proc do |severity, datetime, progname, msg|
32
+ "[#{datetime.strftime("%Y-%m-%d %H:%M:%S")}] #{msg}\n"
21
33
  end
22
-
23
- dir
24
34
  end
35
+
36
+ @git = Git.new(self)
37
+ @server = Server.new(self)
38
+ @test_cases = []
25
39
  end
26
40
 
27
- def initialize
28
- self.options = OpenStruct.new
29
- self.server = Server.new
30
- self.test_cases = []
41
+ def load_config
42
+ if File.exists?("#{app_root}/config/perf_check.rb")
43
+ this = self
44
+ Kernel.send(:define_method, :perf_check){ this }
45
+ load "#{app_root}/config/perf_check.rb"
46
+ Kernel.send(:remove_method, :perf_check)
47
+ end
31
48
  end
32
49
 
33
50
  def add_test_case(route)
34
- test_cases.push(TestCase.new(route.sub(/^([^\/])/, '/\1')))
51
+ test_cases.push(TestCase.new(self, route.sub(/^([^\/])/, '/\1')))
35
52
  end
36
53
 
37
54
  def run
38
- profile_requests
55
+ begin
56
+ profile_requests
39
57
 
40
- if options.reference
41
- Git.stash_if_needed
42
- Git.checkout_reference(options.reference)
43
- test_cases.each{ |x| x.switch_to_reference_context }
58
+ if options.reference
59
+ git.stash_if_needed
60
+ git.checkout_reference(options.reference)
61
+ test_cases.each{ |x| x.switch_to_reference_context }
44
62
 
45
- profile_requests
63
+ profile_requests
64
+ end
65
+ ensure
66
+ server.exit rescue nil
67
+ if options.reference
68
+ git.checkout_current_branch(false) rescue nil
69
+ (git.pop rescue nil) if git.stashed?
70
+ end
46
71
  end
47
72
  end
48
73
 
@@ -59,10 +84,10 @@ class PerfCheck
59
84
  test.cookie = options.cookie
60
85
 
61
86
  if options.diff
62
- PerfCheck.logger.info("Issuing #{test.resource}")
87
+ logger.info("Issuing #{test.resource}")
63
88
  else
64
- PerfCheck.logger.info ''
65
- PerfCheck.logger.info("Benchmarking #{test.resource}:")
89
+ logger.info ''
90
+ logger.info("Benchmarking #{test.resource}:")
66
91
  end
67
92
 
68
93
  test.run(server, options)
@@ -73,18 +98,17 @@ class PerfCheck
73
98
 
74
99
  def run_migrations_up
75
100
  Bundler.with_clean_env{ puts `bundle exec rake db:migrate` }
76
- Git.clean_db
101
+ git.clean_db
77
102
  end
78
103
 
79
104
  def run_migrations_down
80
- Git.migrations_to_run_down.each do |version|
105
+ git.migrations_to_run_down.each do |version|
81
106
  Bundler.with_clean_env{ puts `bundle exec rake db:migrate:down VERSION=#{version}` }
82
107
  end
83
- Git.clean_db
108
+ git.clean_db
84
109
  end
85
110
  end
86
111
 
87
- require 'perf_check/logger'
88
112
  require 'perf_check/server'
89
113
  require 'perf_check/test_case'
90
114
  require 'perf_check/git'
@@ -1,42 +1,41 @@
1
1
  # coding: utf-8
2
2
  class PerfCheck
3
- def self.when_finished(&block)
3
+ def when_finished(&block)
4
4
  @when_finished_callbacks ||= []
5
5
  @when_finished_callbacks << block
6
6
  end
7
7
 
8
- def self.when_finished_callbacks
8
+ def when_finished_callbacks
9
9
  @when_finished_callbacks || []
10
10
  end
11
11
 
12
- def self.before_start(&block)
12
+ def before_start(&block)
13
13
  @before_start_callbacks ||= []
14
14
  @before_start_callbacks << block
15
15
  end
16
16
 
17
- def self.before_start_callbacks
17
+ def before_start_callbacks
18
18
  (@before_start_callbacks || []) + [
19
- proc {
20
- PerfCheck.logger.info("=" * 77)
21
- PerfCheck.logger.info("PERRRRF CHERRRK! Grab a ☕️ and don't touch your working tree (we automate git)")
22
- PerfCheck.logger.info("=" * 77)
19
+ proc { |perf_check|
20
+ perf_check.logger.info("=" * 77)
21
+ perf_check.logger.info("PERRRRF CHERRRK! Grab a ☕️ and don't touch your working tree (we automate git)")
22
+ perf_check.logger.info("=" * 77)
23
23
  }
24
24
  ]
25
25
  end
26
26
 
27
27
 
28
28
  def trigger_before_start_callbacks(test_case)
29
- PerfCheck.before_start_callbacks.each{ |f| f.call(self, test_case) }
29
+ before_start_callbacks.each{ |f| f.call(self, test_case) }
30
30
  end
31
31
 
32
32
  def trigger_when_finished_callbacks(data={})
33
- data = data.merge(:current_branch => PerfCheck::Git.current_branch)
33
+ data = data.merge(:current_branch => git.current_branch)
34
34
  results = OpenStruct.new(data)
35
- results[:ARGV] = ORIGINAL_ARGV
36
35
  if test_cases.size == 1
37
36
  results.current_latency = test_cases.first.this_latency
38
37
  results.reference_latency = test_cases.first.reference_latency
39
38
  end
40
- PerfCheck.when_finished_callbacks.each{ |f| f.call(results) }
39
+ when_finished_callbacks.each{ |f| f.call(self, results) }
41
40
  end
42
41
  end
@@ -1,102 +1,80 @@
1
1
  require 'optparse'
2
2
 
3
3
  class PerfCheck
4
- def self.config
5
- @config ||= OpenStruct.new(
6
- number_of_requests: 20,
7
- reference: 'master',
8
- cookie: nil,
9
- headers: {},
10
- http_statuses: [200],
11
- verify_responses: false,
12
- caching: true,
13
- json: false
14
- )
15
- end
16
-
17
- def config
18
- PerfCheck.config
19
- end
20
-
21
- alias :options :config
22
-
23
- Options = OptionParser.new do |opts|
24
- opts.banner = "Usage: perf_check [options] [route ...]"
25
-
26
- opts.separator "\nBenchmark options:"
27
- opts.on('--requests N', '-n',
28
- 'Use N requests in benchmark, defaults to 20') do |n|
29
- config.number_of_requests = n.to_i
30
- end
31
-
32
- opts.on('--reference COMMIT', '-r',
33
- 'Benchmark against COMMIT instead of master') do |commit|
34
- config.reference = commit
35
- end
4
+ def option_parser
5
+ @optparse ||= OptionParser.new do |opts|
6
+ opts.banner = "Usage: perf_check [options] [route ...]"
7
+
8
+ opts.separator "\nBenchmark options:"
9
+ opts.on('--requests N', '-n',
10
+ 'Use N requests in benchmark, defaults to 20') do |n|
11
+ options.number_of_requests = n.to_i
12
+ end
36
13
 
37
- opts.on('--quick', '-q',
38
- 'Fire off 20 requests just on this branch (no comparison with master)') do
39
- config.reference = nil
40
- end
14
+ opts.on('--reference COMMIT', '-r',
15
+ 'Benchmark against COMMIT instead of master') do |commit|
16
+ options.reference = commit
17
+ end
41
18
 
42
- opts.on('--no-caching', 'Do not enable fragment caching') do
43
- config.caching = false
44
- end
19
+ opts.on('--quick', '-q',
20
+ 'Fire off 20 requests just on this branch (no comparison with master)') do
21
+ options.reference = nil
22
+ end
45
23
 
46
- opts.on('--run-migrations', 'Run migrations on the branch and unmigrate at the end') do
47
- config[:run_migrations?] = true
48
- end
24
+ opts.on('--no-caching', 'Do not enable fragment caching') do
25
+ options.caching = false
26
+ end
49
27
 
50
- opts.on('--fail-fast', '-f', 'Bail immediately on non-200 HTTP response') do
51
- config[:fail_fast?] = true
52
- end
28
+ opts.on('--run-migrations', 'Run migrations on the branch and unmigrate at the end') do
29
+ options[:run_migrations?] = true
30
+ end
53
31
 
54
- opts.on('--302-success', 'Consider HTTP 302 code a successful request') do
55
- config.http_statuses.push(302)
56
- end
32
+ opts.on('--302-success', 'Consider HTTP 302 code a successful request') do
33
+ options.http_statuses.push(302)
34
+ end
57
35
 
58
- opts.on('--302-failure', 'Consider HTTP 302 code an unsuccessful request') do
59
- config.http_statuses.delete(302)
60
- end
36
+ opts.on('--302-failure', 'Consider HTTP 302 code an unsuccessful request') do
37
+ options.http_statuses.delete(302)
38
+ end
61
39
 
62
- opts.separator "\nMisc"
63
- opts.on('--cookie COOKIE', '-c') do |cookie|
64
- config.cookie = cookie
65
- end
40
+ opts.separator "\nMisc"
41
+ opts.on('--cookie COOKIE', '-c') do |cookie|
42
+ options.cookie = cookie
43
+ end
66
44
 
67
- opts.on('--header HEADER', '-H') do |header|
68
- key, value = header.split(':', 2)
69
- config.headers[key.strip] = value.strip
70
- end
45
+ opts.on('--header HEADER', '-H') do |header|
46
+ key, value = header.split(':', 2)
47
+ options.headers[key.strip] = value.strip
48
+ end
71
49
 
72
- opts.on('--json', '-j') do
73
- config.json = true
74
- end
50
+ opts.on('--json', '-j') do
51
+ options.json = true
52
+ end
75
53
 
76
- opts.on('--input FILE', '-i') do |input|
77
- File.readlines(input).each do |resource|
78
- ARGV << resource.strip
54
+ opts.on('--input FILE', '-i') do |input|
55
+ File.readlines(input).each do |resource|
56
+ ARGV << resource.strip
57
+ end
79
58
  end
80
- end
81
59
 
82
- opts.on('--verify-responses',
83
- 'Check whether there is a diff between the responses of this and the reference branch') do
84
- config.verify_responses = true
85
- end
60
+ opts.on('--verify-responses',
61
+ 'Check whether there is a diff between the responses of this and the reference branch') do
62
+ options.verify_responses = true
63
+ end
86
64
 
87
- opts.on('--brief', '-b') do
88
- config.brief = true
89
- end
65
+ opts.on('--brief', '-b') do
66
+ options.brief = true
67
+ end
90
68
 
91
- opts.on('--diff') do
92
- config.diff = true
93
- config.brief = true
94
- config.verify_responses = true
95
- config.number_of_requests = 1
96
- end
69
+ # opts.on('--diff') do
70
+ # options.diff = true
71
+ # options.brief = true
72
+ # options.verify_responses = true
73
+ # options.number_of_requests = 1
74
+ # end
97
75
 
98
- opts.separator ''
99
- opts.separator <<EOF
76
+ opts.separator ''
77
+ opts.separator <<EOF
100
78
  Usage examples:
101
79
  Benchmark PostController#index against master
102
80
  perf_check /user/45/posts
@@ -119,17 +97,12 @@ Usage examples:
119
97
  perf_check --diff --input FILE
120
98
  EOF
121
99
 
122
- opts.separator ''
100
+ opts.separator ''
101
+ end
123
102
  end
124
103
 
125
104
  def self.diff_options
126
105
  @@diff_options ||=
127
106
  ['-U3', '--ignore-matching-lines=/mini-profiler-resources/includes.js']
128
107
  end
129
-
130
- def self.load_config
131
- if File.exists?("#{app_root}/config/perf_check.rb")
132
- require "#{app_root}/config/perf_check"
133
- end
134
- end
135
108
  end
@@ -1,84 +1,105 @@
1
+ require 'shellwords'
1
2
 
2
3
  class PerfCheck
3
4
  class Git
4
- # the branch we are on while loading the script
5
- @current_branch = `git rev-parse --abbrev-ref HEAD`.strip
5
+ class NoSuchBranch < Exception; end
6
+ class StashError < Exception; end
7
+ class StashPopError < Exception; end
8
+ class BundleError < Exception; end
6
9
 
7
- def self.current_branch
8
- @current_branch
10
+ attr_reader :perf_check, :git_root, :current_branch
11
+ attr_accessor :logger
12
+
13
+ def initialize(perf_check)
14
+ @perf_check = perf_check
15
+ @git_root = perf_check.app_root
16
+ @logger = perf_check.logger
17
+
18
+ @current_branch = exec "git rev-parse --abbrev-ref HEAD"
9
19
  end
10
20
 
11
- def self.checkout_reference(reference='master')
21
+ def checkout_reference(reference='master')
12
22
  checkout(reference)
13
- at_exit do
14
- PerfCheck.logger.info ''
15
- Git.checkout_current_branch(false)
16
- end
17
23
  end
18
24
 
19
- def self.checkout_current_branch(bundle=true)
25
+ def checkout_current_branch(bundle=true)
20
26
  checkout(@current_branch, bundle)
21
27
  end
22
28
 
23
- def self.checkout(branch, bundle=true)
24
- PerfCheck.logger.info("Checking out #{branch} and bundling... ")
25
- `git checkout #{branch} --quiet`
29
+ def checkout(branch, bundle=true)
30
+ logger.info("Checking out #{branch} and bundling... ")
31
+ exec "git checkout #{branch} --quiet"
26
32
 
27
33
  unless $?.success?
28
- PerfCheck.logger.fatal("Problem with git checkout! Bailing...") && abort
34
+ logger.fatal("Problem with git checkout! Bailing...")
35
+ raise NoSuchBranch
29
36
  end
30
37
 
31
- `git submodule update --quiet`
38
+ exec "git submodule update --quiet"
32
39
 
33
40
  if bundle
34
- Bundler.with_clean_env{ `bundle` }
41
+ Bundler.with_clean_env{ exec "bundle" }
35
42
  unless $?.success?
36
- PerfCheck.logger.fatal("Problem bundling! Bailing...") && abort
43
+ logger.fatal("Problem bundling! Bailing...")
44
+ raise BundleError
37
45
  end
38
46
  end
39
47
  end
40
48
 
41
- def self.stash_if_needed
49
+ def stash_if_needed
42
50
  if anything_to_stash?
43
- PerfCheck.logger.info("Stashing your changes... ")
44
- system('git stash -q >/dev/null')
51
+ logger.info("Stashing your changes... ")
52
+ exec "git stash -q >/dev/null"
45
53
 
46
54
  unless $?.success?
47
- PerfCheck.logger.fatal("Problem with git stash! Bailing...") && abort
55
+ logger.fatal("Problem with git stash! Bailing...")
56
+ raise StashError
48
57
  end
49
58
 
50
- at_exit do
51
- Git.pop
52
- end
59
+ @stashed = true
53
60
  end
54
61
  end
55
62
 
56
- def self.anything_to_stash?
57
- git_stash = `git diff`
58
- git_stash << `git diff --staged`
63
+ def stashed?
64
+ !!@stashed
65
+ end
66
+
67
+ def anything_to_stash?
68
+ git_stash = exec "git diff"
69
+ git_stash << exec("git diff --staged")
59
70
  !git_stash.empty?
60
71
  end
61
72
 
62
- def self.pop
63
- PerfCheck.logger.info("Git stash applying...")
64
- system('git stash pop -q')
73
+ def pop
74
+ logger.info("Git stash applying...")
75
+ exec "git stash pop -q"
65
76
 
66
77
  unless $?.success?
67
- PerfCheck.logger.fatal("Problem with git stash! Bailing...") && abort
78
+ logger.fatal("Problem with git stash! Bailing...")
79
+ raise StashPopError
68
80
  end
69
81
  end
70
82
 
71
- def self.migrations_to_run_down
72
- current_migrations_not_on_master.map { |filename| File.basename(filename, '.rb').split('_').first }
83
+ def migrations_to_run_down
84
+ current_migrations_not_on_master.map do |filename|
85
+ File.basename(filename, '.rb').split('_').first
86
+ end
73
87
  end
74
88
 
75
- def self.current_migrations_not_on_master
76
- %x{git diff origin/master --name-only --diff-filter=A db/migrate/}.split.reverse
89
+ def clean_db
90
+ exec "git checkout db"
91
+ end
92
+
93
+ private
94
+
95
+ def current_migrations_not_on_master
96
+ exec("git diff origin/master --name-only --diff-filter=A db/migrate/").
97
+ split.reverse
77
98
  end
78
- private_class_method :current_migrations_not_on_master
79
99
 
80
- def self.clean_db
81
- `git checkout db`
100
+ def exec(command)
101
+ root = Shellwords.shellescape(git_root)
102
+ `cd #{root} && #{command}`.strip
82
103
  end
83
104
  end
84
105
  end
@@ -14,7 +14,20 @@ class PerfCheck
14
14
 
15
15
  def call(env)
16
16
  self.query_count = 0
17
- status, headers, body = app.call(env)
17
+
18
+ begin
19
+ status, headers, body = app.call(env)
20
+ rescue ::Exception => e
21
+ trace_file = "#{Rails.root}/tmp/perf_check_traces" <<
22
+ "/trace-#{SecureRandom.hex(16)}.txt"
23
+ FileUtils.mkdir_p(File.dirname(trace_file))
24
+
25
+ File.open(trace_file, 'w') do |f|
26
+ f.puts("#{e.class}: #{e.message}")
27
+ f.write(e.backtrace.join("\n"))
28
+ end
29
+ status, headers, body = 500, {"X-PerfCheck-StackTrace" => trace_file}, ''
30
+ end
18
31
 
19
32
  headers['X-PerfCheck-Query-Count'] = query_count.to_s
20
33
 
@@ -91,7 +91,7 @@ class PerfCheck
91
91
  results[-1].merge!(
92
92
  reference_latency: test.reference_latency,
93
93
  latency_difference: test.latency_difference,
94
- speedup_factor: test.reference_latency / test.this_latency,
94
+ speedup_factor: test.speedup_factor,
95
95
  reference_query_count: test.reference_query_count,
96
96
  reference_requests: []
97
97
  )
@@ -3,9 +3,12 @@ require 'net/http'
3
3
  require 'benchmark'
4
4
  require 'ostruct'
5
5
  require 'fileutils'
6
+ require 'shellwords'
6
7
 
7
8
  class PerfCheck
8
9
  class Server
10
+ attr_reader :perf_check
11
+
9
12
  def self.seed_random!
10
13
  # Seed random
11
14
  srand(1)
@@ -32,14 +35,12 @@ class PerfCheck
32
35
  end
33
36
  end
34
37
 
35
- def initialize
36
- at_exit do
37
- exit rescue nil
38
- end
38
+ def initialize(perf_check)
39
+ @perf_check = perf_check
39
40
  end
40
41
 
41
42
  def pid
42
- pidfile = 'tmp/pids/server.pid'
43
+ pidfile = "#{perf_check.app_root}/tmp/pids/server.pid"
43
44
  File.read(pidfile).to_i if File.exists?(pidfile)
44
45
  end
45
46
 
@@ -48,16 +49,18 @@ class PerfCheck
48
49
  end
49
50
 
50
51
  def prepare_to_profile
51
- FileUtils.mkdir_p('tmp/perf_check/miniprofiler')
52
- Dir["tmp/perf_check/miniprofiler/*"].each{|x| FileUtils.rm(x) }
52
+ app_root = perf_check.app_root
53
+ FileUtils.mkdir_p("#{app_root}/tmp/perf_check/miniprofiler")
54
+ Dir["#{app_root}/tmp/perf_check/miniprofiler/*"].each{|x| FileUtils.rm(x) }
53
55
  end
54
56
 
55
57
  def latest_profiler_url
56
- mp_timer = Dir["tmp/perf_check/miniprofiler/mp_timers_*"].first
58
+ app_root = perf_check.app_root
59
+ mp_timer = Dir["#{app_root}/tmp/perf_check/miniprofiler/mp_timers_*"].first
57
60
  if "#{mp_timer}" =~ /mp_timers_(\w+)/
58
61
  mp_link = "/mini-profiler-resources/results?id=#{$1}"
59
- FileUtils.mkdir_p('tmp/miniprofiler')
60
- FileUtils.mv(mp_timer, mp_timer.sub(/^tmp\/perf_check\//, 'tmp/'))
62
+ FileUtils.mkdir_p("#{app_root}/tmp/miniprofiler")
63
+ FileUtils.mv(mp_timer, mp_timer.sub(/^#{app_root}\/tmp\/perf_check\//, "#{app_root}/tmp/"))
61
64
  end
62
65
  mp_link
63
66
  end
@@ -73,6 +76,7 @@ class PerfCheck
73
76
 
74
77
  latency = 1000 * response['X-Runtime'].to_f
75
78
  query_count = response['X-PerfCheck-Query-Count'].to_i
79
+ backtrace_file = response['X-PerfCheck-StackTrace']
76
80
 
77
81
  Profile.new.tap do |result|
78
82
  result.latency = latency
@@ -80,6 +84,10 @@ class PerfCheck
80
84
  result.profile_url = latest_profiler_url
81
85
  result.response_body = response.body
82
86
  result.response_code = response.code.to_i
87
+ result.server_memory = mem
88
+ if backtrace_file
89
+ result.backtrace = File.read(backtrace_file).lines.map(&:chomp)
90
+ end
83
91
  end
84
92
  end
85
93
 
@@ -93,25 +101,26 @@ class PerfCheck
93
101
 
94
102
  def start
95
103
  ENV['PERF_CHECK'] = '1'
96
- if PerfCheck.config.verify_responses
104
+ if perf_check.options.verify_responses
97
105
  ENV['PERF_CHECK_VERIFICATION'] = '1'
98
106
  end
99
- unless PerfCheck.config.caching
107
+ unless perf_check.options.caching
100
108
  ENV['PERF_CHECK_NOCACHING'] = '1'
101
109
  end
102
110
 
103
- system('rails server -b 127.0.0.1 -d -p 3031 >/dev/null')
111
+ app_root = Shellwords.shellescape(perf_check.app_root)
112
+ system("cd #{app_root} && rails server -b 127.0.0.1 -d -p 3031 >/dev/null")
104
113
  sleep(1.5)
105
114
 
106
115
  @running = true
107
116
  end
108
117
 
109
118
  def restart
110
- if !@running
111
- PerfCheck.logger.info("starting rails...")
119
+ if !running?
120
+ perf_check.logger.info("starting rails...")
112
121
  start
113
122
  else
114
- PerfCheck.logger.info("re-starting rails...")
123
+ perf_check.logger.info("re-starting rails...")
115
124
  exit
116
125
  start
117
126
  end
@@ -125,6 +134,10 @@ class PerfCheck
125
134
  3031
126
135
  end
127
136
 
137
+ def running?
138
+ @running
139
+ end
140
+
128
141
  class Profile < OpenStruct; end
129
142
  end
130
143
  end
@@ -4,74 +4,53 @@ require 'diffy'
4
4
 
5
5
  class PerfCheck
6
6
  class TestCase
7
+ attr_reader :perf_check
7
8
  attr_accessor :resource
8
9
  attr_accessor :cookie, :this_response, :reference_response
9
10
  attr_accessor :this_profiles, :reference_profiles
10
11
 
11
- def initialize(route)
12
+ def initialize(perf_check, route)
13
+ @perf_check = perf_check
12
14
  self.this_profiles = []
13
15
  self.reference_profiles = []
14
16
  self.resource = route
15
17
  end
16
18
 
17
- def switch_to_reference_context
18
- @context = :reference
19
- end
20
-
21
19
  def run(server, options)
22
20
  unless options.diff
23
- PerfCheck.logger.info("\t"+['request', 'latency', 'server rss', 'status', 'queries', 'profiler data'].map(&:underline).join(" "))
21
+ perf_check.logger.info("\t"+['request', 'latency', 'server rss', 'status', 'queries', 'profiler data'].map(&:underline).join(" "))
24
22
  end
25
23
 
26
- profiles = (@context == :reference) ? reference_profiles : this_profiles
27
-
28
- headers = {'Cookie' => "#{cookie}".strip}
29
- headers['Accept'] = 'text/html,application/xhtml+xml,application/xml'
30
- headers.merge!(PerfCheck.config.headers)
31
-
32
24
  (options.number_of_requests+1).times do |i|
33
- profile = server.profile do |http|
34
- http.get(resource, headers)
35
- end
25
+ profile = issue_request(server, options)
26
+ next if i.zero? # first request just warms up the server
36
27
 
37
- unless options.http_statuses.include? profile.response_code
38
- if options.fail_fast?
39
- File.open("tmp/perf_check/failed_request.html", 'w') do |error_dump|
40
- error_dump.write(profile.response_body)
41
- end
42
- error = sprintf("\t%2i:\tFAILED! (HTTP %d)", i, profile.response_code)
43
- PerfCheck.logger.fatal(error.red.bold)
44
- PerfCheck.logger.fatal("\t The server responded with a non-2xx status for this request.")
45
- PerfCheck.logger.fatal("\t The response has been written to tmp/perf_check/failed_request.html")
46
- abort
47
- end
48
- end
49
-
50
- next if i.zero?
51
-
52
- if options.verify_responses
53
- if i == 1
54
- if @context == :reference
55
- self.reference_response = profile.response_body
56
- else
57
- self.this_response = profile.response_body
58
- end
59
- end
28
+ if options.verify_responses && i == 1
29
+ response_for_comparison(profile.response_body)
60
30
  end
61
31
 
62
- profile.server_memory = server.mem
63
-
64
32
  unless options.diff
65
33
  row = sprintf("\t%2i:\t %.1fms %4dMB\t %s\t %s\t %s",
66
34
  i, profile.latency, profile.server_memory,
67
35
  profile.response_code, profile.query_count, profile.profile_url)
68
- PerfCheck.logger.info(row)
36
+ perf_check.logger.info(row)
69
37
  end
70
38
 
71
- profiles << profile
39
+ context_profiles << profile
40
+ unless options.http_statuses.include?(profile.response_code)
41
+ error = sprintf("\t :\tFAILED! (HTTP %d)", profile.response_code)
42
+ perf_check.logger.warn(error.red.bold)
43
+ perf_check.logger.warn("\t The server responded with an invalid http code")
44
+ if profile.backtrace
45
+ perf_check.logger.warn("Backtrace found:")
46
+ backtrace = [profile.backtrace[0], *profile.backtrace.grep(/#{perf_check.app_root}/)]
47
+ backtrace.each{ |line| perf_check.logger.warn(" #{line}") }
48
+ end
49
+ break
50
+ end
72
51
  end
73
52
 
74
- PerfCheck.logger.info '' unless options.diff # pretty!
53
+ perf_check.logger.info '' unless options.diff # pretty!
75
54
  end
76
55
 
77
56
  def this_latency
@@ -96,7 +75,7 @@ class PerfCheck
96
75
  this_latency - reference_latency
97
76
  end
98
77
 
99
- def latency_factor
78
+ def speedup_factor
100
79
  reference_latency / this_latency
101
80
  end
102
81
 
@@ -123,5 +102,35 @@ class PerfCheck
123
102
  def hash
124
103
  resource.hash
125
104
  end
105
+
106
+ def issue_request(server, options)
107
+ server.profile do |http|
108
+ http.get(resource, request_headers)
109
+ end
110
+ end
111
+
112
+ def request_headers
113
+ headers = {'Cookie' => "#{cookie}".strip}
114
+ headers['Accept'] = 'text/html,application/xhtml+xml,application/xml'
115
+ headers.merge!(perf_check.options.headers)
116
+ end
117
+
118
+ def switch_to_reference_context
119
+ @context = :reference
120
+ end
121
+
122
+ private
123
+
124
+ def context_profiles
125
+ (@context == :reference) ? reference_profiles : this_profiles
126
+ end
127
+
128
+ def response_for_comparison(response_body)
129
+ if @context == :reference
130
+ self.reference_response = response_body
131
+ else
132
+ self.this_response = response_body
133
+ end
134
+ end
126
135
  end
127
136
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perf_check
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rubytune
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-15 00:00:00.000000000 Z
11
+ date: 2016-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: colorize
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -50,7 +64,6 @@ files:
50
64
  - lib/perf_check/callbacks.rb
51
65
  - lib/perf_check/config.rb
52
66
  - lib/perf_check/git.rb
53
- - lib/perf_check/logger.rb
54
67
  - lib/perf_check/middleware.rb
55
68
  - lib/perf_check/output.rb
56
69
  - lib/perf_check/railtie.rb
@@ -76,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
89
  version: '0'
77
90
  requirements: []
78
91
  rubyforge_project:
79
- rubygems_version: 2.4.5
92
+ rubygems_version: 2.4.5.1
80
93
  signing_key:
81
94
  specification_version: 4
82
95
  summary: PERF CHECKKK!
@@ -1,14 +0,0 @@
1
-
2
- require 'logger'
3
-
4
- class PerfCheck
5
- def self.logger
6
- @logger ||= Logger.new(STDERR).tap do |logger|
7
- logger.formatter = proc do |severity, datetime, progname, msg|
8
- "[#{datetime}] #{sprintf('%5s', severity)} --: #{msg}\n"
9
- end
10
- end
11
- end
12
-
13
- def logger; self.class.logger; end
14
- end