pghero 2.2.0 → 2.2.1

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 344a413582024c07e65a19f69e4e5a8aa5bd9024d40ce82357ce234349c8f52f
4
- data.tar.gz: a9c4c8f537f9e2067582d9dba2b44f7fd83b43ed8ab6cc5aeaeaa04107087e96
3
+ metadata.gz: 7edafa3113ab1d1e6dd6df9c2abb1fe90ccd1077811475c001cfef05c021e8d9
4
+ data.tar.gz: f7e65b01aa540e9023c30c9cefcbf2fd7381fb71b24852043c82e81e878bc8f7
5
5
  SHA512:
6
- metadata.gz: 3b7999bab7e77cba83016ed05dde01778f4e79cbe4fa5982750dfedfe46c74438f2700d13ff8e07744c92f1a4c87b2c740bf455921d6004f047a57d2aa982720
7
- data.tar.gz: eb91e9b0f35115de7369e521cfa5c0c39854f4d2e8ea650782af2fd28a465e825daa5f7a1f485b6944c00dc7f14855524c034cd7f953d93a4f55718d43110183
6
+ metadata.gz: 857502906ac831709d69a371f4da5ca2bce260f55f72731f3db48be28b159d0816080e2dd90f87301653c609dc5d0dd27ec36d0e58f0649c45d18e9046851bc0
7
+ data.tar.gz: f54eb2b5e13a75b691fcb3efcc3b63d921a33194caf2f4d62147f8d7090faa4cfa70abfc379315bed61378176f2ef8919747990ed9ba8ff4f3886a32f54edbd3
@@ -1,3 +1,9 @@
1
+ ## 2.2.1
2
+
3
+ - Added `config_path` option
4
+ - Fixed error with sequences when temporary tables
5
+ - Fixed error when `config.action_controller.include_all_helpers = false`
6
+
1
7
  ## 2.2.0
2
8
 
3
9
  - Added check for connections idle in transaction
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014-2018 Andrew Kane, 2008-2014 Heroku (initial queries)
1
+ Copyright (c) 2014-2019 Andrew Kane, 2008-2014 Heroku (initial queries)
2
2
 
3
3
  MIT License
4
4
 
@@ -4,15 +4,23 @@
4
4
  *= require_self
5
5
  */
6
6
 
7
+ html {
8
+ box-sizing: border-box;
9
+ font-size: 14px;
10
+ }
11
+
12
+ *, *:before, *:after {
13
+ box-sizing: inherit;
14
+ }
15
+
7
16
  body {
8
17
  margin: 0;
9
18
  padding: 20px;
10
19
  background-color: #eee;
11
20
  }
12
21
 
