pghero 2.7.1 → 2.8.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: 911330cefafafbbb8773eea03c1857144019d7c0c496f37e7114f0584ea0d87b
4
- data.tar.gz: 741121519dec2b6cf37a919053a12ecd09237911deea31ae28f6c5535c193301
3
+ metadata.gz: 3b4a596a016b5c33a99013818181ef967c26dcec092ee787b6629eac091ad121
4
+ data.tar.gz: 63fc2a949a62828a4537e54fb087c5a5b143bc88ab2753b4d777d4f793fb34ab
5
5
  SHA512:
6
- metadata.gz: f784263eb1f47916a996755115ab4a59ae9335bf8bdd692f654db2d82a75e1f3b2887364580f9abb033a2c900d41bc24cd1a2cbfdfe4ab9bb1189146605bda57
7
- data.tar.gz: 215258014b6dd25f1d58df31a725611a34f236be66811238551930e927871ebe5f271a7c74e67ae477b0a8a43d4d379b9947185e8b6c7c08f502234ecbca5132
6
+ metadata.gz: beda0eb3c89c9a3928b9d998e7e9ae8858adda77873c9dc0d03836498a765c219815c6441b814d3b23da78f02f759d71737eb51236135cfb72c6372358043c37
7
+ data.tar.gz: 020de38d5b406ede495f44ad16ce6a5bde8ebb59c49027af4dc49afb88cf768bc20f3c064fe72070f2739eb2979d2b95d0f7cdbdd106851252e6c81f646d3e82
data/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ ## 2.8.1 (2021-03-25)
2
+
3
+ - Added support for pg_query 2
4
+
5
+ ## 2.8.0 (2021-03-14)
6
+
7
+ - No longer show walsender in long running queries
8
+ - Show relative time on maintenance page
9
+ - Added support for `PGHERO_TZ` for Docker and Linux
10
+
11
+ ## 2.7.4 (2021-02-01)
12
+
13
+ - Fixed error on redirect with Ruby 3
14
+ - Fixed error with unparsable sequences
15
+
16
+ ## 2.7.3 (2020-11-23)
17
+
18
+ - Improved index suggestions when hash, GiST, GIN, or BRIN index present
19
+
20
+ ## 2.7.2 (2020-09-10)
21
+
22
+ - Fixed error with historical query stats
23
+
1
24
  ## 2.7.1 (2020-09-07)
2
25
 
3
26
  - Added `/health` endpoint to Docker image and Linux packages
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014-2019 Andrew Kane, 2008-2014 Heroku (initial queries)
1
+ Copyright (c) 2014-2021 Andrew Kane, 2008-2014 Heroku (initial queries)
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -4,11 +4,11 @@ A performance dashboard for Postgres
4
4
 
