class PerfCheck
Attributes
app_root[R]
git[R]
logger[RW]
options[R]
server[R]
test_cases[R]
Public Class Methods
new(app_root)
click to toggle source
# File lib/perf_check.rb, line 16 def initialize(app_root) @app_root = app_root @options = OpenStruct.new( number_of_requests: 20, reference: 'master', branch: nil, cookie: nil, headers: {}, http_statuses: [200], verify_no_diff: false, diff: false, diff_options: ['-U3', '--ignore-matching-lines=/mini-profiler-resources/includes.js'], brief: false, caching: true, json: false, hard_reset: false, environment: 'development' ) @logger = Logger.new(STDERR).tap do |logger| logger.formatter = proc do |severity, datetime, progname, msg| "[#{datetime.strftime("%Y-%m-%d %H:%M:%S")}] #{msg}\n" end end @git = Git.new(self) @server = Server.new(self) @test_cases = [] end
Public Instance Methods
add_test_case(route)
click to toggle source
# File lib/perf_check.rb, line 68 def add_test_case(route) test_cases.push(TestCase.new(self, route.sub(/^([^\/])/, '/\1'))) end
before_start(&block)
click to toggle source
# File lib/perf_check/callbacks.rb, line 12 def before_start(&block) @before_start_callbacks ||= [] @before_start_callbacks << block end
before_start_callbacks()
click to toggle source
# File lib/perf_check/callbacks.rb, line 17 def before_start_callbacks (@before_start_callbacks || []) + [ proc { |perf_check| perf_check.logger.info("=" * 77) perf_check.logger.info("PERRRRF CHERRRK! Grab a ☕️ and don't touch your working tree (we automate git)") perf_check.logger.info("=" * 77) } ] end
load_config()
click to toggle source
# File lib/perf_check.rb, line 47 def load_config File.stat(File.join(app_root,'Gemfile')).file? if File.exists?("#{app_root}/config/perf_check.rb") this = self Kernel.send(:define_method, :perf_check){ this } dir = Dir.pwd begin Dir.chdir(app_root) load "#{app_root}/config/perf_check.rb" rescue LoadError => e error = ConfigLoadError.new(e.message) error.set_backtrace(e.backtrace) raise error ensure Dir.chdir(dir) Kernel.send(:remove_method, :perf_check) end end end
option_parser()
click to toggle source
# File lib/perf_check/config.rb, line 12 def option_parser @optparse ||= OptionParser.new do |opts| opts.banner = "Usage: perf_check [options] [route ...]" opts.separator "\nBenchmark options:" opts.on('--requests N', '-n', 'Use N requests in benchmark, defaults to 20') do |n| options.number_of_requests = n.to_i end opts.on('--reference COMMIT', '-r', 'Benchmark against COMMIT instead of master') do |commit| options.reference = commit end opts.on('--branch COMMIT', '-branch', 'Set the current branch to benchmark against (defaults to the branch you currently have checked out)') do |branch| options.branch = branch end opts.on('--quick', '-q', '20 requests just on this branch (no comparison with master)') do options.reference = nil end opts.on('--compare-paths', 'Compare two paths against each other on the same branch') do options[:compare_paths?] = true end opts.on('--302-success', 'Consider HTTP 302 code a successful request') do options.http_statuses.push(302) end opts.on('--302-failure', 'Consider HTTP 302 code an unsuccessful request') do options.http_statuses.delete(302) end opts.separator "\nRails environment" opts.on('--deployment','Use git fetch/reset instead of the safe/friendly checkout') do options.hard_reset = true end opts.on('--environment', '-e', 'Change the rails environment we are profiling. Defaults to development') do |env| options.environment = env end opts.on('--no-caching', 'Do not enable fragment caching (Rails.cache will still work)') do options.caching = false end opts.separator "\nMisc" opts.on('-h', 'Display this help') do # Do nothing, just don't error end opts.on('--run-migrations', 'Run migrations on the branch and unmigrate at the end') do options[:run_migrations?] = true end opts.on('--cookie COOKIE', '-c') do |cookie| options.cookie = cookie end opts.on('--header HEADER', '-H') do |header| key, value = header.split(':', 2) options.headers[key.strip] = value.strip end opts.on('--input FILE', '-i') do |input| File.readlines(input).each do |resource| ARGV << resource.strip end end opts.on('--brief', '-b') do options.brief = true end opts.on('--verify-no-diff', 'Check whether there is a diff between the responses of this and the reference branch') do options.verify_no_diff = true end opts.on('--diff') do options.diff = true options.brief = true options.verify_no_diff = true options.number_of_requests = 1 end opts.on("--diff-option OPT") do |opt| options.diff_options << opt end opts.separator '' opts.separator <<EOF Usage examples: Benchmark PostController#index against master perf_check /user/45/posts perf_check /user/45/posts -n5 Benchmark against a specific commit perf_check /user/45/posts -r 0123abcdefg perf_check /user/45/posts -r HEAD~2 Benchmark the changes in the working tree perf_check /user/45/posts -r HEAD Benchmark and diff the output against master perf_check /user/45/posts --verify-no-diff Just diff the output on your branch with master perf_check /user/45/posts --diff Diff a bunch of urls listed in a file (newline seperated) perf_check --diff --input FILE EOF opts.separator '' end end
parse_arguments(argv)
click to toggle source
# File lib/perf_check/config.rb, line 5 def parse_arguments(argv) options.argv = argv.is_a?(String) ? Shellwords.shellsplit(argv) : argv option_parser.parse(options.argv).each do |route| add_test_case(route.strip) end end
print_brief_results()
click to toggle source
# File lib/perf_check/output.rb, line 11 def print_brief_results test_cases.each do |test| print(test.resource.ljust(40) + ': ') codes = (test.this_profiles+test.reference_profiles).map(&:response_code).uniq print("(HTTP "+codes.join(',')+") ") printf('%.1fms', test.this_latency) puts && next if test.reference_profiles.empty? print(sprintf(' (%+5.1fms)', test.latency_difference).bold) print_diff_results(test.response_diff) if options.verify_no_diff puts end end
print_diff_results(diff)
click to toggle source
# File lib/perf_check/output.rb, line 3 def print_diff_results(diff) if diff.changed? print(" Diff: #{diff.file}".bold.light_red) else print(" Diff: Output is identical!".bold.light_green) end end
print_full_results()
click to toggle source
# File lib/perf_check/output.rb, line 59 def print_full_results puts("==== Results ====") test_cases.each do |test| puts(test.resource.bold) if test.reference_profiles.empty? printf("your branch: ".rjust(15)+"%.1fms\n", test.this_latency) next end reference_latency_output = latency_output(test.reference_latency) test_latency_output = latency_output(test.this_latency) latency_difference = test.latency_difference reference_latency = test.reference_latency test_latency = test.this_latency change_factor = change_factor( latency_difference, reference_latency, test_latency ) change_factor_output = sprintf('%.1fx', change_factor) percent_change = calculate_percent_change(latency_difference, reference_latency) formatted_change, color = formatted_change_and_color(change_factor_output, percent_change, latency_difference) formatted_change = latency_output(latency_difference) + " (#{formatted_change})" print_results(reference_latency_output, test_latency_output, formatted_change, color) print_diff_results(test.response_diff) if options.verify_no_diff end end
print_results_of_compared_paths()
click to toggle source
# File lib/perf_check/output.rb, line 28 def print_results_of_compared_paths puts("==== Results ====") first_test = test_cases[0] second_test = test_cases[1] test_latency = first_test.this_latency reference_latency = second_test.reference_latency test_latency_output = latency_output(test_latency) reference_latency_output = latency_output(reference_latency) puts("reference path:" + first_test.resource.bold) puts("test path:" + second_test.resource.bold) latency_difference = test_latency - reference_latency change_factor = change_factor(latency_difference, reference_latency, test_latency) change_factor_output = sprintf('%.1fx', change_factor) percent_change = calculate_percent_change(latency_difference, reference_latency) formatted_change, color = formatted_change_and_color( change_factor_output, percent_change, latency_difference ) formatted_change = latency_output(latency_difference) + " (#{formatted_change})" print_results(reference_latency_output, test_latency, formatted_change, color) end
run()
click to toggle source
# File lib/perf_check.rb, line 72 def run begin if options.compare_paths? raise "Must have two paths" if test_cases.count != 2 profile_compare_paths_requests else profile_requests if options.reference git.stash_if_needed git.checkout(options.reference, bundle_after_checkout: true, hard_reset: options.hard_reset) test_cases.each{ |x| x.switch_to_reference_context } profile_requests end end ensure server.exit if options.reference git.checkout(git.current_branch, bundle_after_checkout: true, hard_reset: options.hard_reset) git.pop if git.stashed? end callbacks = {} if $! callbacks[:error_message] = "#{$!.class}: #{$!.message}\n" callbacks[:error_message] << $!.backtrace.map{|x| "\t#{x}"}.join("\n") end trigger_when_finished_callbacks(callbacks) end end
trigger_before_start_callbacks(test_case)
click to toggle source
# File lib/perf_check/callbacks.rb, line 28 def trigger_before_start_callbacks(test_case) before_start_callbacks.each{ |f| f.call(self, test_case) } end
trigger_when_finished_callbacks(data={})
click to toggle source
# File lib/perf_check/callbacks.rb, line 32 def trigger_when_finished_callbacks(data={}) data = data.merge(:current_branch => git.current_branch) results = OpenStruct.new(data) if test_cases.size == 1 results.current_latency = test_cases.first.this_latency results.reference_latency = test_cases.first.reference_latency end when_finished_callbacks.each{ |f| f.call(self, results) } end
when_finished(&block)
click to toggle source
# File lib/perf_check/callbacks.rb, line 3 def when_finished(&block) @when_finished_callbacks ||= [] @when_finished_callbacks << block end
when_finished_callbacks()
click to toggle source
# File lib/perf_check/callbacks.rb, line 8 def when_finished_callbacks @when_finished_callbacks || [] end
Private Instance Methods
calculate_percent_change(latency_difference, reference_latency)
click to toggle source
# File lib/perf_check/output.rb, line 127 def calculate_percent_change(latency_difference, reference_latency) 100*(latency_difference / reference_latency).abs end
change_factor(latency_difference, reference_latency, test_latency)
click to toggle source
# File lib/perf_check/output.rb, line 99 def change_factor(latency_difference, reference_latency, test_latency) if latency_difference < 0 reference_latency / test_latency else test_latency / reference_latency end end
formatted_change_and_color(change_factor_output, percent_change, latency_difference)
click to toggle source
# File lib/perf_check/output.rb, line 113 def formatted_change_and_color(change_factor_output, percent_change, latency_difference) if percent_change < 10 formatted_change = "yours is about the same" color = :blue elsif latency_difference < 0 formatted_change = "yours is #{change_factor_output} faster!" color = :green else formatted_change = "yours is #{change_factor_output} slower!!!" color = :light_red end [formatted_change, color] end
latency_output(latency)
click to toggle source
# File lib/perf_check/output.rb, line 95 def latency_output(latency) sprintf('%.1fms', latency) end
print_results(reference_latency, test_latency, formatted_change, color)
click to toggle source
# File lib/perf_check/output.rb, line 107 def print_results(reference_latency, test_latency, formatted_change, color) puts("reference: ".rjust(15) + "#{reference_latency}") puts("your branch: ".rjust(15)+ "#{test_latency}") puts(("change: ".rjust(15) + "#{formatted_change}").bold.send(color)) end
profile_compare_paths_requests()
click to toggle source
# File lib/perf_check.rb, line 107 def profile_compare_paths_requests first = test_cases[0] reference_test = test_cases[1] profile_test_case(first) reference_test.switch_to_reference_context profile_test_case(reference_test) end
profile_requests()
click to toggle source
# File lib/perf_check.rb, line 134 def profile_requests test_cases.each do |test| profile_test_case(test) end end
profile_test_case(test)
click to toggle source
# File lib/perf_check.rb, line 115 def profile_test_case(test) trigger_before_start_callbacks(test) run_migrations_up if options.run_migrations? server.restart test.cookie = options.cookie if options.diff logger.info("Issuing #{test.resource}") else logger.info '' logger.info("Benchmarking #{test.resource}:") end test.run(server, options) ensure run_migrations_down if options.run_migrations? end
run_migrations_down()
click to toggle source
# File lib/perf_check.rb, line 148 def run_migrations_down Bundler.with_original_env do git.migrations_to_run_down.each do |version| logger.info "Running db:migrate:down VERSION=#{version}" logger.info `cd #{app_root} && bundle exec rake db:migrate:down VERSION=#{version}` end end git.clean_db end
run_migrations_up()
click to toggle source
# File lib/perf_check.rb, line 140 def run_migrations_up logger.info "Running db:migrate" Bundler.with_original_env do logger.info `cd #{app_root} && bundle exec rake db:migrate` end git.clean_db end