rails-pg-extras 4.8.1 → 5.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +83 -2
- data/app/controllers/rails_pg_extras/web/application_controller.rb +1 -1
- data/lib/rails-pg-extras.rb +61 -1
- data/lib/rails_pg_extras/configuration.rb +3 -1
- data/lib/rails_pg_extras/version.rb +1 -1
- data/spec/smoke_spec.rb +15 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4bd83f21b85de2b816dc334b72ad1bd0dfcb0f13ea6946fec42ebb93f47ba876
|
4
|
+
data.tar.gz: 4e9e5b00cc7e07cf3baa1bc3e224fe8ab50510afa909b47e753873fb1e7a11e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1a30823d47f9e0cab719b7ee6e750da28c751f4dc20d21756f7f3a633c07dd6390397e09502300cfcf9185184a9e00af83de40f2fd9b12a6319a6ade31ccb26f
|
7
|
+
data.tar.gz: f01d58920638949122f26a74857cdab232168b274aee2d28d180eed09ea60a48ace036aa3ba772dc8ce41893d6e4a4397255167bc5ccf699a181c7205438ed93
|
data/README.md
CHANGED
@@ -51,6 +51,12 @@ 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, 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
|
+
|
56
|
+
```ruby
|
57
|
+
ENV["RAILS_PG_EXTRAS_DATABASE_URL"] = "postgresql://postgres:secret@localhost:5432/database_name"
|
58
|
+
```
|
59
|
+
|
54
60
|
## Usage
|
55
61
|
|
56
62
|
Each command can be used as a rake task, or a directly from the Ruby code.
|
@@ -119,7 +125,7 @@ You can enable UI using a Rails engine by adding the following code in `config/r
|
|
119
125
|
mount RailsPgExtras::Web::Engine, at: 'pg_extras'
|
120
126
|
```
|
121
127
|
|
122
|
-
You can enable HTTP basic auth by specifying `RAILS_PG_EXTRAS_USER` and `RAILS_PG_EXTRAS_PASSWORD` variables. Authentication is mandatory unless you specify `RAILS_PG_EXTRAS_PUBLIC_DASHBOARD=true`.
|
128
|
+
You can enable HTTP basic auth by specifying `RAILS_PG_EXTRAS_USER` and `RAILS_PG_EXTRAS_PASSWORD` variables. Authentication is mandatory unless you specify `RAILS_PG_EXTRAS_PUBLIC_DASHBOARD=true` or setting `RailsPgExtras.configuration.public_dashboard` to `true`.
|
123
129
|
|
124
130
|
You can configure available web actions in `config/initializers/rails_pg_extras.rb`:
|
125
131
|
|
@@ -134,6 +140,52 @@ end
|
|
134
140
|
|
135
141
|
## Available methods
|
136
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 in miliseconds. 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
|
+
RailsPgExtras.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=>3.452000004472211,
|
156
|
+
:min_duration=>3.452000004472211,
|
157
|
+
:max_duration=>3.452000004472211,
|
158
|
+
:avg_duration=>3.452000004472211},
|
159
|
+
"SELECT \"teams\".* FROM \"teams\" WHERE \"teams\".\"id\" = $1 LIMIT $2"=>
|
160
|
+
{:count=>10,
|
161
|
+
:total_duration=>14.487000000372063,
|
162
|
+
:min_duration=>0.778000001446344,
|
163
|
+
:max_duration=>1.985000002605375,
|
164
|
+
:avg_duration=>1.4487000000372063}},
|
165
|
+
:total_duration=>24.812000003294088,
|
166
|
+
:sql_duration=>17.939000004844274}
|
167
|
+
|
168
|
+
RailsPgExtras.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=>3.7079999965499155,
|
175
|
+
:min_duration=>3.7079999965499155,
|
176
|
+
:max_duration=>3.7079999965499155,
|
177
|
+
:avg_duration=>3.7079999965499155},
|
178
|
+
"SELECT \"teams\".* FROM \"teams\" WHERE \"teams\".\"id\" IN ($1, $2, $3, $4, $5, $6, $7, $8)"=>
|
179
|
+
{:count=>1,
|
180
|
+
:total_duration=>2.422000005026348,
|
181
|
+
:min_duration=>2.422000005026348,
|
182
|
+
:max_duration=>2.422000005026348,
|
183
|
+
:avg_duration=>2.422000005026348}},
|
184
|
+
:total_duration=>9.905999999318738,
|
185
|
+
:sql_duration=>6.1300000015762635}
|
186
|
+
|
187
|
+
```
|
188
|
+
|
137
189
|
### `table_info`
|
138
190
|
|
139
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.
|
@@ -282,7 +334,7 @@ This command provides information on the efficiency of indexes, represented as w
|
|
282
334
|
### `locks`
|
283
335
|
|
284
336
|
```ruby
|
285
|
-
RailsPgExtras.locks
|
337
|
+
RailsPgExtras.locks(args: { limit: 20 })
|
286
338
|
|
287
339
|
$ rake pg_extras:locks
|
288
340
|
|
@@ -630,6 +682,16 @@ RailsPgExtras.kill_all
|
|
630
682
|
|
631
683
|
This commands kills all the currently active connections to the database. It can be useful as a last resort when your database is stuck in a deadlock.
|
632
684
|
|
685
|
+
### `kill_pid`
|
686
|
+
|
687
|
+
```ruby
|
688
|
+
|
689
|
+
RailsPgExtras.kill_pid(args: { pid: 4657 })
|
690
|
+
|
691
|
+
```
|
692
|
+
|
693
|
+
This commands kills currently active database connection by its `pid` number. You can use `connections` method to find the correct `pid` values.
|
694
|
+
|
633
695
|
### `pg_stat_statements_reset`
|
634
696
|
|
635
697
|
```ruby
|
@@ -669,6 +731,25 @@ $ rake pg_extras:extensions
|
|
669
731
|
|
670
732
|
This command lists all the currently installed and available PostgreSQL extensions.
|
671
733
|
|
734
|
+
### `connections`
|
735
|
+
|
736
|
+
```ruby
|
737
|
+
|
738
|
+
RailsPgExtras.connections
|
739
|
+
|
740
|
+
+----------------------------------------------------------------+
|
741
|
+
| Returns the list of all active database connections |
|
742
|
+
+------------------+--------------------------+------------------+
|
743
|
+
| username | pid | client_address | application_name |
|
744
|
+
+------------------+--------------------------+------------------+
|
745
|
+
| postgres | 15962 | 172.31.69.166/32 | sidekiq |
|
746
|
+
| postgres | 16810 | 172.31.69.166/32 | bin/rails |
|
747
|
+
+------------------+--------------------------+------------------+
|
748
|
+
|
749
|
+
```
|
750
|
+
|
751
|
+
This command returns the list of all active database connections.
|
752
|
+
|
672
753
|
### mandelbrot
|
673
754
|
|
674
755
|
```ruby
|
@@ -19,7 +19,7 @@ module RailsPgExtras::Web
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def validate_credentials!
|
22
|
-
if (ENV['RAILS_PG_EXTRAS_USER'].blank? || ENV['RAILS_PG_EXTRAS_PASSWORD'].blank?) &&
|
22
|
+
if (ENV['RAILS_PG_EXTRAS_USER'].blank? || ENV['RAILS_PG_EXTRAS_PASSWORD'].blank?) && !RailsPgExtras.configuration.public_dashboard
|
23
23
|
raise "Missing credentials for rails-pg-extras dashboard! If you want to enable public dashboard please set RAILS_PG_EXTRAS_PUBLIC_DASHBOARD=true"
|
24
24
|
end
|
25
25
|
end
|
data/lib/rails-pg-extras.rb
CHANGED
@@ -62,6 +62,62 @@ 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
|
+
(ending - starting) * 1000
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.measure_queries(&block)
|
73
|
+
queries = {}
|
74
|
+
sql_duration = 0
|
75
|
+
|
76
|
+
method_name = if ActiveSupport::Notifications.respond_to?(:monotonic_subscribe)
|
77
|
+
:monotonic_subscribe
|
78
|
+
else
|
79
|
+
:subscribe
|
80
|
+
end
|
81
|
+
|
82
|
+
subscriber = ActiveSupport::Notifications.public_send(method_name, "sql.active_record") do |_name, start, finish, _id, payload|
|
83
|
+
unless payload[:name] =~ /SCHEMA/
|
84
|
+
key = payload[:sql]
|
85
|
+
queries[key] ||= { count: 0, total_duration: 0, min_duration: nil, max_duration: nil }
|
86
|
+
queries[key][:count] += 1
|
87
|
+
duration = (finish - start) * 1000
|
88
|
+
queries[key][:total_duration] += duration
|
89
|
+
sql_duration += duration
|
90
|
+
|
91
|
+
if queries[key][:min_duration] == nil || queries[key][:min_duration] > duration
|
92
|
+
queries[key][:min_duration] = duration.round(2)
|
93
|
+
end
|
94
|
+
|
95
|
+
if queries[key][:max_duration] == nil || queries[key][:max_duration] < duration
|
96
|
+
queries[key][:max_duration] = duration.round(2)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
total_duration = measure_duration do
|
102
|
+
block.call
|
103
|
+
end
|
104
|
+
|
105
|
+
queries = queries.reduce({}) do |agg, val|
|
106
|
+
val[1][:avg_duration] = (val[1][:total_duration] / val[1][:count]).round(2)
|
107
|
+
val[1][:total_duration] = val[1][:total_duration].round(2)
|
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.round(2),
|
116
|
+
sql_duration: sql_duration.round(2)
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
|
65
121
|
def self.index_info(args: {}, in_format: :display_table)
|
66
122
|
data = RailsPgExtras::IndexInfo.call(args[:table_name])
|
67
123
|
|
@@ -91,7 +147,11 @@ module RailsPgExtras
|
|
91
147
|
end
|
92
148
|
|
93
149
|
def self.connection
|
94
|
-
|
150
|
+
if (db_url = ENV['RAILS_PG_EXTRAS_DATABASE_URL'])
|
151
|
+
ActiveRecord::Base.establish_connection(db_url).connection
|
152
|
+
else
|
153
|
+
ActiveRecord::Base.connection
|
154
|
+
end
|
95
155
|
end
|
96
156
|
end
|
97
157
|
|
@@ -4,12 +4,14 @@ require "rails_pg_extras/web"
|
|
4
4
|
|
5
5
|
module RailsPgExtras
|
6
6
|
class Configuration
|
7
|
-
DEFAULT_CONFIG = { enabled_web_actions: Web::ACTIONS - [:kill_all] }
|
7
|
+
DEFAULT_CONFIG = { enabled_web_actions: Web::ACTIONS - [:kill_all], public_dashboard: ENV["RAILS_PG_EXTRAS_PUBLIC_DASHBOARD"] == "true" }
|
8
8
|
|
9
9
|
attr_reader :enabled_web_actions
|
10
|
+
attr_accessor :public_dashboard
|
10
11
|
|
11
12
|
def initialize(attrs)
|
12
13
|
self.enabled_web_actions = attrs[:enabled_web_actions]
|
14
|
+
self.public_dashboard = attrs[:public_dashboard]
|
13
15
|
end
|
14
16
|
|
15
17
|
def enabled_web_actions=(*actions)
|
data/spec/smoke_spec.rb
CHANGED
@@ -28,4 +28,19 @@ describe RailsPgExtras do
|
|
28
28
|
RailsPgExtras.table_info(in_format: :hash)
|
29
29
|
end.not_to raise_error
|
30
30
|
end
|
31
|
+
|
32
|
+
it "collecting queries data works" do
|
33
|
+
output = RailsPgExtras.measure_queries { RailsPgExtras.diagnose(in_format: :hash) }
|
34
|
+
expect(output.fetch(:count)).to eq 10
|
35
|
+
end
|
36
|
+
|
37
|
+
it "supports custom RAILS_PG_EXTRAS_DATABASE_URL" do
|
38
|
+
ENV['RAILS_PG_EXTRAS_DATABASE_URL'] = ENV['DATABASE_URL']
|
39
|
+
|
40
|
+
expect do
|
41
|
+
RailsPgExtras.calls
|
42
|
+
end.not_to raise_error
|
43
|
+
|
44
|
+
ENV['RAILS_PG_EXTRAS_DATABASE_URL'] = nil
|
45
|
+
end
|
31
46
|
end
|
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
|
+
version: 5.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- pawurb
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-03-01 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:
|
19
|
+
version: 5.2.1
|
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:
|
26
|
+
version: 5.2.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rails
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|