rails-pg-extras 4.13.0 → 5.1.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
  SHA256:
3
- metadata.gz: b8b00ae8cb079824ce1de1284ccf30cca423e27d361a1e70a05066688f0ee094
4
- data.tar.gz: e76d91e64b1949a0a92c6255390de16e6e0ce7345835c03d8166db1092173c0f
3
+ metadata.gz: 1f7109cdaf06d94ed2a1707fffcd477a471c84ece3e648e4279f602f27e89b67
4
+ data.tar.gz: 583b204cfad10e9c3602946d5e00d0c10eaf1a7407e7234fc1f22db154e22bab
5
5
  SHA512:
6
- metadata.gz: 8288bd8a17c6a44f179595c7b82efc4c4384b55cbfaebd679a73f37dda8e68fcf80a8f02868f93b82c45382be66c0f8fa81a4d83e72765dc18abf67929f1454b
7
- data.tar.gz: 378693843abc170d78cab4c0da0a2108a7f831f7c48805cb78ec020b1dfc1ded2458d50b04deb884675313a13d1178e7d942c778386109ba9148c2e169869b78
6
+ metadata.gz: a3f334dcdf081777134219ab9f3c6ca0a0e95ca36495d71447690526c71bf893960219bda40d12c82973a2779131ff6977e99a141829f3a4121378b65d8e1659
7
+ data.tar.gz: 8eb2229994c72de036a4cf5e865b2503efcb831dd12c1182e4b45d43956cc89ffecb44f2bb1b7fe7a5ab50b82f4a662ddf2f7bee3756cac054d75639c8c5a6e2
data/README.md CHANGED
@@ -51,7 +51,7 @@ You should see the similar line in the output:
51
51
  RailsPgExtras.add_extensions
52
52
  ```
53
53
 
54
- By deafult a primary ActiveRecord database connection is used for running metadata queries. To connect to a different database you can specify an `ENV['RAILS_PG_EXTRAS_DATABASE_URL']` value in the following format:
54
+ By deafult a primary ActiveRecord database connection is used for running metadata queries, rake tasks and web UI. To connect to a different database you can specify an `ENV['RAILS_PG_EXTRAS_DATABASE_URL']` value in the following format:
55
55
 
56
56
  ```ruby
57
57
  ENV["RAILS_PG_EXTRAS_DATABASE_URL"] = "postgresql://postgres:secret@localhost:5432/database_name"
@@ -140,6 +140,52 @@ end
140
140
 
141
141
  ## Available methods
142
142
 
143
+ ### `measure_queries`
144
+
145
+ This method displays query types executed when running a provided Ruby snippet, with their avg., min., max., and total duration. It also outputs info about the snippet execution duration and the portion spent running SQL queries (`total_duration`/`sql_duration`). It can help debug N+1 issues and review the impact of configuring eager loading:
146
+
147
+ ```ruby
148
+
149
+ User.measure_queries { User.limit(10).map(&:team) }
150
+
151
+ # {:count=>11,
152
+ # :queries=>
153
+ # {"SELECT \"users\".* FROM \"users\" LIMIT $1"=>
154
+ # {:count=>1,
155
+ # :total_duration=>0.003183000022545457,
156
+ # :min_duration=>0.003183000022545457,
157
+ # :max_duration=>0.003183000022545457,
158
+ # :avg_duration=>0.003183000022545457},
159
+ # "SELECT \"teams\".* FROM \"teams\" WHERE \"teams\".\"id\" = $1 LIMIT $2"=>
160
+ # {:count=>10,
161
+ # :total_duration=>0.011682000011205673,
162
+ # :min_duration=>0.0007209999894257635,
163
+ # :max_duration=>0.0024030000204220414,
164
+ # :avg_duration=>0.0011682000011205673}},
165
+ # :total_duration=>0.15247199998702854,
166
+ # :sql_duration=>0.01486500003375113}
167
+
168
+ User.measure_queries { User.limit(10).includes(:team).map(&:team) }
169
+
170
+ # {:count=>2,
171
+ # :queries=>
172
+ # {"SELECT \"users\".* FROM \"users\" LIMIT $1"=>
173
+ # {:count=>1,
174
+ # :total_duration=>0.0036189999955240637,
175
+ # :min_duration=>0.0036189999955240637,
176
+ # :max_duration=>0.0036189999955240637,
177
+ # :avg_duration=>0.0036189999955240637},
178
+ # "SELECT \"teams\".* FROM \"teams\" WHERE \"teams\".\"id\" IN ($1, $2, $3, $4, $5, $6, $7, $8)"=>
179
+ # {:count=>1,
180
+ # :total_duration=>0.003714999998919666,
181
+ # :min_duration=>0.003714999998919666,
182
+ # :max_duration=>0.003714999998919666,
183
+ # :avg_duration=>0.003714999998919666}},
184
+ # :total_duration=>0.023814999993192032,
185
+ # :sql_duration=>0.0073339999944437295}
186
+
187
+ ```
188
+
143
189
  ### `table_info`
