rails_api_benchmark 0.1.1 → 0.2.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: 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