rails_api_benchmark 0.1.1 → 0.2.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: 16e76dc43a1a8457ffc51885a838b224bbb2f37d
4
- data.tar.gz: 44020a8c4cd7bae78d0c758653d1f45f0244b9ba
3
+ metadata.gz: a90d0eb56db8b3b59f332bddfc6922a04d665bf0
4
+ data.tar.gz: 5da05eee4add84cc3ad4de8121cdbfaf912a1a14
5
5
  SHA512:
6
- metadata.gz: 36f8f6ec137d2227ef6b9b9b7bc7759d0c4068aa7c094fad84c1d5c2226a9f54e4a0ac04b0ba10a4b3ed8f7885cf60f3a1b868070578c64ec99807a7b8e00d65
7
- data.tar.gz: 57695424f8624fa90b5117b63e050fdd79c22d38ba2518612423e18a43e73f1eafed84cd5dadc8050975cf018f50320ed698e9a2199d91f75ba4637b77f7b265
6
+ metadata.gz: 02d6a923a95a529fcc83552f5e40b6988cb2b22449ee964af315bd867e69c45521e43d146a3251f54d4d623fdf84d6670272956ec2d83d8c346ea1ffd8b9de9d
7
+ data.tar.gz: 0fda977c19ab3012a0b2ba4a47c1a3d3f5a96e72770a5e17a1c2e567b345416da3b30d5dbb05dfc05a238499bb5fb2b3232e21c9e46dd265616ba3f6e2f3e7fe
data/README.md CHANGED
@@ -6,6 +6,12 @@ Work in progress, yet you can use it like this.
6
6
 
7
7
  Run it with rake api:benchmark
8
8
 
9
+ ## Important
10
+
11
+ * Only JSON responses are supported yet
12
+ * Only GET requests are supported yet
13
+ * Configuration is not validated
14
+
9
15
  ## Installation
10
16
 
11
17
  Install gnuplot (Google)
@@ -47,9 +53,11 @@ unless Rails.env.production?
47
53
  }
