activerecord-analyze 0.1.0 → 0.5.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
- SHA1:
3
- metadata.gz: 2c1961ed7400cf716abcc28764176bdf02b4c3eb
4
- data.tar.gz: c982bed6eb37c9413bbec9785416f96b9c55490c
2
+ SHA256:
3
+ metadata.gz: 1f3e8c5511121ff7c9a7e2aab56d1638e1f42286477bfb97a72ae6d284f719b1
4
+ data.tar.gz: 0a6eba3afdf108a616ad837d9520148a2a387ab0ab3441afbbe2e9851e974e1e
5
5
  SHA512:
6
- metadata.gz: 5f57e1be57c643f06a0ba3036a0e2709ce9ac799c3a02ca7a0c1cce99d92340b9b0a7d574f8c8652d52ee522a3c5edbccec8ea5f8d1d8fbd21468c1c20372ec7
7
- data.tar.gz: 432321449fdff40d5f410cb635b4f20448437c471526c87de4eaa25782cae474a816abf4f64a871804a8009d8d80e68989cac75fa09d342b7fa590ac89d59163
6
+ metadata.gz: 94223f7a2f15534a7b1d648cb5a019f4aeab0efef044a104ff70e4ee876cdc332de67dfdc86fb325aac5b847a515ae6b02846fed2a19c1b342df4e43b14c7e3d
7
+ data.tar.gz: 14aa9186a5e14d00dc6b6475d9d7052e00942d82bb5deafcb14f7c170a80282216c05597e102e9ce55c934807ceee145462d6f416ece2030ae21cfbbd495f3d8
data/README.md CHANGED
@@ -1,2 +1,112 @@
1
1
  # ActiveRecord Analyze
2
2
 
