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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acfc146396c33697f24aa0e96d251c77fd94e2a5d10a8154dcff9df02a981ac8
4
- data.tar.gz: 5290474635b58a4e35afd3c075726b24e75a6194acd691e5b90b5b3289f2b5f1
3
+ metadata.gz: 4bd83f21b85de2b816dc334b72ad1bd0dfcb0f13ea6946fec42ebb93f47ba876
4
+ data.tar.gz: 4e9e5b00cc7e07cf3baa1bc3e224fe8ab50510afa909b47e753873fb1e7a11e7
5
5
  SHA512:
6
- metadata.gz: 3db2562f135d7b904239f5481d5050f1404062b679fa9dc1399379bea58f66c981d7adadee8d5af34ff4e5c0ffc10f6cd1de2c3fab03923083c45fa11d24785b
7
- data.tar.gz: 91b734c2c9bc9e6678d50def9cc4603e5444f35b16ec90514e14644954fa1d8d8e4b014c287dc2beb2cb9edfc3cadd29f517ad3f4fd42a37872304b91c5c553b
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?) && ENV["RAILS_PG_EXTRAS_PUBLIC_DASHBOARD"] != "true"
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
@@ -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
- ActiveRecord::Base.connection
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsPgExtras
4
- VERSION = "4.8.1"
4
+ VERSION = "5.2.1"
5
5
  end
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.8.1
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: 2022-07-19 00:00:00.000000000 Z
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: 4.8.1
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: 4.8.1
26
+ version: 5.2.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rails
29
29
  requirement: !ruby/object:Gem::Requirement