pghero 2.0.6 → 2.0.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of pghero might be problematic. Click here for more details.

@@ -155,15 +155,16 @@ module PgHero
155
155
 
156
156
  def show_query
157
157
  @query_hash = params[:query_hash].to_i
158
+ @user = params[:user].to_s
158
159
  @title = @query_hash
159
160
 
160
- stats = @database.query_stats(historical: true, query_hash: @query_hash, start_at: 24.hours.ago).first
161
+ stats = @database.query_stats(historical: true, query_hash: @query_hash, start_at: 24.hours.ago).find { |qs| qs[:user] == @user }
161
162
  if stats
162
163
  @query = stats[:query]
163
164
  @explainable_query = stats[:explainable_query]
164
165
 
165
166
  if @show_details
166
- query_hash_stats = @database.query_hash_stats(@query_hash)
167
+ query_hash_stats = @database.query_hash_stats(@query_hash, user: @user)
167
168
 
168
169
  @chart_data = [{name: "Value", data: query_hash_stats.map { |r| [r[:captured_at], (r[:total_minutes] * 60 * 1000).round] }, library: chart_library_options}]
169
170
  @chart2_data = [{name: "Value", data: query_hash_stats.map { |r| [r[:captured_at], r[:average_time].round(1)] }, library: chart_library_options}]
@@ -51,7 +51,7 @@
51
51
  <% end %>
52
52
  <% end %>
53
53
  <% if @show_details && query[:query_hash] %>
54
- <%= link_to "details", show_query_path(query[:query_hash]), target: "_blank" %>
54
+ <%= link_to "details", show_query_path(query[:query_hash], user: query[:user]), target: "_blank" %>
55
55
  <% end %>
56
56
  </span>
57
57
  </td>
@@ -259,7 +259,7 @@
259
259
  <td>
260
260
  <%= index[:name] %>
261
261
  <% if index[:schema] != "public" %>
262
- <span class="text-muted"><%= query[:schema] %></span>
262
+ <span class="text-muted"><%= index[:schema] %></span>
263
263
  <% end %>
264
264
  </td>
265
265
  </tr>