13
- body, textarea {
14
- font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
15
- font-size: 14px;
22
+ body {
23
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
16
24
  line-height: 1.4;
17
25
  color: #333;
18
26
  }
@@ -30,7 +38,6 @@ table {
30
38
  width: 100%;
31
39
  border-collapse: collapse;
32
40
  border-spacing: 0;
33
- margin-bottom: 20px;
34
41
  table-layout: fixed;
35
42
  }
36
43
 
@@ -40,13 +47,11 @@ th {
40
47
  }
41
48
 
42
49
  h1 {
43
- margin-top: 0;
44
50
  font-size: 20px;
45
51
  font-weight: bold;
46
52
  }
47
53
 
48
54
  h2 {
49
- margin-top: 0;
50
55
  font-size: 16px;
51
56
  font-weight: bold;
52
57
  }
@@ -57,8 +62,13 @@ h1 small {
57
62
  color: #999;
58
63
  }
59
64
 
60
- h1, p {
61
- margin-bottom: 20px;
65
+ h1, h2, p, ul, hr, table, pre {
66
+ margin-top: 0;
67
+ margin-bottom: 1rem;
68
+ }
69
+
70
+ .field {
71
+ margin-bottom: 0.5rem;
62
72
  }
63
73
 
64
74
  h3 {
@@ -66,16 +76,16 @@ h3 {
66
76
  }
67
77
 
68
78
  ul {
79
+ list-style-type: disc;
80
+ padding-left: 20px;
81
+ }
82
+
83
+ ul.nav {
69
84
  list-style-type: none;
70
85
  padding: 0;
71
86
  margin: 0;
72
87
  }
73
88
 
74
- ul.list-normal {
75
- list-style-type: disc;
76
- padding-left: 20px;
77
- }
78
-
79
89
  table td, table th {
80
90
  padding: 8px;
81
91
  }
@@ -98,13 +108,13 @@ textarea {
98
108
  height: 120px;
99
109
  border: solid 1px #ddd;
100
110
  padding: 10px;
111
+ font-size: inherit;
101
112
  }
102
113
 
103
114
  hr {
104
115
  border: none;
105
116
  height: 0;
106
117
  border-top: solid 1px #ddd;
107
- margin-bottom: 15px;
108
118
  }
109
119
 
110
120
  .container {
@@ -171,9 +171,9 @@ module PgHero
171
171
  if @show_details
172
172
  query_hash_stats = @database.query_hash_stats(@query_hash, user: @user)
173
173
 
174
- @chart_data = [{name: "Value", data: query_hash_stats.map { |r| [r[:captured_at], (r[:total_minutes] * 60 * 1000).round] }, library: chart_library_options}]
175
- @chart2_data = [{name: "Value", data: query_hash_stats.map { |r| [r[:captured_at], r[:average_time].round(1)] }, library: chart_library_options}]
176
- @chart3_data = [{name: "Value", data: query_hash_stats.map { |r| [r[:captured_at], r[:calls]] }, library: chart_library_options}]
174
+ @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}]
175
+ @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}]
176
+ @chart3_data = [{name: "Value", data: query_hash_stats.map { |r| [r[:captured_at].change(sec: 0), r[:calls]] }, library: chart_library_options}]
177
177
 
178
178
  @origins = Hash[query_hash_stats.group_by { |r| r[:origin].to_s }.map { |k, v| [k, v.size] }]
179
179
  @total_count = query_hash_stats.size
@@ -286,7 +286,7 @@ module PgHero
286
286
 
287
287
  def kill
288
288
  if @database.kill(params[:pid])
289
- redirect_to root_path, notice: "Query killed"
289
+ redirect_backward notice: "Query killed"
290
290
  else
291
291
  redirect_backward notice: "Query no longer running"
292
292
  end
@@ -1,5 +1,5 @@
1
1
  module PgHero
2
- module BaseHelper
2
+ module HomeHelper
3
3
  def pghero_pretty_ident(table, schema: nil)
4
4
  ident = table
5
5
  if schema && schema != "public"
@@ -11,5 +11,9 @@ module PgHero
11
11
  @database.quote_ident(ident)
12
12
  end
13
13
  end
14
+
15
+ def pghero_js_var(name, value)
16
+ "var #{name} = #{json_escape(value.to_json(root: false))};".html_safe
17
+ end
14
18
  end
15
19
  end
@@ -5,12 +5,12 @@
5
5
  </div>
6
6
 
7
7
  <script>
8
- var sort = <%= @sort.to_json.html_safe %>;
9
- var minAverageTime = <%= @min_average_time.to_json %>;
10
- var minCalls = <%= @min_calls.to_json %>;
11
- var debug = <%= @debug.to_json %>;
12
- var startAt = <%= params[:start_at] ? @start_at.to_i * 1000 : "null" %>;
13
- var endAt = <%= @end_at.to_i %> * 1000;
8
+ <%= pghero_js_var("sort", @sort) %>
9
+ <%= pghero_js_var("minAverageTime", @min_average_time) %>
10
+ <%= pghero_js_var("minCalls", @min_calls) %>
11
+ <%= pghero_js_var("debug", @debug) %>
12
+ <%= pghero_js_var("startAt", params[:start_at] ? @start_at.to_i * 1000 : nil) %>
13
+ <%= pghero_js_var("endAt", @end_at.to_i * 1000) %>
14
14
 
