perf_check 0.4.1 → 0.5.0

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