3
+ This gem adds an `analyze` method to Active Record query objects. It executes `EXPLAIN ANALYZE` on a query SQL.
4
+
5
+ You can check out this blog post for more info on how to [debug and fix slow queries in Rails apps](https://pawelurbanek.com/slow-rails-queries).
6
+
7
+ ## Installation
8
+
9
+ In your Gemfile:
10
+
11
+ ```ruby
12
+
13
+ gem 'activerecord-analyze'
14
+
15
+ ```
16
+
17
+ ## Options
18
+
19
+ The `analyze` method supports the following EXPLAIN query options ([PostgreSQL docs reference](https://www.postgresql.org/docs/12/sql-explain.html)):
20
+
21
+ ```
22
+ VERBOSE [ boolean ]
23
+ COSTS [ boolean ]
24
+ SETTINGS [ boolean ]
25
+ BUFFERS [ boolean ]
26
+ TIMING [ boolean ]
27
+ SUMMARY [ boolean ]
28
+ FORMAT { TEXT | XML | JSON | YAML }
29
+ ```
30
+
31
+ You can execute it like that:
32
+
33
+ ```ruby
34
+
35
+ User.all.analyze(
36
+ format: :json,
37
+ verbose: true,
38
+ costs: true,
39
+ settings: true,
40
+ buffers: true,
41
+ timing: true,
42
+ summary: true
43
+ )
44
+
45
+ # EXPLAIN (FORMAT JSON, ANALYZE, VERBOSE, COSTS, SETTINGS, BUFFERS, TIMING, SUMMARY)
46
+ # SELECT "users".* FROM "users"
47
+ # [
48
+ # {
49
+ # "Plan": {
50
+ # "Node Type": "Seq Scan",
51
+ # "Parallel Aware": false,
52
+ # "Relation Name": "users",
53
+ # "Schema": "public",
54
+ # "Alias": "users",
55
+ # "Startup Cost": 0.00,
56
+ # "Total Cost": 11.56,
57
+ # "Plan Rows": 520,
58
+ # "Plan Width": 127,
59
+ # "Actual Startup Time": 0.006,
60
+ # "Actual Total Time": 0.007,
61
+ # "Actual Rows": 2,
62
+ # "Actual Loops": 1,
63
+ # "Output": ["id", "team_id", "email"],
64
+ # "Shared Hit Blocks": 1,
65
+ # "Shared Read Blocks": 0,
66
+ # "Shared Dirtied Blocks": 0,
67
+ # "Shared Written Blocks": 0,
68
+ # "Local Hit Blocks": 0,
69
+ # "Local Read Blocks": 0,
70
+ # "Local Dirtied Blocks": 0,
71
+ # "Local Written Blocks": 0,
72
+ # "Temp Read Blocks": 0,
73
+ # "Temp Written Blocks": 0,
74
+ # "I/O Read Time": 0.000,
75
+ # "I/O Write Time": 0.000
76
+ # },
77
+ # "Settings": {
78
+ # "cpu_index_tuple_cost": "0.001",
79
+ # "cpu_operator_cost": "0.0005",
80
+ # "cpu_tuple_cost": "0.003",
81
+ # "effective_cache_size": "10800000kB",
82
+ # "max_parallel_workers_per_gather": "1",
83
+ # "random_page_cost": "2",
84
+ # "work_mem": "100MB"
85
+ # },
86
+ # "Planning Time": 0.033,
87
+ # "Triggers": [
88
+ # ],
89
+ # "Execution Time": 0.018
90
+ # }
91
+ # ]
92
+
93
+ ```
94
+
95
+ The following `format` options are supported `:json, :hash, :yaml, :text, :xml`. Especially the `:json` format is useful because it allows you to visualize a query plan using [a visualizer tool](https://tatiyants.com/pev/#/plans/new).
96
+
97
+ Optionally you can disable running the `ANALYZE` query and only generate the plan:
98
+
99
+ ```ruby
100
+
101
+ User.all.analyze(analyze: false)
102
+
103
+ # EXPLAIN ANALYZE for: SELECT "users".* FROM "users"
104
+ # QUERY PLAN
105
+ # ----------------------------------------------------------
106
+ # Seq Scan on users (cost=0.00..15.20 rows=520 width=127)
107
+
108
+ ```
109
+
110
+ ### Disclaimer
111
+
112
+ It is a bit experimental and can break with new Rails release.
@@ -15,5 +15,5 @@ Gem::Specification.new do |gem|
15
15
  gem.test_files = gem.files.grep(%r{^(spec)/})
16
16
  gem.require_paths = ["lib"]
17
17
  gem.license = "MIT"
18
- gem.add_dependency "rails", "~> 5.1.0"
18
+ gem.add_dependency "rails", ">= 5.1.0"
19
19
  end
@@ -1,2 +1,4 @@
1
+ require 'activerecord-analyze/main'
2
+
1
3
  module ActiveRecordAnalyze
2
4
  end
@@ -2,9 +2,60 @@ module ActiveRecord
2
2
  module ConnectionAdapters
3
3
  module PostgreSQL
4
4
  module DatabaseStatements
5
- def analyze(arel, binds = [])
6
- sql = "EXPLAIN ANALYZE #{to_sql(arel, binds)}"
7
- PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN ANALYZE", binds))
5
+ def analyze(arel, binds = [], opts = {})
6
+ format_sql = if fmt = opts[:format].presence
7
+ case fmt
8
+ when :json
9
+ "FORMAT JSON,"
10
+ when :hash
11
+ "FORMAT JSON,"
12
+ when :yaml
13
+ "FORMAT YAML,"
14
+ when :text
15
+ "FORMAT TEXT,"
16
+ when :xml
17
+ "FORMAT XML,"
18
+ end
19
+ end
20
+
21
+ verbose_sql = if opts[:verbose] == true
22
+ ", VERBOSE"
23
+ end
24
+
25
+ costs_sql = if opts[:costs] == true
26
+ ", COSTS"
27
+ end
28
+
29
+ settings_sql = if opts[:settings] == true
30
+ ", SETTINGS"
31
+ end
32
+
33
+ buffers_sql = if opts[:buffers] == true
34
+ ", BUFFERS"
35
+ end
36
+
37
+ timing_sql = if opts[:timing] == true
38
+ ", TIMING"
39
+ end
40
+
41
+ summary_sql = if opts[:summary] == true
42
+ ", SUMMARY"
43
+ end
44
+
45
+ analyze_sql = if opts[:analyze] == false
46
+ ""
47
+ else
48
+ "ANALYZE"
49
+ end
50
+
51
+ opts_sql = "(#{format_sql} #{analyze_sql}#{verbose_sql}#{costs_sql}#{settings_sql}#{buffers_sql}#{timing_sql}#{summary_sql})"
52
+ .strip.gsub(/\s+/, " ")
53
+ .gsub(/\(\s?\s?\s?,/, "(")
54
+ .gsub(/\s,\s/, " ")
55
+ .gsub(/\(\s?\)/, "")
56
+
57
+ sql = "EXPLAIN #{opts_sql} #{to_sql(arel, binds)}"
58
+ PostgreSQL::ExplainPrettyPrinter.new.pp(exec_query(sql, "EXPLAIN #{opts_sql}".strip, binds))
8
59
  end
9
60
  end
10
61
  end
@@ -13,23 +64,40 @@ end
13
64
 
14
65
  module ActiveRecord
15
66
  class Relation
16
- def analyze
17
- exec_analyze(collecting_queries_for_explain { exec_queries })
67
+ def analyze(opts = {})
68
+ res = exec_analyze(collecting_queries_for_explain { exec_queries }, opts)
69
+ if [:json, :hash].include?(opts[:format])
70
+ raw_json = "[" + res[/\[(.*?)(\(\d row)/m, 1]
71
+
72
+ if opts[:format] == :json
73
+ JSON.parse(raw_json).to_json
74
+ elsif opts[:format] == :hash
75
+ JSON.parse(raw_json)
76
+ end
77
+ else
78
+ res
79
+ end
18
80
  end
19
81
  end
20
82
  end
21
83
 
22
84
  module ActiveRecord
23
85
  module Explain
24
- def exec_analyze(queries) # :nodoc:
86
+ def exec_analyze(queries, opts = {}) # :nodoc:
25
87
  str = queries.map do |sql, binds|
26
- msg = "EXPLAIN ANALYZE for: #{sql}".dup
88
+ analyze_msg = if opts[:analyze] == false
89
+ ""
90
+ else
91
+ " ANALYZE"
92
+ end
93
+
94
+ msg = "EXPLAIN#{analyze_msg} for: #{sql}".dup
27
95
  unless binds.empty?
28
96
  msg << " "
29
97
  msg << binds.map { |attr| render_bind(attr) }.inspect
30
98
  end
31
99
  msg << "\n"
32
- msg << connection.analyze(sql, binds)
100
+ msg << connection.analyze(sql, binds, opts)
33
101
  end.join("\n")
34
102
 
35
103
  # Overriding inspect to be more human readable, especially in the console.
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordAnalyze
2
- VERSION = "0.1.0"
2
+ VERSION = "0.5.0"
3
3
  end
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-analyze
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pawurb
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-11 00:00:00.000000000 Z
11
+ date: 2021-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 5.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 5.1.0
27
27
  description: ' Gem adds an "analyze" method to all Active Record query objects. Compatible
@@ -45,7 +45,7 @@ homepage: http://github.com/pawurb/activerecord-analyze
45
45
  licenses:
46
46
  - MIT
47
47
  metadata: {}
48
- post_install_message:
48
+ post_install_message:
49
49
  rdoc_options: []
50
50
  require_paths:
51
51
  - lib
@@ -60,9 +60,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  requirements: []
63
- rubyforge_project:
64
- rubygems_version: 2.6.14
65
- signing_key:
63
+ rubygems_version: 3.1.4
64
+ signing_key:
66
65
  specification_version: 4
67
66
  summary: Add EXPLAIN ANALYZE to Active Record query objects
68
67
  test_files: []