15
15
  initSlider();
16
16
  </script>
@@ -2,7 +2,7 @@
2
2
  <h1>Explain</h1>
3
3
 
4
4
  <%= form_tag explain_path do %>
5
- <%= text_area_tag :query, @query, placeholder: "Enter a SQL query" %>
5
+ <div class="field"><%= text_area_tag :query, @query, placeholder: "Enter a SQL query" %></div>
6
6
  <p>
7
7
  <%= submit_tag "Explain", class: "btn btn-info", style: "margin-right: 10px;" %>
8
8
  <%= submit_tag "Analyze", class: "btn btn-danger", style: "margin-right: 10px;" %>
@@ -16,7 +16,7 @@
16
16
  <% end %>
17
17
  <pre><code><%= @explanation %></code></pre>
18
18
  <% unless @visualize %>
19
- <p><%= link_to "See how to interpret this", "http://www.postgresql.org/docs/current/static/using-explain.html", target: "_blank" %></p>
19
+ <p><%= link_to "See how to interpret this", "https://www.postgresql.org/docs/current/static/using-explain.html", target: "_blank" %></p>
20
20
  <% end %>
21
21
  <% if (index = @suggested_index) %>
22
22
  <%= render partial: "suggested_index", locals: {index: index, details: index[:details]} %>
@@ -89,7 +89,7 @@
89
89
  <td><%= table %></td>
90
90
  <td><%= @row_counts[table] %></td>
91
91
  <td>
92
- <ul class="list-normal">
92
+ <ul>
93
93
  <% @indexes_by_table[table].to_a.sort_by { |i| [i[:primary] ? 0 : 1, i[:columns]] }.each do |i3| %>
94
94
  <li>
95
95
  <%= i3[:columns].join(", ") %><% if i3[:using] != "btree" %>
@@ -3,24 +3,24 @@ databases:
3
3
  # Database URL (defaults to app database)
4
4
  # url: <%%= ENV["DATABASE_URL"] %>
5
5
 
6
- # Minimum time for long running queries
7
- # long_running_query_sec: 60
6
+ # Add more databases
7
+ # other:
8
+ # url: <%%= ENV["OTHER_DATABASE_URL"] %>
8
9
 
9
- # Minimum average time for slow queries
10
- # slow_query_ms: 20
10
+ # Minimum time for long running queries
11
+ # long_running_query_sec: 60
11
12
 
12
- # Minimum calls for slow queries
13
- # slow_query_calls: 100
13
+ # Minimum average time for slow queries
14
+ # slow_query_ms: 20
14
15
 
15
- # Minimum connections for high connections warning
16
- # total_connections_threshold: 500
16
+ # Minimum calls for slow queries
17
+ # slow_query_calls: 100
17
18
 
18
- # Statement timeout for explain
19
- # explain_timeout_sec: 10
19
+ # Minimum connections for high connections warning
20
+ # total_connections_threshold: 500
20
21
 
21
- # Add more databases
22
- # other:
23
- # url: <%%= ENV["OTHER_DATABASE_URL"] %>
22
+ # Statement timeout for explain
23
+ # explain_timeout_sec: 10
24
24
 
25
25
  # Time zone (defaults to app time zone)
26
26
  # time_zone: "Pacific Time (US & Canada)"
@@ -1,5 +1,6 @@
1
- require "pghero/version"
2
- require "active_record"
1
+ # dependencies
2
+ require "active_support"
3
+ require "forwardable"
3
4
 
4
5
  # methods
5
6
  require "pghero/methods/basic"
@@ -21,18 +22,18 @@ require "pghero/methods/users"
21
22
 
22
23
  require "pghero/database"
23
24
  require "pghero/engine" if defined?(Rails)
24
-
25
- # models
26
- require "pghero/connection"
27
- require "pghero/query_stats"
25
+ require "pghero/version"
28
26
 
29
27
  module PgHero
28
+ autoload :Connection, "pghero/connection"
29
+ autoload :QueryStats, "pghero/query_stats"
30
+
30
31
  class Error < StandardError; end