144
190
 
145
191
  This method displays metadata metrics for all or a selected table. You can use it to check the table's size, its cache hit metrics, and whether it is correctly indexed. Many sequential scans or no index scans are potential indicators of misconfigured indexes. This method aggregates data provided by other methods in an easy to analyze summary format.
@@ -62,6 +62,61 @@ module RailsPgExtras
62
62
  end
63
63
  end
64
64
 
65
+ def self.measure_duration(&block)
66
+ starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
67
+ block.call
68
+ ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
69
+ elapsed = ending - starting
70
+ elapsed
71
+ end
72
+
73
+ def self.measure_queries(&block)
74
+ queries = {}
75
+ sql_duration = 0
76
+
77
+ method_name = if ActiveSupport::Notifications.respond_to?(:monotonic_subscribe)
78
+ :monotonic_subscribe
79
+ else
80
+ :subscribe
81
+ end
82
+
83
+ subscriber = ActiveSupport::Notifications.public_send(method_name, "sql.active_record") do |_name, start, finish, _id, payload|
84
+ unless payload[:name] =~ /SCHEMA/
85
+ key = payload[:sql]
86
+ queries[key] ||= { count: 0, total_duration: 0, min_duration: nil, max_duration: nil }
87
+ queries[key][:count] += 1
88
+ duration = finish - start
89
+ queries[key][:total_duration] += duration
90
+ sql_duration += duration
91
+
92
+ if queries[key][:min_duration] == nil || queries[key][:min_duration] > duration
93
+ queries[key][:min_duration] = duration
94
+ end
95
+
96
+ if queries[key][:max_duration] == nil || queries[key][:max_duration] < duration
97
+ queries[key][:max_duration] = duration
98
+ end
99
+ end
100
+ end
101
+
102
+ total_duration = measure_duration do
103
+ block.call
104
+ end
105
+
106
+ queries = queries.reduce({}) do |agg, val|
107
+ val[1][:avg_duration] = val[1][:total_duration] / val[1][:count]
108
+ agg.merge(val[0] => val[1])
109
+ end
110
+
111
+ ActiveSupport::Notifications.unsubscribe(subscriber)
112
+ {
113
+ count: queries.reduce(0) { |agg, val| agg + val[1].fetch(:count) },
114
+ queries: queries,
115
+ total_duration: total_duration,
116
+ sql_duration: sql_duration
117
+ }
118
+ end
119
+
65
120
  def self.index_info(args: {}, in_format: :display_table)
66
121
  data = RailsPgExtras::IndexInfo.call(args[:table_name])
67
122
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsPgExtras
4
- VERSION = "4.13.0"
4
+ VERSION = "5.1.0"
5
5
  end
data/spec/smoke_spec.rb CHANGED
@@ -29,6 +29,11 @@ describe RailsPgExtras do
29
29
  end.not_to raise_error
30
30
  end
31
31
 
32
+ it "collecting queries data works" do
33
+ output = RailsPgExtras.queries_data { RailsPgExtras.diagnose(in_format: :hash) }
34
+ expect(output.fetch(:count)).to eq 10
35
+ end
36
+
32
37
  it "supports custom RAILS_PG_EXTRAS_DATABASE_URL" do
33
38
  ENV['RAILS_PG_EXTRAS_DATABASE_URL'] = ENV['DATABASE_URL']
34
39
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-pg-extras
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.13.0
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - pawurb
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-31 00:00:00.000000000 Z
11
+ date: 2023-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-pg-extras
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 4.13.0
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
- version: 4.13.0
26
+ version: 5.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rails
29
29
  requirement: !ruby/object:Gem::Requirement