48
54
  config.regexps = [ # Used to get results from the output of benchmark tools
49
55
  {
56
+ key: :response_time,
50
57
  name: 'Average time per request (ms)',
51
58
  regexp: /Time\s+per\s+request:\s+([0-9.]*).*\(mean\)/
52
59
  }, {
60
+ key: :req_per_sec,
53
61
  name: 'Requests per second (#)',
54
62
  regexp: /Requests\s+per\s+second:\s+([0-9.]*).*\(mean\)/
55
63
  }
@@ -82,8 +90,6 @@ The gem is available as open source under the terms of the [MIT License](http://
82
90
 
83
91
  ### TODO
84
92
  * POST requests
85
- * Create Formatter class to avoid dirty code in Endpoint
86
- * Create markdown template to embed nicely on github (Mustache)
87
93
  * Create generators (for config initializer first)
88
94
  * Add simplecov to permit controller coverage for example
89
95
  * Generate documentation page(s) (markdown) to list the results
@@ -10,7 +10,8 @@ module RailsApiBenchmark
10
10
  RailsApiBenchmark::Server.run
11
11
  sleep(3) # Leave time to boot
12
12
  RailsApiBenchmark::Core.run
13
- RailsApiBenchmark::Subprocess.kill_all
13
+
14
+ at_exit { RailsApiBenchmark::Subprocess.kill_all }
14
15
  end
15
16
  end
16
17
  end
@@ -4,15 +4,36 @@ module RailsApiBenchmark
4
4
  class Core
5
5
  include Logging
6
6
 
7
- class << self
8
- def run
9
- logger.info 'Begin benchmark...'
10
- RailsApiBenchmark.config.routes.each do |route|
11
- puts route[:name]
12
- Endpoint.new(route).benchmark
13
- Endpoint.new(route).query
14
- end
15
- logger.info 'DONE!'
7
+ def self.run
8
+ new.run
9
+ end
10
+
11
+ def initialize
12
+ @config = RailsApiBenchmark.config
13
+ end
14
+
15
+ def run
16
+ init_files
17
+ @config.routes.each do |route|
18
+ e = Endpoint.new(route)
19
+ res = e.run_benchmark
20
+ write_results(e, res)
21
+ end
22
+ create_index
23
+ end
24
+
25
+ def write_results(endpoint, results)
26
+ Renderer.new(endpoint, results).process
27
+ end
28
+
29
+ def init_files
30
+ FileUtils.mkdir_p(@config.results_folder)
31
+ end
32
+
33
+ def create_index
34
+ view = Views::IndexMarkdown.new(@config.routes)
35
+ File.open(File.join(@config.results_folder, view.file_name), 'w') do |file|
36
+ file << view.render
16
37
  end
17
38
  end
18
39
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
4
+
3
5
  module RailsApiBenchmark
4
6
  class Endpoint
5
7
  include Logging
@@ -11,11 +13,16 @@ module RailsApiBenchmark
11
13
  @route = opts[:route]
12
14
  @method = opts[:method]
13
15
  @title = opts[:title]
14
- @response = nil
15
- @results = []
16
+ @results = {}
16
17
  @results_folder = "#{RailsApiBenchmark.config.results_folder}/#{@name}"
17
18
  end
18
19
 
20
+ def run_benchmark
21
+ query
22
+ benchmark
23
+ @results
24
+ end
25
+
19
26
  def benchmark
20
27
  benchmark_cmd = RailsApiBenchmark.config.bench_cmd
21
28
  opts = RailsApiBenchmark.config.all.merge(route: @route)
@@ -23,14 +30,14 @@ module RailsApiBenchmark
23
30
  output = `#{benchmark_cmd % opts}`
24
31
 
25
32
  regexps = RailsApiBenchmark.config.regexps
26
- regexps.each { |r| @results << output.scan(r[:regexp]) }
33
+ regexps.each { |r| @results[r[:key]] = output.scan(r[:regexp]).flatten.first }
27
34
  end
28
35
 
29
36
  def query
30
37
  curl_cmd = RailsApiBenchmark.config.curl_cmd
31
38
  opts = RailsApiBenchmark.config.all.merge(route: @route)
32
39
 
33
- @response = `#{curl_cmd % opts}`
40
+ @results[:response] = JSON.pretty_generate(JSON.parse(`#{curl_cmd % opts}`))
34
41
  end
35
42
 
36
43
  private
@@ -0,0 +1,23 @@
1
+ module RailsApiBenchmark
2
+ class Graph
3
+ def initialize(target, output_dir)
4
+ @output_dir = output_dir
5
+ @target = target
6
+ end
7
+
8
+ def generate
9
+ run
10
+ copy_file
11
+ end
12
+
13
+ def run
14
+ gnuplotscript = File.expand_path('../../../gnuplotscript', __FILE__)
15
+ `gnuplot -e "plot_title='Benchmark #{@target.title}'; plot_file='#{@target.name}_plot.jpg'" #{gnuplotscript}`
16
+ end
17
+
18
+ def copy_file
19
+ dest = File.join(@output_dir)
20
+ FileUtils.mv("#{@target.name}_plot.jpg", dest)
21
+ end
22
+ end
23
+ end
@@ -1,15 +1,3 @@
1
- # module RailsApiBenchmark
2
- # module Logging
3
- # def logger
4
- # Logging.logger
5
- # end
6
- #
7
- # def self.logger
8
- # @logger ||= Logger.new(STDOUT)
9
- # end
10
- # end
11
- # end
12
-
13
1
  module RailsApiBenchmark
14
2
  module Logging
15
3
  class << self
@@ -20,7 +8,6 @@ module RailsApiBenchmark
20
8
  attr_writer :logger
21
9
  end
22
10
 
23
- # Addition
24
11
  def self.included(base)
25
12
  class << base
26
13
  def logger
@@ -0,0 +1,27 @@
1
+ module RailsApiBenchmark
2
+ class Renderer
3
+ attr_reader :output_dir, :target, :results
4
+
5
+ def initialize(target, results)
6
+ @output_dir = RailsApiBenchmark.config.results_folder
7
+ @target = target
8
+ @results = results
9
+ end
10
+
11
+ def process
12
+ Graph.new(@target, @output_dir).generate
13
+ generate_view
14
+ end
15
+
16
+ private
17
+
18
+ def generate_view
19
+ view = Views::ResultsMarkdown.new(target, results)
20
+ dest = File.join(@output_dir, view.file_path)
21
+ FileUtils.mkdir_p(File.dirname(dest))
22
+ File.open(File.join(@output_dir, view.file_path), 'w') do |file|
23
+ file << view.render
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ require 'mustache'
2
+
3
+ module RailsApiBenchmark
4
+ module Views
5
+ class ResultsMarkdown < View
6
+ attr_reader :title, :req_per_sec, :response_time
7
+
8
+ def initialize(target, results)
9
+ super
10
+ @title = target.title
11
+ @req_per_sec = results[:req_per_sec] || 'Unknown'
12
+ @response_time = results[:response_time] || 'Unknown'
13
+ end
14
+
15
+ # Maybe put this in a superclass like MarkdownView to DRY
16
+ def extension
17
+ 'md'
18
+ end
19
+
20
+ def file_name
21
+ 'results'
22
+ end
23
+
24
+ def folder
25
+ @target.name
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ # Benchmark results:
2
+
3
+ {{#config}}
4
+ **Configuration:**
5
+ - Concurrency: {{concurrency}}
6
+ - Request nb: {{nb_requests}}
7
+ - Host: {{host}}
8
+ {{/config}}
9
+
10
+ ## Endpoints tested ({{nb_routes}})
11
+
12
+ {{#routes}}
13
+ {{#.}}
14
+ ### {{title}}
15
+ * Name: {{name}}
16
+
17
+ * Route: {{route}}
18
+
19
+ {{#description}}
20
+ * Description: {{description}}
21
+ {{/description}}
22
+
23
+ [See results]({{name}}/results.md)
24
+
25
+ {{/.}}
26
+ {{/routes}}
@@ -0,0 +1,21 @@
1
+ # {{title}}
2
+
3
+ ### Results
4
+
5
+ {{#results}}
6
+ Requests per second: {{req_per_sec}} #
7
+
8
+ Average response time: {{response_time}} ms
9
+ {{/results}}
10
+
11
+ {{#target}}
12
+ ### Graph
13
+ ![{{title}}](../{{name}}_plot.jpg?raw=true "{{title}}")
14
+ {{/target}}
15
+
16
+ ### Response
17
+ {{#results}}
18
+ ```json
19
+ {{{response}}}
20
+ ```
21
+ {{/results}}
@@ -1,3 +1,3 @@
1
1
  module RailsApiBenchmark
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
@@ -0,0 +1,22 @@
1
+ require 'mustache'
2
+
3
+ module RailsApiBenchmark
4
+ module Views
5
+ class IndexMarkdown < View
6
+ attr_reader :routes, :nb_routes, :config
7
+
8
+ def initialize(routes)
9
+ super
10
+ @file_name = 'README'
11
+ @config = RailsApiBenchmark.config.all
12
+ @routes = routes
13
+ @nb_routes = routes.count
14
+ end
15
+
16
+ # Maybe put this in a superclass like MarkdownView to DRY
17
+ def extension
18
+ 'md'
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ require 'mustache'
2
+
3
+ module RailsApiBenchmark
4
+ module Views
5
+ class ResultsMarkdown < View
6
+ attr_reader :title, :results, :target
7
+
8
+ def initialize(target, results)
9
+ super
10
+ @file_name = 'results'
11
+ @target = target
12
+ @results = results
13
+ end
14
+
15
+ # Maybe put this in a superclass like MarkdownView to DRY
16
+ def extension
17
+ 'md'
18
+ end
19
+
20
+ def folder
21
+ @target.name
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,37 @@
1
+ require 'mustache'
2
+
3
+ module RailsApiBenchmark
4
+ module Views
5
+ class View < Mustache
6
+ def initialize(*_args)
7
+ @template_path = File.expand_path('../../templates', __FILE__)
8
+ end
9
+
10
+ def file_name
11
+ "#{@file_name}.#{extension}"
12
+ end
13
+
14
+ def file_path
15
+ [folder, file_name].compact.join('/')
16
+ end
17
+
18
+ def folder
19
+ nil
20
+ end
21
+
22
+ private
23
+
24
+ def template_name # Avoid module
25
+ self.class.name
26
+ .split('::').last
27
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
28
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
29
+ .tr('-', '_')
30
+ .downcase
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ require_relative 'results_markdown'
37
+ require_relative 'index_markdown'
@@ -1,8 +1,11 @@
1
1
  require 'rails_api_benchmark/logging'
2
2
  require 'rails_api_benchmark/server'
3
3
  require 'rails_api_benchmark/core'
4
+ require 'rails_api_benchmark/renderer'
5
+ require 'rails_api_benchmark/graph'
4
6
  require 'rails_api_benchmark/subprocess'
5
7
  require 'rails_api_benchmark/endpoint'
8
+ require 'rails_api_benchmark/views/view' # Requires all the views
6
9
 
7
10
  module RailsApiBenchmark
8
11
  class Config
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_api_benchmark
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Terry Raimondo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-12 00:00:00.000000000 Z
11
+ date: 2017-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -59,10 +59,18 @@ files:
59
59
  - lib/rails_api_benchmark/benchmark_tasks.rb
60
60
  - lib/rails_api_benchmark/core.rb
61
61
  - lib/rails_api_benchmark/endpoint.rb
62
+ - lib/rails_api_benchmark/graph.rb
62
63
  - lib/rails_api_benchmark/logging.rb
64
+ - lib/rails_api_benchmark/renderer.rb
65
+ - lib/rails_api_benchmark/results_markdown.rb
63
66
  - lib/rails_api_benchmark/server.rb
64
67
  - lib/rails_api_benchmark/subprocess.rb
68
+ - lib/rails_api_benchmark/templates/index_markdown.mustache
69
+ - lib/rails_api_benchmark/templates/results_markdown.mustache
65
70
  - lib/rails_api_benchmark/version.rb
71
+ - lib/rails_api_benchmark/views/index_markdown.rb
72
+ - lib/rails_api_benchmark/views/results_markdown.rb
73
+ - lib/rails_api_benchmark/views/view.rb
66
74
  homepage: https://github.com/terry90/rails_api_benchmark
67
75
  licenses:
68
76
  - MIT