31
32
  class NotEnabled < Error; end
32
33
 
33
34
  # settings
34
35
  class << self
35
- attr_accessor :long_running_query_sec, :slow_query_ms, :slow_query_calls, :explain_timeout_sec, :total_connections_threshold, :cache_hit_rate_threshold, :env, :show_migrations
36
+ attr_accessor :long_running_query_sec, :slow_query_ms, :slow_query_calls, :explain_timeout_sec, :total_connections_threshold, :cache_hit_rate_threshold, :env, :show_migrations, :config_path
36
37
  end
37
38
  self.long_running_query_sec = (ENV["PGHERO_LONG_RUNNING_QUERY_SEC"] || 60).to_i
38
39
  self.slow_query_ms = (ENV["PGHERO_SLOW_QUERY_MS"] || 20).to_i
@@ -42,6 +43,7 @@ module PgHero
42
43
  self.cache_hit_rate_threshold = 99
43
44
  self.env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
44
45
  self.show_migrations = true
46
+ self.config_path = ENV["PGHERO_CONFIG_PATH"] || "config/pghero.yml"
45
47
 
46
48
  class << self
47
49
  extend Forwardable
@@ -68,7 +70,10 @@ module PgHero
68
70
 
69
71
  def config
70
72
  @config ||= begin
71
- path = "config/pghero.yml"
73
+ require "erb"
74
+ require "yaml"
75
+
76
+ path = config_path
72
77
 
73
78
  config_file_exists = File.exist?(path)
74
79
 
@@ -75,9 +75,9 @@ module PgHero
75
75
  end
76
76
  case url
77
77
  when String
78
- url = "#{url}#{url.include?("?") ? "&" : "?"}connect_timeout=2" unless url.include?("connect_timeout=")
78
+ url = "#{url}#{url.include?("?") ? "&" : "?"}connect_timeout=5" unless url.include?("connect_timeout=")
79
79
  when Hash
80
- url[:connect_timeout] ||= 2
80
+ url[:connect_timeout] ||= 5
81
81
  end
82
82
  establish_connection url if url
83
83
  end
@@ -27,7 +27,7 @@ module PgHero
27
27
  end
28
28
 
29
29
  def quote_ident(value)
30
- quote_table_name(value)
30
+ connection.quote_column_name(value)
31
31
  end
32
32
 
33
33
  private
@@ -37,7 +37,7 @@ module PgHero
37
37
  # squish for logs
38
38
  retries = 0
39
39
  begin
40
- result = conn.select_all(squish(sql))
40
+ result = conn.select_all(add_source(squish(sql)))
41
41
  cast_method = ActiveRecord::VERSION::MAJOR < 5 ? :type_cast : :cast_value
42
42
  result.map { |row| Hash[row.map { |col, val| [col.to_sym, result.column_types[col].send(cast_method, val)] }] }
43
43
  rescue ActiveRecord::StatementInvalid => e
@@ -73,7 +73,7 @@ module PgHero
73
73
  end
74
74
 
75
75
  def execute(sql)
76
- connection.execute(sql)
76
+ connection.execute(add_source(sql))
77
77
  end
78
78
 
79
79
  def connection
@@ -95,6 +95,10 @@ module PgHero
95
95
  str.to_s.gsub(/\A[[:space:]]+/, "").gsub(/[[:space:]]+\z/, "").gsub(/[[:space:]]+/, " ")
96
96
  end
97
97
 
98
+ def add_source(sql)
99
+ "#{sql} /*pghero*/"
100
+ end
101
+
98
102
  def quote(value)
99
103
  connection.quote(value)
100
104
  end
@@ -5,8 +5,8 @@ module PgHero
5
5
  select_one("SELECT pg_terminate_backend(#{pid.to_i})")
6
6
  end
7
7
 
