pghero 3.3.4 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2332e2a4f7b9bd25d972ec1f06635dfc43676412c5be4147e3a7e3dc8effa213
4
- data.tar.gz: 1a24e423a0d8749d69eb938eaa27db1beed974d1a058e6ae6331badeae9c4d59
3
+ metadata.gz: fe3a00850cccac8c36536d08a519790103053c9b058d61754f834c44cfb0d4eb
4
+ data.tar.gz: e7ae77f4353d7f27ca04f1a59aae6cf785f8e0dddac2f276e6a0973b4beb60e7
5
5
  SHA512:
6
- metadata.gz: b19ca11ceee2f44bbcdec49e14f0653df6efc740e4fbd710700a03254ac8d40db84690409a731c06359d547934d401615925e88df317ccae4137eecefcfe8ddf
7
- data.tar.gz: b6c0069a0e34ecd66d359aff9226fe678ccc15d55b1ad0e70f421a7fa9b9a2113d7eb31cc30d69364489fa1e10e2b5d6d0e48ada03d15e79aff5ba9dda350659
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014-2023 Andrew Kane, 2008-2014 Heroku (initial queries)
1
+ Copyright (c) 2014-2024 Andrew Kane, 2008-2014 Heroku (initial queries)
2
2
 
3
3
  MIT License
4
4
 
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?branch=master)](https://github.com/ankane/pghero/actions) [![Docker Pulls](https://img.shields.io/docker/pulls/ankane/pghero)](https://hub.docker.com/r/ankane/pghero)
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::ProtocolViolation: ERROR: bind message supplies 0 parameters")
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 = select_all("EXPLAIN #{sql}").map { |v| v[:"QUERY PLAN"] }.join("\n")
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
- # TODO option to include current period
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 >= 90500
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
@@ -1,3 +1,3 @@
1
1
  module PgHero
2
- VERSION = "3.3.4"
2
+ VERSION = "3.4.1"
3
3
  end
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.3.4
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: 2023-09-05 00:00:00.000000000 Z
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.4.10
127
+ rubygems_version: 3.5.3
128
128
  signing_key:
129
129
  specification_version: 4
130
130
  summary: A performance dashboard for Postgres