pghero 3.3.4 → 3.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/LICENSE.txt +1 -1
- data/README.md +1 -1
- data/app/controllers/pg_hero/home_controller.rb +9 -3
- data/lib/pghero/methods/explain.rb +3 -2
- data/lib/pghero/methods/query_stats.rb +15 -5
- data/lib/pghero/methods/settings.rb +8 -1
- data/lib/pghero/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe3a00850cccac8c36536d08a519790103053c9b058d61754f834c44cfb0d4eb
|
4
|
+
data.tar.gz: e7ae77f4353d7f27ca04f1a59aae6cf785f8e0dddac2f276e6a0973b4beb60e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89d0bc08fd97968e725fea80a23d70835853acbe74648a052299b62a5e343820b36f0a76ecb3cb207d1443b47fc5d43154b40a7a0e39be518719f2c3ef589398
|
7
|
+
data.tar.gz: df4e987d43c13dd0d3866feefc4cfe2cf5b84c8519fb854d54e169f42ab0909b07890893498dd2e30635e36645630647db9451b2c32de737c345ac52b6ed3205
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## 3.4.1 (2024-02-07)
|
2
|
+
|
3
|
+
- Added current stats to query details page
|
4
|
+
- Improved tune page for latest PgTune
|
5
|
+
|
6
|
+
## 3.4.0 (2023-11-28)
|
7
|
+
|
8
|
+
- Added support for explaining normalized queries with Postgres 16
|
9
|
+
- Added Docker image for `linux/arm64`
|
10
|
+
|
1
11
|
## 3.3.4 (2023-09-05)
|
2
12
|
|
3
13
|
- Fixed support for aliases in config file
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -8,7 +8,7 @@ A performance dashboard for Postgres
|
|
8
8
|
|
9
9
|
:tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
|
10
10
|
|
11
|
-
[![Build Status](https://github.com/ankane/pghero/workflows/build/badge.svg
|
11
|
+
[![Build Status](https://github.com/ankane/pghero/actions/workflows/build.yml/badge.svg)](https://github.com/ankane/pghero/actions)
|
12
12
|
|
13
13
|
## Documentation
|
14
14
|
|
@@ -186,7 +186,7 @@ module PgHero
|
|
186
186
|
@explainable_query = stats[:explainable_query]
|
187
187
|
|
188
188
|
if @show_details
|
189
|
-
query_hash_stats = @database.query_hash_stats(@query_hash, user: @user)
|
189
|
+
query_hash_stats = @database.query_hash_stats(@query_hash, user: @user, current: true)
|
190
190
|
|
191
191
|
@chart_data = [{name: "Value", data: query_hash_stats.map { |r| [r[:captured_at].change(sec: 0), (r[:total_minutes] * 60 * 1000).round] }, library: chart_library_options}]
|
192
192
|
@chart2_data = [{name: "Value", data: query_hash_stats.map { |r| [r[:captured_at].change(sec: 0), r[:average_time].round(1)] }, library: chart_library_options}]
|
@@ -292,12 +292,14 @@ module PgHero
|
|
292
292
|
# need to prevent CSRF and DoS
|
293
293
|
if request.post? && @query.present?
|
294
294
|
begin
|
295
|
+
generic_plan = @database.server_version_num >= 160000 && @query.include?("$1")
|
296
|
+
|
295
297
|
explain_options =
|
296
298
|
case params[:commit]
|
297
299
|
when "Analyze"
|
298
300
|
{analyze: true}
|
299
301
|
when "Visualize"
|
300
|
-
if @explain_analyze_enabled
|
302
|
+
if @explain_analyze_enabled && !generic_plan
|
301
303
|
{analyze: true, costs: true, verbose: true, buffers: true, format: "json"}
|
302
304
|
else
|
303
305
|
{costs: true, verbose: true, format: "json"}
|
@@ -306,6 +308,8 @@ module PgHero
|
|
306
308
|
{}
|
307
309
|
end
|
308
310
|
|
311
|
+
explain_options[:generic_plan] = true if generic_plan
|
312
|
+
|
309
313
|
if explain_options[:analyze] && !@explain_analyze_enabled
|
310
314
|
render_text "Explain analyze not enabled", status: :bad_request
|
311
315
|
return
|
@@ -319,8 +323,10 @@ module PgHero
|
|
319
323
|
@error =
|
320
324
|
if message == "Unsafe statement"
|
321
325
|
"Unsafe statement"
|
322
|
-
elsif message.start_with?("PG::
|
326
|
+
elsif message.start_with?("PG::UndefinedParameter")
|
323
327
|
"Can't explain queries with bind parameters"
|
328
|
+
elsif message.include?("EXPLAIN options ANALYZE and GENERIC_PLAN cannot be used together")
|
329
|
+
"Can't analyze queries with bind parameters"
|
324
330
|
elsif message.start_with?("PG::SyntaxError")
|
325
331
|
"Syntax error with query"
|
326
332
|
elsif message.start_with?("PG::QueryCanceled")
|
@@ -12,7 +12,7 @@ module PgHero
|
|
12
12
|
if (sql.sub(/;\z/, "").include?(";") || sql.upcase.include?("COMMIT")) && !explain_safe?
|
13
13
|
raise ActiveRecord::StatementInvalid, "Unsafe statement"
|
14
14
|
end
|
15
|
-
explanation =
|
15
|
+
explanation = execute("EXPLAIN #{sql}").map { |v| v["QUERY PLAN"] }.join("\n")
|
16
16
|
end
|
17
17
|
|
18
18
|
explanation
|
@@ -20,11 +20,12 @@ module PgHero
|
|
20
20
|
|
21
21
|
# TODO rename to explain in 4.0
|
22
22
|
# note: this method is not affected by the explain option
|
23
|
-
def explain_v2(sql, analyze: nil, verbose: nil, costs: nil, settings: nil, buffers: nil, wal: nil, timing: nil, summary: nil, format: "text")
|
23
|
+
def explain_v2(sql, analyze: nil, verbose: nil, costs: nil, settings: nil, generic_plan: nil, buffers: nil, wal: nil, timing: nil, summary: nil, format: "text")
|
24
24
|
options = []
|
25
25
|
add_explain_option(options, "ANALYZE", analyze)
|
26
26
|
add_explain_option(options, "VERBOSE", verbose)
|
27
27
|
add_explain_option(options, "SETTINGS", settings)
|
28
|
+
add_explain_option(options, "GENERIC_PLAN", generic_plan)
|
28
29
|
add_explain_option(options, "COSTS", costs)
|
29
30
|
add_explain_option(options, "BUFFERS", buffers)
|
30
31
|
add_explain_option(options, "WAL", wal)
|
@@ -172,11 +172,10 @@ module PgHero
|
|
172
172
|
query_stats.select { |q| q[:calls].to_i >= slow_query_calls.to_i && q[:average_time].to_f >= slow_query_ms.to_f }
|
173
173
|
end
|
174
174
|
|
175
|
-
|
176
|
-
def query_hash_stats(query_hash, user: nil)
|
175
|
+
def query_hash_stats(query_hash, user: nil, current: false)
|
177
176
|
if historical_query_stats_enabled? && supports_query_hash?
|
178
177
|
start_at = 24.hours.ago
|
179
|
-
select_all_stats <<~SQL
|
178
|
+
stats = select_all_stats <<~SQL
|
180
179
|
SELECT
|
181
180
|
captured_at,
|
182
181
|
total_time / 1000 / 60 AS total_minutes,
|
@@ -193,6 +192,15 @@ module PgHero
|
|
193
192
|
ORDER BY
|
194
193
|
1 ASC
|
195
194
|
SQL
|
195
|
+
if current
|
196
|
+
captured_at = Time.current
|
197
|
+
current_stats = current_query_stats(query_hash: query_hash, user: user, origin: true)
|
198
|
+
current_stats.each do |r|
|
199
|
+
r[:captured_at] = captured_at
|
200
|
+
end
|
201
|
+
stats += current_stats
|
202
|
+
end
|
203
|
+
stats
|
196
204
|
else
|
197
205
|
raise NotEnabled, "Query hash stats not enabled"
|
198
206
|
end
|
@@ -201,7 +209,7 @@ module PgHero
|
|
201
209
|
private
|
202
210
|
|
203
211
|
# http://www.craigkerstiens.com/2013/01/10/more-on-postgres-performance/
|
204
|
-
def current_query_stats(limit: nil, sort: nil, database: nil, query_hash: nil)
|
212
|
+
def current_query_stats(limit: nil, sort: nil, database: nil, query_hash: nil, user: nil, origin: false)
|
205
213
|
if query_stats_enabled?
|
206
214
|
limit ||= 100
|
207
215
|
sort ||= "total_minutes"
|
@@ -225,10 +233,12 @@ module PgHero
|
|
225
233
|
calls > 0 AND
|
226
234
|
pg_database.datname = #{database ? quote(database) : "current_database()"}
|
227
235
|
#{query_hash ? "AND queryid = #{quote(query_hash)}" : nil}
|
236
|
+
#{user ? "AND rolname = #{quote(user)}" : nil}
|
228
237
|
)
|
229
238
|
SELECT
|
230
239
|
query,
|
231
240
|
query AS explainable_query,
|
241
|
+
#{origin ? "(SELECT regexp_matches(query, '.*/\\*(.+?)\\*/'))[1] AS origin," : nil}
|
232
242
|
query_hash,
|
233
243
|
query_stats.user,
|
234
244
|
total_minutes,
|
@@ -319,7 +329,7 @@ module PgHero
|
|
319
329
|
end
|
320
330
|
|
321
331
|
def explainable?(query)
|
322
|
-
query =~ /select/i && !query.include?("?)") && !query.include?("= ?") && !query.include?("$1") && query !~ /limit \?/i
|
332
|
+
query =~ /select/i && (server_version_num >= 160000 || (!query.include?("?)") && !query.include?("= ?") && !query.include?("$1") && query !~ /limit \?/i))
|
323
333
|
end
|
324
334
|
|
325
335
|
# removes comments
|
@@ -3,7 +3,14 @@ module PgHero
|
|
3
3
|
module Settings
|
4
4
|
def settings
|
5
5
|
names =
|
6
|
-
if server_version_num >=
|
6
|
+
if server_version_num >= 100000
|
7
|
+
%i(
|
8
|
+
max_connections shared_buffers effective_cache_size maintenance_work_mem
|
9
|
+
checkpoint_completion_target wal_buffers default_statistics_target
|
10
|
+
random_page_cost effective_io_concurrency work_mem huge_pages
|
11
|
+
min_wal_size max_wal_size
|
12
|
+
)
|
13
|
+
elsif server_version_num >= 90500
|
7
14
|
%i(
|
8
15
|
max_connections shared_buffers effective_cache_size work_mem
|
9
16
|
maintenance_work_mem min_wal_size max_wal_size checkpoint_completion_target
|
data/lib/pghero/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pghero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -124,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '0'
|
126
126
|
requirements: []
|
127
|
-
rubygems_version: 3.
|
127
|
+
rubygems_version: 3.5.3
|
128
128
|
signing_key:
|
129
129
|
specification_version: 4
|
130
130
|
summary: A performance dashboard for Postgres
|