8
- def kill_long_running_queries
9
- long_running_queries.each { |query| kill(query[:pid]) }
8
+ def kill_long_running_queries(min_duration: nil)
9
+ running_queries(min_duration: min_duration || long_running_query_sec).each { |query| kill(query[:pid]) }
10
10
  true
11
11
  end
12
12
 
@@ -10,7 +10,7 @@ module PgHero
10
10
 
11
11
  # https://www.postgresql.org/message-id/CADKbJJWz9M0swPT3oqe8f9+tfD4-F54uE6Xtkh4nERpVsQnjnw@mail.gmail.com
12
12
  def replication_lag
13
- with_feature_support do
13
+ with_feature_support(:replication_lag) do
14
14
  lag_condition =
15
15
  if server_version_num >= 100000
16
16
  "pg_last_wal_receive_lsn() = pg_last_wal_replay_lsn()"
@@ -31,7 +31,7 @@ module PgHero
31
31
 
32
32
  def replication_slots
33
33
  if server_version_num >= 90400
34
- with_feature_support([]) do
34
+ with_feature_support(:replication_slots, []) do
35
35
  select_all <<-SQL
36
36
  SELECT
37
37
  slot_name,
@@ -46,18 +46,26 @@ module PgHero
46
46
  end
47
47
 
48
48
  def replicating?
49
- with_feature_support(false) do
49
+ with_feature_support(:replicating?, false) do
50
50
  select_all("SELECT state FROM pg_stat_replication").any?
51
51
  end
52
52
  end
53
53
 
54
54
  private
55
55
 
56
- def with_feature_support(default = nil)
56
+ def feature_support
57
+ @feature_support ||= {}
58
+ end
59
+
60
+ def with_feature_support(cache_key, default = nil)
61
+ # cache feature support to minimize errors in logs
62
+ return default if feature_support[cache_key] == false
63
+
57
64
  begin
58
65
  yield
59
66
  rescue ActiveRecord::StatementInvalid => e
60
67
  raise unless e.message.start_with?("PG::FeatureNotSupported:")
68
+ feature_support[cache_key] = false
61
69
  default
62
70
  end
63
71
  end
@@ -5,6 +5,8 @@ module PgHero
5
5
  # get columns with default values
6
6
  # use pg_get_expr to get correct default value
7
7
  # it's what information_schema.columns uses
8
+ # also, exclude temporary tables to prevent error
9
+ # when accessing across sessions
8
10
  sequences = select_all <<-SQL
9
11
  SELECT
10
12
  n.nspname AS table_schema,
@@ -24,6 +26,7 @@ module PgHero
24
26
  NOT a.attisdropped
25
27
  AND a.attnum > 0
26
28
  AND d.adsrc LIKE 'nextval%'
29
+ AND n.nspname NOT LIKE 'pg\\_temp\\_%'
27
30
  SQL
28
31
 
29
32
  # parse out sequence
@@ -1,3 +1,3 @@
1
1
  module PgHero
2
- VERSION = "2.2.0"
2
+ VERSION = "2.2.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: 2.2.0
4
+ version: 2.2.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: 2018-09-03 00:00:00.000000000 Z
11
+ date: 2019-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -129,7 +129,7 @@ files:
129
129
  - app/assets/stylesheets/pghero/arduino-light.css
130
130
  - app/assets/stylesheets/pghero/jquery.nouislider.css
131
131
  - app/controllers/pg_hero/home_controller.rb
132
- - app/helpers/pg_hero/base_helper.rb
132
+ - app/helpers/pg_hero/home_helper.rb
133
133
  - app/views/layouts/pg_hero/application.html.erb
134
134
  - app/views/pg_hero/home/_connections_table.html.erb
135
135
  - app/views/pg_hero/home/_live_queries_table.html.erb
@@ -197,8 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
197
197
  - !ruby/object:Gem::Version
198
198
  version: '0'
199
199
  requirements: []
200
- rubyforge_project:
201
- rubygems_version: 2.7.7
200
+ rubygems_version: 3.0.3
202
201
  signing_key:
203
202
  specification_version: 4
204
203
  summary: A performance dashboard for Postgres