5
5
  [See it in action](https://pghero.dokkuapp.com/)
6
6
 
7
- [![Screenshot](https://pghero.dokkuapp.com/assets/pghero-a09d6c90d3d5cbf90f437b792a9b0c89751054c5e10be57af4efb79feab2458b.png)](https://pghero.dokkuapp.com/)
7
+ [![Screenshot](https://pghero.dokkuapp.com/assets/pghero-ebb9c8e11434fd0e4ead81db88fe255b29cbbc845ec315b79e6c5e25015bc921.png)](https://pghero.dokkuapp.com/)
8
8
 
9
9
  :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
10
10
 
11
- [![Build Status](https://travis-ci.org/ankane/pghero.svg?branch=master)](https://travis-ci.org/ankane/pghero) [![Docker Pulls](https://img.shields.io/docker/pulls/ankane/pghero)](https://hub.docker.com/repository/docker/ankane/pghero)
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/repository/docker/ankane/pghero)
12
12
 
13
13
  ## Installation
14
14
 
@@ -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,7 @@ module PgHero
42
42
  retries = 0
43
43
  begin
44
44
  result = conn.select_all(add_source(squish(sql)))
45
- if ActiveRecord::VERSION::MAJOR >= 6
45
+ if ActiveRecord::VERSION::STRING.to_f >= 6.1
46
46
  result = result.map(&:symbolize_keys)
47
47
  else
48
48
  result = result.map { |row| Hash[row.map { |col, val| [col.to_sym, result.column_types[col].send(:cast_value, val)] }] }
@@ -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] }
@@ -185,36 +201,69 @@ module PgHero
185
201
  rescue PgQuery::ParseError
186
202
  return {error: "Parse error"}
187
203
  end
188
- return {error: "Unknown structure"} unless tree.size == 1
189
-
190
- tree = tree.first
191
-
192
- # pg_query 1.0.0
193
- tree = tree["RawStmt"]["stmt"] if tree["RawStmt"]
194
-
195
- table = parse_table(tree) rescue nil
196
- unless table
197
- error =
198
- case tree.keys.first
199
- when "InsertStmt"
200
- "INSERT statement"
201
- when "VariableSetStmt"
202
- "SET statement"
203
- when "SelectStmt"
204
- if (tree["SelectStmt"]["fromClause"].first["JoinExpr"] rescue false)
205
- "JOIN not supported yet"
204
+
205
+ if PgQuery::VERSION.to_i >= 2
206
+ return {error: "Unknown structure"} unless tree.stmts.size == 1
207
+
208
+ tree = tree.stmts.first.stmt
209
+
210
+ table = parse_table_v2(tree) rescue nil
211
+ unless table
212
+ error =
213
+ case tree.node
214
+ when :insert_stmt
215
+ "INSERT statement"
216
+ when :variable_set_stmt
217
+ "SET statement"
218
+ when :select_stmt
219
+ if (tree.select_stmt.from_clause.first.join_expr rescue false)
220
+ "JOIN not supported yet"
221
+ end
206
222
  end
207
- end
208
- return {error: error || "Unknown structure"}
209
- end
223
+ return {error: error || "Unknown structure"}
224
+ end
210
225
 
211
- select = tree.values.first
212
- where = (select["whereClause"] ? parse_where(select["whereClause"]) : []) rescue nil
213
- return {error: "Unknown structure"} unless where
226
+ select = tree[tree.node.to_s]
227
+ where = (select.where_clause ? parse_where_v2(select.where_clause) : []) rescue nil
228
+ return {error: "Unknown structure"} unless where
214
229
 
215
- sort = (select["sortClause"] ? parse_sort(select["sortClause"]) : []) rescue []
230
+ sort = (select.sort_clause ? parse_sort_v2(select.sort_clause) : []) rescue []
216
231
 
217
- {table: table, where: where, sort: sort}
232
+ {table: table, where: where, sort: sort}
233
+ else
234
+ # TODO remove support for pg_query < 2 in PgHero 3.0
235
+
236
+ return {error: "Unknown structure"} unless tree.size == 1
237
+
238
+ tree = tree.first
239
+
240
+ # pg_query 1.0.0
241
+ tree = tree["RawStmt"]["stmt"] if tree["RawStmt"]
242
+
243
+ table = parse_table(tree) rescue nil
244
+ unless table
245
+ error =
246
+ case tree.keys.first
247
+ when "InsertStmt"
248
+ "INSERT statement"
249
+ when "VariableSetStmt"
250
+ "SET statement"
251
+ when "SelectStmt"
252
+ if (tree["SelectStmt"]["fromClause"].first["JoinExpr"] rescue false)
253
+ "JOIN not supported yet"
254
+ end
255
+ end
256
+ return {error: error || "Unknown structure"}
257
+ end
258
+
259
+ select = tree.values.first
260
+ where = (select["whereClause"] ? parse_where(select["whereClause"]) : []) rescue nil
261
+ return {error: "Unknown structure"} unless where
262
+
263
+ sort = (select["sortClause"] ? parse_sort(select["sortClause"]) : []) rescue []
264
+
265
+ {table: table, where: where, sort: sort}
266
+ end
218
267
  end
219
268
 
220
269
  # TODO better row estimation
@@ -251,6 +300,17 @@ module PgHero
251
300
  end
252
301
  end
253
302
 
303
+ def parse_table_v2(tree)
304
+ case tree.node
305
+ when :select_stmt
306
+ tree.select_stmt.from_clause.first.range_var.relname
307
+ when :delete_stmt
308
+ tree.delete_stmt.relation.relname
309
+ when :update_stmt
310
+ tree.update_stmt.relation.relname
311
+ end
312
+ end
313
+
254
314
  def parse_table(tree)
255
315
  case tree.keys.first
256
316
  when "SelectStmt"
@@ -262,6 +322,26 @@ module PgHero
262
322
  end
263
323
  end
264
324
 
325
+ # TODO capture values
326
+ def parse_where_v2(tree)
327
+ aexpr = tree.a_expr
328
+
329
+ if tree.bool_expr
330
+ if tree.bool_expr.boolop == :AND_EXPR
331
+ tree.bool_expr.args.flat_map { |v| parse_where_v2(v) }
332
+ else
333
+ raise "Not Implemented"
334
+ end
335
+ elsif aexpr && ["=", "<>", ">", ">=", "<", "<=", "~~", "~~*", "BETWEEN"].include?(aexpr.name.first.string.str)
336
+ [{column: aexpr.lexpr.column_ref.fields.last.string.str, op: aexpr.name.first.string.str}]
337
+ elsif tree.null_test
338
+ op = tree.null_test.nulltesttype == :IS_NOT_NULL ? "not_null" : "null"
339
+ [{column: tree.null_test.arg.column_ref.fields.last.string.str, op: op}]
340
+ else
341
+ raise "Not Implemented"
342
+ end
343
+ end
344
+
265
345
  # TODO capture values
266
346
  def parse_where(tree)
267
347
  aexpr = tree["A_Expr"]
@@ -282,6 +362,15 @@ module PgHero
282
362
  end
283
363
  end
284
364
 
365
+ def parse_sort_v2(sort_clause)
366
+ sort_clause.map do |v|
367
+ {
368
+ column: v.sort_by.node.column_ref.fields.last.string.str,
369
+ direction: v.sort_by.sortby_dir == :SORTBY_DESC ? "desc" : "asc"
370
+ }
371
+ end
372
+ end
373
+
285
374
  def parse_sort(sort_clause)
286
375
  sort_clause.map do |v|
287
376
  {
@@ -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.1"
2
+ VERSION = "2.8.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.7.1
4
+ version: 2.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-08 00:00:00.000000000 Z
11
+ date: 2021-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -24,91 +24,7 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '5'
27
- - !ruby/object:Gem::Dependency
28
- name: activerecord-import
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: bundler
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: minitest
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: rake
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: pg
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: pg_query
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
- description:
27
+ description:
112
28
  email: andrew@chartkick.com
113
29
  executables: []
114
30
  extensions: []
@@ -191,7 +107,7 @@ homepage: https://github.com/ankane/pghero
191
107
  licenses:
192
108
  - MIT
193
109
  metadata: {}
194
- post_install_message:
110
+ post_install_message:
195
111
  rdoc_options: []
196
112
  require_paths:
197
113
  - lib
@@ -206,8 +122,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
206
122
  - !ruby/object:Gem::Version
207
123
  version: '0'
208
124
  requirements: []
209
- rubygems_version: 3.1.2
210
- signing_key:
125
+ rubygems_version: 3.2.3
126
+ signing_key:
211
127
  specification_version: 4
212
128
  summary: A performance dashboard for Postgres
213
129
  test_files: []