@@ -0,0 +1,16 @@
1
+ # Contributing
2
+
3
+ ```sh
4
+ git clone https://github.com/ankane/pghero.git
5
+ git clone https://github.com/pghero/pghero.git pghero-dev
6
+ cd pghero-dev
7
+ git checkout dev
8
+ createdb pghero_dev
9
+ export DATABASE_URL=postgres:///pghero_dev
10
+ bundle exec rails generate pghero:query_stats
11
+ bundle exec rails generate pghero:space_stats
12
+ bundle exec rake db:migrate
13
+ foreman start
14
+ ```
15
+
16
+ And visit [http://localhost:5000](http://localhost:5000)
@@ -110,7 +110,7 @@ module PgHero
110
110
  def capture_query_stats(verbose: false)
111
111
  each_database do |database|
112
112
  puts "Capturing query stats for #{database.id}..." if verbose
113
- database.capture_query_stats
113
+ database.capture_query_stats(raise_errors: true)
114
114
  end
115
115
  end
116
116
 
@@ -56,10 +56,11 @@ module PgHero
56
56
  true
57
57
  end
58
58
 
59
- def reset_query_stats
59
+ def reset_query_stats(raise_errors: false)
60
60
  execute("SELECT pg_stat_statements_reset()")
61
61
  true
62
- rescue ActiveRecord::StatementInvalid
62
+ rescue ActiveRecord::StatementInvalid => e
63
+ raise e if raise_errors
63
64
  false
64
65
  end
65
66
 
@@ -87,7 +88,7 @@ module PgHero
87
88
  #
88
89
  # to get around this, we capture queries for every Postgres database before we
89
90
  # reset query stats for the Postgres instance with the `capture_query_stats` option
90
- def capture_query_stats
91
+ def capture_query_stats(raise_errors: false)
91
92
  return if config["capture_query_stats"] && config["capture_query_stats"] != true
92
93
 
93
94
  # get all databases that use same query stats and build mapping
@@ -105,7 +106,7 @@ module PgHero
105
106
 
106
107
  supports_query_hash = supports_query_hash?
107
108
 
108
- if query_stats.any? { |_, v| v.any? } && reset_query_stats
109
+ if query_stats.any? { |_, v| v.any? } && reset_query_stats(raise_errors: raise_errors)
109
110
  query_stats.each do |db_id, db_query_stats|
110
111
  if db_query_stats.any?
111
112
  values =
@@ -133,7 +134,7 @@ module PgHero
133
134
  query_stats.select { |q| q[:calls].to_i >= slow_query_calls.to_i && q[:average_time].to_f >= slow_query_ms.to_f }
134
135
  end
135
136
 
136
- def query_hash_stats(query_hash)
137
+ def query_hash_stats(query_hash, user: nil)
137
138
  if historical_query_stats_enabled? && supports_query_hash?
138
139
  start_at = 24.hours.ago
139
140
  select_all_stats <<-SQL
@@ -149,6 +150,7 @@ module PgHero
149
150
  database = #{quote(id)}
150
151
  AND captured_at >= #{quote(start_at)}
151
152
  AND query_hash = #{quote(query_hash)}
153
+ #{user ? "AND \"user\" = #{quote(user)}" : ""}
152
154
  ORDER BY
153
155
  1 ASC
154
156
  SQL
@@ -2,43 +2,58 @@ module PgHero
2
2
  module Methods
3
3
  module Sequences
4
4
  def sequences
5
+ # get columns with default values
6
+ # use pg_get_expr to get correct default value
7
+ # it's what information_schema.columns uses
5
8
  sequences = select_all <<-SQL
6
9
  SELECT
7
- n.nspname AS schema,
10
+ n.nspname AS table_schema,
8
11
  c.relname AS table,
9
12
  attname AS column,
10
13
  format_type(a.atttypid, a.atttypmod) AS column_type,
11
- CASE WHEN format_type(a.atttypid, a.atttypmod) = 'integer' THEN 2147483647::bigint ELSE (pg_sequence_parameters(s.oid)).maximum_value::bigint END AS max_value,
12
- s.relname AS sequence
14
+ pg_get_expr(d.adbin, d.adrelid) AS default_value
13
15
  FROM
14
16
  pg_catalog.pg_attribute a
15
17
  INNER JOIN
16
18
  pg_catalog.pg_class c ON c.oid = a.attrelid
17
19
  INNER JOIN
18
20
  pg_catalog.pg_namespace n ON n.oid = c.relnamespace
19
- LEFT JOIN
20
- pg_catalog.pg_attrdef d ON (a.attrelid, a.attnum) = (d.adrelid, d.adnum)
21
21
  INNER JOIN
22
- pg_catalog.pg_class s ON s.relkind = 'S'
23
- AND s.relname = regexp_replace(d.adsrc, '^nextval\\(''(.*)''\\:\\:regclass\\)$', '\\1')
22
+ pg_catalog.pg_attrdef d ON (a.attrelid, a.attnum) = (d.adrelid, d.adnum)
24
23
  WHERE
25
24
  NOT a.attisdropped
26
25
  AND a.attnum > 0
27
26
  AND d.adsrc LIKE 'nextval%'
28
- ORDER BY
29
- s.relname ASC
30
27
  SQL
31
28
 
29
+ # parse out sequence
30
+ sequences.each do |column|
31
+ m = /^nextval\('(.+)'\:\:regclass\)$/.match(column.delete(:default_value))
32
+ column[:schema], column[:sequence] = unquote_ident(m[1])
33
+ column[:max_value] = column[:column_type] == 'integer' ? 2147483647 : 9223372036854775807
34
+ end
35
+
32
36
  select_all(sequences.map { |s| "SELECT last_value FROM #{quote_ident(s[:schema])}.#{quote_ident(s[:sequence])}" }.join(" UNION ALL ")).each_with_index do |row, i|
33
37
  sequences[i][:last_value] = row[:last_value]
34
38
  end
35
39
 
36
- sequences
40
+ sequences.sort_by { |s| s[:sequence] }
37
41
  end
38
42
 
39
43
  def sequence_danger(threshold: 0.9)
40
44
  sequences.select { |s| s[:last_value] / s[:max_value].to_f > threshold }.sort_by { |s| s[:max_value] - s[:last_value] }
41
45
  end
46
+
47
+ private
48
+
49
+ def unquote_ident(value)
50
+ schema, seq = value.split(".")
51
+ unless seq
52
+ seq = schema
53
+ schema = 'public'
54
+ end
55
+ [unquote(schema), unquote(seq)]
56
+ end
42
57
  end
43
58
  end
44
59
  end
@@ -1,3 +1,3 @@
1
1
  module PgHero
2
- VERSION = "2.0.6"
2
+ VERSION = "2.0.7"
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: 2.0.6
4
+ version: 2.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-24 00:00:00.000000000 Z
11
+ date: 2017-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -153,6 +153,7 @@ files:
153
153
  - app/views/pg_hero/home/system.html.erb
154
154
  - app/views/pg_hero/home/tune.html.erb
155
155
  - config/routes.rb
156
+ - guides/Contributing.md
156
157
  - guides/Docker.md
157
158
  - guides/Heroku.md
158
159
  - guides/Linux.md
@@ -221,6 +222,7 @@ signing_key:
221
222
  specification_version: 4
222
223
  summary: A performance dashboard for Postgres
223
224
  test_files:
225
+ - guides/Contributing.md
224
226
  - guides/Docker.md
225
227
  - guides/Heroku.md
226
228
  - guides/Linux.md