pghero 2.7.0 → 2.8.0

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.

@@ -508,3 +508,7 @@ body {
508
508
  .duplicate-indexes pre {
509
509
  margin-top: 10px;
510
510
  }
511
+
512
+ .no-outline:focus {
513
+ outline: none;
514
+ }
@@ -1,4 +1,4 @@
1
- /*! nouislider - 14.0.3 - 10/10/2019 */
1
+ /*! nouislider - 14.6.1 - 8/17/2020 */
2
2
  /* Functional styling;
3
3
  * These styles are required for noUiSlider to function.
4
4
  * You don't need to change these rules to apply your design.
@@ -18,7 +18,6 @@
18
18
  }
19
19
  .noUi-target {
20
20
  position: relative;
21
- direction: ltr;
22
21
  }
23
22
  .noUi-base,
24
23
  .noUi-connects {
@@ -39,7 +38,7 @@
39
38
  position: absolute;
40
39
  z-index: 1;
41
40
  top: 0;
42
- left: 0;
41
+ right: 0;
43
42
  -ms-transform-origin: 0 0;
44
43
  -webkit-transform-origin: 0 0;
45
44
  -webkit-transform-style: preserve-3d;
@@ -56,9 +55,9 @@
56
55
  }
57
56
  /* Offset direction
58
57
  */
59
- html:not([dir="rtl"]) .noUi-horizontal .noUi-origin {
60
- left: auto;
61
- right: 0;
58
+ .noUi-txt-dir-rtl.noUi-horizontal .noUi-origin {
59
+ left: 0;
60
+ right: auto;
62
61
  }
63
62
  /* Give origins 0 height/width so they don't interfere with clicking the
64
63
  * connect elements.
@@ -94,7 +93,7 @@ html:not([dir="rtl"]) .noUi-horizontal .noUi-origin {
94
93
  .noUi-horizontal .noUi-handle {
95
94
  width: 34px;
96
95
  height: 28px;
97
- left: -17px;
96
+ right: -17px;
98
97
  top: -6px;
99
98
  }
100
99
  .noUi-vertical {
@@ -103,12 +102,12 @@ html:not([dir="rtl"]) .noUi-horizontal .noUi-origin {
103
102
  .noUi-vertical .noUi-handle {
104
103
  width: 28px;
105
104
  height: 34px;
106
- left: -6px;
105
+ right: -6px;
107
106
  top: -17px;
108
107
  }
109
- html:not([dir="rtl"]) .noUi-horizontal .noUi-handle {
110
- right: -17px;
111
- left: auto;
108
+ .noUi-txt-dir-rtl.noUi-horizontal .noUi-handle {
109
+ left: -17px;
110
+ right: auto;
112
111
  }
113
112
  /* Styling;
114
113
  * Giving the connect element a border radius causes issues with using transform: scale
@@ -297,3 +296,15 @@ html:not([dir="rtl"]) .noUi-horizontal .noUi-handle {
297
296
  top: 50%;
298
297
  right: 120%;
299
298
  }
299
+ .noUi-horizontal .noUi-origin > .noUi-tooltip {
300
+ -webkit-transform: translate(50%, 0);
301
+ transform: translate(50%, 0);
302
+ left: auto;
303
+ bottom: 10px;
304
+ }
305
+ .noUi-vertical .noUi-origin > .noUi-tooltip {
306
+ -webkit-transform: translate(0, -18px);
307
+ transform: translate(0, -18px);
308
+ top: auto;
309
+ right: 28px;
310
+ }
@@ -34,7 +34,8 @@ module PgHero
34
34
  @inactive_replication_slots = @database.replication_slots.select { |r| !r[:active] }
35
35
  end
36
36
 
37
- @autovacuum_queries, @long_running_queries = @database.long_running_queries.partition { |q| q[:query].starts_with?("autovacuum:") }
37
+ @walsender_queries, long_running_queries = @database.long_running_queries.partition { |q| q[:backend_type] == "walsender" }
38
+ @autovacuum_queries, @long_running_queries = long_running_queries.partition { |q| q[:query].starts_with?("autovacuum:") }
38
39
 
39
40
  connection_states = @database.connection_states
40
41
  @total_connections = connection_states.values.sum
@@ -373,11 +374,7 @@ module PgHero
373
374
  protected
374
375
 
375
376
  def redirect_backward(options = {})
376
- if Rails.version >= "5.1"
377
- redirect_back options.merge(fallback_location: root_path)
378
- else
379
- redirect_to :back, options
380
- end
377
+ redirect_back fallback_location: root_path, **options
381
378
  end
382
379
 
383
380
  def set_database
@@ -26,5 +26,15 @@ module PgHero
26
26
  ret << ", column: #{columns.inspect}" if columns
27
27
  ret
28
28
  end
29
+
30
+ def pghero_formatted_vacuum_times(time)
31
+ content_tag(:span, title: pghero_formatted_date_time(time)) do
32
+ "#{time_ago_in_words(time, include_seconds: true).sub(/(over|about|almost) /, "").sub("less than", "<")} ago"
33
+ end
34
+ end
35
+
36
+ def pghero_formatted_date_time(time)
37
+ l time.in_time_zone(@time_zone), format: :long
38
+ end
29
39
  end
30
40
  end
@@ -26,6 +26,9 @@
26
26
  <% if @autovacuum_queries.any? %>
27
27
  <span class="tiny"><%= @autovacuum_queries.size %> autovacuum</span>
28
28
  <% end %>
29
+ <% if @walsender_queries.any? %>
30
+ <span class="tiny"><%= @walsender_queries.size %> walsender</span>
31
+ <% end %>
29
32
  </div>
30
33
  <% if @extended %>
31
34
  <div class="alert alert-<%= @good_cache_rate ? "success" : "warning" %>">
@@ -24,7 +24,7 @@
24
24
  <td>
25
25
  <% time = [table[:last_autovacuum], table[:last_vacuum]].compact.max %>
26
26
  <% if time %>
27
- <%= l time.in_time_zone(@time_zone), format: :short %>
27
+ <%= pghero_formatted_vacuum_times(time) %>
28
28
  <% else %>
29
29
  <span class="text-muted">Unknown</span>
30
30
  <% end %>
@@ -32,7 +32,7 @@
32
32
  <td>
33
33
  <% time = [table[:last_autoanalyze], table[:last_analyze]].compact.max %>
34
34
  <% if time %>
35
- <%= l time.in_time_zone(@time_zone), format: :short %>
35
+ <%= pghero_formatted_vacuum_times(time) %>
36
36
  <% else %>
37
37
  <span class="text-muted">Unknown</span>
38
38
  <% end %>
data/lib/pghero.rb CHANGED
@@ -113,13 +113,13 @@ module PgHero
113
113
 
114
114
  if !ENV["PGHERO_DATABASE_URL"] && spec_supported?
115
115
  ActiveRecord::Base.configurations.configs_for(env_name: env, include_replicas: true).each do |db|
116
- databases[db.spec_name] = {"spec" => db.spec_name}
116
+ databases[db.send(spec_name_key)] = {"spec" => db.send(spec_name_key)}
117
117
  end
118
118
  end
119
119
 
120
120
  if databases.empty?
121
121
  databases["primary"] = {
122
- "url" => ENV["PGHERO_DATABASE_URL"] || ActiveRecord::Base.connection_config
122
+ "url" => ENV["PGHERO_DATABASE_URL"] || connection_config(ActiveRecord::Base)
123
123
  }
124
124
  end
125
125
 
@@ -211,6 +211,18 @@ module PgHero
211
211
  ActiveRecord::VERSION::MAJOR >= 6
212
212
  end
213
213
 
214
+ # private
215
+ def connection_config(model)
216
+ ActiveRecord::VERSION::STRING.to_f >= 6.1 ? model.connection_db_config.configuration_hash : model.connection_config
217
+ end
218
+
219
+ # private
220
+ # Rails 6.1 deprecate `spec_name` and use `name` for configurations
221
+ # https://github.com/rails/rails/pull/38536
222
+ def spec_name_key
223
+ ActiveRecord::VERSION::STRING.to_f >= 6.1 ? :name : :spec_name
224
+ end
225
+
214
226
  private
215
227
 
216
228
  def each_database
@@ -149,11 +149,14 @@ module PgHero
149
149
  # resolve spec
150
150
  if !url && config["spec"]
151
151
  raise Error, "Spec requires Rails 6+" unless PgHero.spec_supported?
152
- resolved = ActiveRecord::Base.configurations.configs_for(env_name: PgHero.env, spec_name: config["spec"], include_replicas: true)
152
+ config_options = {env_name: PgHero.env, PgHero.spec_name_key => config["spec"], include_replicas: true}
153
+ resolved = ActiveRecord::Base.configurations.configs_for(**config_options)
153
154
  raise Error, "Spec not found: #{config["spec"]}" unless resolved
154
- url = resolved.config
155
+ url = ActiveRecord::VERSION::STRING.to_f >= 6.1 ? resolved.configuration_hash : resolved.config
155
156
  end
156
157
 
158
+ url = url.dup
159
+
157
160
  Class.new(PgHero::Connection) do
158
161
  def self.name
159
162
  "PgHero::Connection::Database#{object_id}"
@@ -42,7 +42,11 @@ module PgHero
42
42
  retries = 0
43
43
  begin
44
44
  result = conn.select_all(add_source(squish(sql)))
45
- result = result.map { |row| Hash[row.map { |col, val| [col.to_sym, result.column_types[col].send(:cast_value, val)] }] }
45
+ if ActiveRecord::VERSION::STRING.to_f >= 6.1
46
+ result = result.map(&:symbolize_keys)
47
+ else
48
+ result = result.map { |row| Hash[row.map { |col, val| [col.to_sym, result.column_types[col].send(:cast_value, val)] }] }
49
+ end
46
50
  if filter_data
47
51
  query_columns.each do |column|
48
52
  result.each do |row|
@@ -12,7 +12,8 @@ module PgHero
12
12
  query,
13
13
  COALESCE(query_start, xact_start) AS started_at,
14
14
  EXTRACT(EPOCH FROM NOW() - COALESCE(query_start, xact_start)) * 1000.0 AS duration_ms,
15
- usename AS user
15
+ usename AS user,
16
+ #{server_version_num >= 100000 ? "backend_type" : "NULL AS backend_type"}
16
17
  FROM
17
18
  pg_stat_activity
18
19
  WHERE
@@ -47,7 +47,8 @@ module PgHero
47
47
  end
48
48
  end
49
49
 
50
- sequences.sort_by { |s| s[:sequence] }
50
+ # use to_s for unparsable sequences
51
+ sequences.sort_by { |s| s[:sequence].to_s }
51
52
  end
52
53
 
53
54
  def sequence_danger(threshold: 0.9, sequences: nil)
@@ -30,7 +30,23 @@ module PgHero
30
30
  if best_index[:found]
31
31
  index = best_index[:index]
32
32
  best_index[:table_indexes] = indexes_by_table[index[:table]].to_a
33
- covering_index = existing_columns[index[:using] || "btree"][index[:table]].find { |e| index_covers?(e, index[:columns]) }
33
+
34
+ # indexes of same type
35
+ indexes = existing_columns[index[:using] || "btree"][index[:table]]
36
+
37
+ if best_index[:structure][:sort].empty?
38
+ # gist indexes without an opclass
39
+ # (opclass is part of column name, so columns won't match if opclass present)
40
+ indexes += existing_columns["gist"][index[:table]]
41
+
42
+ # hash indexes work for equality
43
+ indexes += existing_columns["hash"][index[:table]] if best_index[:structure][:where].all? { |v| v[:op] == "=" }
44
+
45
+ # brin indexes work for all
46
+ indexes += existing_columns["brin"][index[:table]]
47
+ end
48
+
49
+ covering_index = indexes.find { |e| index_covers?(e.map { |v| v.sub(/ inet_ops\z/, "") }, index[:columns]) }
34
50
  if covering_index
35
51
  best_index[:covering_index] = covering_index
36
52
  best_index[:explanation] = "Covered by index on (#{covering_index.join(", ")})"
@@ -86,7 +102,7 @@ module PgHero
86
102
  # get stats about columns for relevant tables
87
103
  tables = parts.values.map { |t| t[:table] }.uniq
88
104
  # TODO get schema from query structure, then try search path
89
- schema = connection_model.connection_config[:schema] || "public"
105
+ schema = PgHero.connection_config(connection_model)[:schema] || "public"
90
106
  if tables.any?
91
107
  row_stats = Hash[table_stats(table: tables, schema: schema).map { |i| [i[:table], i[:estimated_rows]] }]
92
108
  col_stats = column_stats(table: tables, schema: schema).group_by { |i| i[:table] }
@@ -144,7 +144,7 @@ module PgHero
144
144
 
145
145
  # validate input since we need to interpolate below
146
146
  raise Error, "Invalid metric name" unless metric_name =~ /\A[a-z\/_]+\z/i
147
- raise Error, "Invalid database id" unless gcp_database_id =~ /\A[a-z\-:]+\z/i
147
+ raise Error, "Invalid database id" unless gcp_database_id =~ /\A[a-z0-9\-:]+\z/i
148
148
 
149
149
  # we handle three situations:
150
150
  # 1. google-cloud-monitoring-v3
@@ -276,7 +276,6 @@ module PgHero
276
276
 
277
277
  def add_missing_data(data, start_time, end_time, period)
278
278
  time = start_time
279
- end_time = end_time
280
279
  while time < end_time
281
280
  data[time] ||= nil
282
281
  time += period
@@ -5,7 +5,7 @@ module PgHero
5
5
  # TODO quote in 3.0, but still not officially supported
6
6
  def create_user(user, password: nil, schema: "public", database: nil, readonly: false, tables: nil)
7
7
  password ||= random_password
8
- database ||= connection_model.connection_config[:database]
8
+ database ||= PgHero.connection_config(connection_model)[:database]
9
9
 
10
10
  commands =
11
11
  [
@@ -44,7 +44,7 @@ module PgHero
44
44
  # documented as unsafe to pass user input
45
45
  # TODO quote in 3.0, but still not officially supported
46
46
  def drop_user(user, schema: "public", database: nil)
47
- database ||= connection_model.connection_config[:database]
47
+ database ||= PgHero.connection_config(connection_model)[:database]
48
48
 
49
49
  # thanks shiftb
50
50
  commands =
@@ -1,3 +1,3 @@
1
1
  module PgHero
2
- VERSION = "2.7.0"
2
+ VERSION = "2.8.0"
3
3
  end
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Chart.js Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013-2019 Andrew Kane
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2006, Ivan Sagalaev.
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ * Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,20 @@
1
+ Copyright OpenJS Foundation and other contributors, https://openjsf.org/
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,22 @@
1
+ Copyright (c) JS Foundation and other contributors
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Léon Gersen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.