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 +4 -4
- data/bin/perf_check +22 -10
- data/lib/perf_check.rb +55 -31
- data/lib/perf_check/callbacks.rb +11 -12
- data/lib/perf_check/config.rb +61 -88
- data/lib/perf_check/git.rb +59 -38
- data/lib/perf_check/middleware.rb +14 -1
- data/lib/perf_check/output.rb +1 -1
- data/lib/perf_check/server.rb +29 -16
- data/lib/perf_check/test_case.rb +53 -44
- metadata +17 -4
- data/lib/perf_check/logger.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c41d74ec42bbf613535e0a755f07d509c061763
|
4
|
+
data.tar.gz: baa5b5b5aa29175b800e5356e90caec667a98863
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 699c58295c4aac2949b7d764e55cff7160080187afbc2430200016017ba21013ca710d04e7c3cbb7ea768dac378e28daf82f9cdca36e367c6f297f379d8758a1
|
7
|
+
data.tar.gz: b935d9879a07bc679343f5e49c5a2e549b38158f3e2b849a3e5a3655d7244d071794f1fa074b881922649c7324ab2d859eb5b550d4ebee318846d2d06d70032f
|
data/bin/perf_check
CHANGED
@@ -2,13 +2,26 @@
|
|
2
2
|
|
3
3
|
require 'perf_check'
|
4
4
|
|
5
|
-
|
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
|
-
|
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
|
-
|
25
|
-
|
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(
|
42
|
+
abort(perf_check.option_parser.help)
|
31
43
|
end
|
32
44
|
|
33
45
|
perf_check.run
|
34
46
|
|
35
|
-
if
|
47
|
+
if perf_check.options.brief
|
36
48
|
perf_check.print_brief_results
|
37
|
-
elsif
|
49
|
+
elsif perf_check.options.json
|
38
50
|
perf_check.print_json_results
|
39
51
|
else
|
40
52
|
perf_check.print_full_results
|
data/lib/perf_check.rb
CHANGED
@@ -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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
55
|
+
begin
|
56
|
+
profile_requests
|
39
57
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
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
|
-
|
87
|
+
logger.info("Issuing #{test.resource}")
|
63
88
|
else
|
64
|
-
|
65
|
-
|
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
|
-
|
101
|
+
git.clean_db
|
77
102
|
end
|
78
103
|
|
79
104
|
def run_migrations_down
|
80
|
-
|
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
|
-
|
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'
|
data/lib/perf_check/callbacks.rb
CHANGED
@@ -1,42 +1,41 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
class PerfCheck
|
3
|
-
def
|
3
|
+
def when_finished(&block)
|
4
4
|
@when_finished_callbacks ||= []
|
5
5
|
@when_finished_callbacks << block
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
8
|
+
def when_finished_callbacks
|
9
9
|
@when_finished_callbacks || []
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
12
|
+
def before_start(&block)
|
13
13
|
@before_start_callbacks ||= []
|
14
14
|
@before_start_callbacks << block
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
17
|
+
def before_start_callbacks
|
18
18
|
(@before_start_callbacks || []) + [
|
19
|
-
proc {
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
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 =>
|
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
|
-
|
39
|
+
when_finished_callbacks.each{ |f| f.call(self, results) }
|
41
40
|
end
|
42
41
|
end
|
data/lib/perf_check/config.rb
CHANGED
@@ -1,102 +1,80 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
|
3
3
|
class PerfCheck
|
4
|
-
def
|
5
|
-
@
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
14
|
+
opts.on('--reference COMMIT', '-r',
|
15
|
+
'Benchmark against COMMIT instead of master') do |commit|
|
16
|
+
options.reference = commit
|
17
|
+
end
|
41
18
|
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
24
|
+
opts.on('--no-caching', 'Do not enable fragment caching') do
|
25
|
+
options.caching = false
|
26
|
+
end
|
49
27
|
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
55
|
-
|
56
|
-
|
32
|
+
opts.on('--302-success', 'Consider HTTP 302 code a successful request') do
|
33
|
+
options.http_statuses.push(302)
|
34
|
+
end
|
57
35
|
|
58
|
-
|
59
|
-
|
60
|
-
|
36
|
+
opts.on('--302-failure', 'Consider HTTP 302 code an unsuccessful request') do
|
37
|
+
options.http_statuses.delete(302)
|
38
|
+
end
|
61
39
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
40
|
+
opts.separator "\nMisc"
|
41
|
+
opts.on('--cookie COOKIE', '-c') do |cookie|
|
42
|
+
options.cookie = cookie
|
43
|
+
end
|
66
44
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
50
|
+
opts.on('--json', '-j') do
|
51
|
+
options.json = true
|
52
|
+
end
|
75
53
|
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
88
|
-
|
89
|
-
|
65
|
+
opts.on('--brief', '-b') do
|
66
|
+
options.brief = true
|
67
|
+
end
|
90
68
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
99
|
-
|
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
|
-
|
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
|
data/lib/perf_check/git.rb
CHANGED
@@ -1,84 +1,105 @@
|
|
1
|
+
require 'shellwords'
|
1
2
|
|
2
3
|
class PerfCheck
|
3
4
|
class Git
|
4
|
-
|
5
|
-
|
5
|
+
class NoSuchBranch < Exception; end
|
6
|
+
class StashError < Exception; end
|
7
|
+
class StashPopError < Exception; end
|
8
|
+
class BundleError < Exception; end
|
6
9
|
|
7
|
-
|
8
|
-
|
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
|
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
|
25
|
+
def checkout_current_branch(bundle=true)
|
20
26
|
checkout(@current_branch, bundle)
|
21
27
|
end
|
22
28
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
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
|
-
|
34
|
+
logger.fatal("Problem with git checkout! Bailing...")
|
35
|
+
raise NoSuchBranch
|
29
36
|
end
|
30
37
|
|
31
|
-
|
38
|
+
exec "git submodule update --quiet"
|
32
39
|
|
33
40
|
if bundle
|
34
|
-
Bundler.with_clean_env{
|
41
|
+
Bundler.with_clean_env{ exec "bundle" }
|
35
42
|
unless $?.success?
|
36
|
-
|
43
|
+
logger.fatal("Problem bundling! Bailing...")
|
44
|
+
raise BundleError
|
37
45
|
end
|
38
46
|
end
|
39
47
|
end
|
40
48
|
|
41
|
-
def
|
49
|
+
def stash_if_needed
|
42
50
|
if anything_to_stash?
|
43
|
-
|
44
|
-
|
51
|
+
logger.info("Stashing your changes... ")
|
52
|
+
exec "git stash -q >/dev/null"
|
45
53
|
|
46
54
|
unless $?.success?
|
47
|
-
|
55
|
+
logger.fatal("Problem with git stash! Bailing...")
|
56
|
+
raise StashError
|
48
57
|
end
|
49
58
|
|
50
|
-
|
51
|
-
Git.pop
|
52
|
-
end
|
59
|
+
@stashed = true
|
53
60
|
end
|
54
61
|
end
|
55
62
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
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
|
63
|
-
|
64
|
-
|
73
|
+
def pop
|
74
|
+
logger.info("Git stash applying...")
|
75
|
+
exec "git stash pop -q"
|
65
76
|
|
66
77
|
unless $?.success?
|
67
|
-
|
78
|
+
logger.fatal("Problem with git stash! Bailing...")
|
79
|
+
raise StashPopError
|
68
80
|
end
|
69
81
|
end
|
70
82
|
|
71
|
-
def
|
72
|
-
current_migrations_not_on_master.map
|
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
|
76
|
-
|
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
|
81
|
-
|
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
|
-
|
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
|
|
data/lib/perf_check/output.rb
CHANGED
@@ -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.
|
94
|
+
speedup_factor: test.speedup_factor,
|
95
95
|
reference_query_count: test.reference_query_count,
|
96
96
|
reference_requests: []
|
97
97
|
)
|
data/lib/perf_check/server.rb
CHANGED
@@ -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
|
-
|
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 =
|
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
|
-
|
52
|
-
|
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
|
-
|
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(
|
60
|
-
FileUtils.mv(mp_timer, mp_timer.sub(
|
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
|
104
|
+
if perf_check.options.verify_responses
|
97
105
|
ENV['PERF_CHECK_VERIFICATION'] = '1'
|
98
106
|
end
|
99
|
-
unless
|
107
|
+
unless perf_check.options.caching
|
100
108
|
ENV['PERF_CHECK_NOCACHING'] = '1'
|
101
109
|
end
|
102
110
|
|
103
|
-
|
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
|
111
|
-
|
119
|
+
if !running?
|
120
|
+
perf_check.logger.info("starting rails...")
|
112
121
|
start
|
113
122
|
else
|
114
|
-
|
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
|
data/lib/perf_check/test_case.rb
CHANGED
@@ -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
|
-
|
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
|
34
|
-
|
35
|
-
end
|
25
|
+
profile = issue_request(server, options)
|
26
|
+
next if i.zero? # first request just warms up the server
|
36
27
|
|
37
|
-
|
38
|
-
|
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
|
-
|
36
|
+
perf_check.logger.info(row)
|
69
37
|
end
|
70
38
|
|
71
|
-
|
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
|
-
|
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
|
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
|
+
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-
|
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!
|
data/lib/perf_check/logger.rb
DELETED
@@ -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
|