pghero 3.7.0 → 4.0.0
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 +4 -4
- data/CHANGELOG.md +22 -2
- data/LICENSE.txt +1 -1
- data/README.md +4 -3
- data/app/assets/javascripts/pghero/Chart.bundle.js +2228 -5320
- data/app/assets/javascripts/pghero/application.js +104 -68
- data/app/assets/javascripts/pghero/highlight.min.js +330 -341
- data/app/assets/javascripts/pghero/nouislider.js +64 -5
- data/app/assets/stylesheets/pghero/application.css +7 -1
- data/app/controllers/pg_hero/home_controller.rb +75 -41
- data/app/views/layouts/pg_hero/application.html.erb +1 -1
- data/app/views/pg_hero/home/_live_queries_table.html.erb +4 -2
- data/app/views/pg_hero/home/_queries_table.html.erb +8 -7
- data/app/views/pg_hero/home/_query_stats_slider.html.erb +1 -0
- data/app/views/pg_hero/home/_suggested_index.html.erb +3 -3
- data/app/views/pg_hero/home/explain.html.erb +2 -1
- data/app/views/pg_hero/home/index.html.erb +17 -18
- data/app/views/pg_hero/home/index_bloat.html.erb +2 -2
- data/app/views/pg_hero/home/live_queries.html.erb +7 -3
- data/app/views/pg_hero/home/queries.html.erb +31 -6
- data/app/views/pg_hero/home/show_query.html.erb +5 -5
- data/app/views/pg_hero/home/space.html.erb +5 -4
- data/app/views/pg_hero/home/tune.html.erb +1 -1
- data/lib/generators/pghero/templates/config.yml.tt +6 -1
- data/lib/generators/pghero/templates/query_stats.rb.tt +7 -1
- data/lib/generators/pghero/templates/upgrade_query_stats.rb.tt +23 -0
- data/lib/generators/pghero/upgrade_query_stats_generator.rb +19 -0
- data/lib/pghero/database.rb +6 -9
- data/lib/pghero/engine.rb +3 -10
- data/lib/pghero/methods/basic.rb +28 -27
- data/lib/pghero/methods/connections.rb +16 -32
- data/lib/pghero/methods/explain.rb +13 -20
- data/lib/pghero/methods/indexes.rb +8 -5
- data/lib/pghero/methods/kill.rb +4 -1
- data/lib/pghero/methods/maintenance.rb +14 -20
- data/lib/pghero/methods/queries.rb +7 -5
- data/lib/pghero/methods/query_stats.rb +264 -238
- data/lib/pghero/methods/replication.rb +9 -20
- data/lib/pghero/methods/sequences.rb +1 -1
- data/lib/pghero/methods/settings.rb +6 -11
- data/lib/pghero/methods/space.rb +11 -8
- data/lib/pghero/methods/suggested_indexes.rb +15 -16
- data/lib/pghero/methods/system.rb +6 -106
- data/lib/pghero/methods/tables.rb +7 -3
- data/lib/pghero/query.rb +8 -0
- data/lib/pghero/query_stats.rb +3 -0
- data/lib/pghero/version.rb +1 -1
- data/lib/pghero.rb +10 -42
- data/licenses/LICENSE-chart.js.txt +1 -1
- metadata +8 -8
- data/app/assets/javascripts/pghero/jquery.js +0 -10993
- data/lib/pghero/methods/users.rb +0 -95
- data/licenses/LICENSE-jquery.txt +0 -20
|
@@ -93,7 +93,7 @@ module PgHero
|
|
|
93
93
|
SELECT
|
|
94
94
|
n.nspname AS schema,
|
|
95
95
|
c.relname AS sequence,
|
|
96
|
-
has_sequence_privilege(c.oid, 'SELECT') AS readable
|
|
96
|
+
has_sequence_privilege(c.oid, 'SELECT') AND (c.relpersistence <> 'u' OR NOT pg_is_in_recovery()) AS readable
|
|
97
97
|
FROM
|
|
98
98
|
pg_class c
|
|
99
99
|
INNER JOIN
|
|
@@ -3,24 +3,19 @@ module PgHero
|
|
|
3
3
|
module Settings
|
|
4
4
|
def settings
|
|
5
5
|
names =
|
|
6
|
-
if server_version_num >=
|
|
6
|
+
if server_version_num >= 180000
|
|
7
7
|
%i(
|
|
8
8
|
max_connections shared_buffers effective_cache_size maintenance_work_mem
|
|
9
9
|
checkpoint_completion_target wal_buffers default_statistics_target
|
|
10
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
|
|
14
|
-
%i(
|
|
15
|
-
max_connections shared_buffers effective_cache_size work_mem
|
|
16
|
-
maintenance_work_mem min_wal_size max_wal_size checkpoint_completion_target
|
|
17
|
-
wal_buffers default_statistics_target
|
|
11
|
+
jit wal_compression io_method min_wal_size max_wal_size
|
|
18
12
|
)
|
|
19
13
|
else
|
|
20
14
|
%i(
|
|
21
|
-
max_connections shared_buffers effective_cache_size
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
max_connections shared_buffers effective_cache_size maintenance_work_mem
|
|
16
|
+
checkpoint_completion_target wal_buffers default_statistics_target
|
|
17
|
+
random_page_cost effective_io_concurrency work_mem huge_pages
|
|
18
|
+
jit wal_compression min_wal_size max_wal_size
|
|
24
19
|
)
|
|
25
20
|
end
|
|
26
21
|
fetch_settings(names)
|
data/lib/pghero/methods/space.rb
CHANGED
|
@@ -52,7 +52,7 @@ module PgHero
|
|
|
52
52
|
sizes = relation_sizes.to_h { |r| [[r[:schema], r[:relation]], r[:size_bytes]] }
|
|
53
53
|
start_at = days.days.ago
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
sql = <<~SQL
|
|
56
56
|
WITH t AS (
|
|
57
57
|
SELECT
|
|
58
58
|
schema,
|
|
@@ -61,8 +61,8 @@ module PgHero
|
|
|
61
61
|
FROM
|
|
62
62
|
pghero_space_stats
|
|
63
63
|
WHERE
|
|
64
|
-
database =
|
|
65
|
-
AND captured_at >=
|
|
64
|
+
database = :id
|
|
65
|
+
AND captured_at >= :start_at
|
|
66
66
|
GROUP BY
|
|
67
67
|
1, 2
|
|
68
68
|
)
|
|
@@ -75,6 +75,7 @@ module PgHero
|
|
|
75
75
|
ORDER BY
|
|
76
76
|
1, 2
|
|
77
77
|
SQL
|
|
78
|
+
stats = select_all_stats(sql, {id: id, start_at: start_at})
|
|
78
79
|
|
|
79
80
|
stats.each do |r|
|
|
80
81
|
relation = [r[:schema], r[:relation]]
|
|
@@ -95,20 +96,22 @@ module PgHero
|
|
|
95
96
|
sizes = relation_sizes.map { |r| [[r[:schema], r[:relation]], r[:size_bytes]] }.to_h
|
|
96
97
|
start_at = 30.days.ago
|
|
97
98
|
|
|
98
|
-
|
|
99
|
+
sql = <<~SQL
|
|
99
100
|
SELECT
|
|
100
101
|
captured_at,
|
|
101
102
|
size AS size_bytes
|
|
102
103
|
FROM
|
|
103
104
|
pghero_space_stats
|
|
104
105
|
WHERE
|
|
105
|
-
database =
|
|
106
|
-
AND captured_at >=
|
|
107
|
-
AND schema =
|
|
108
|
-
AND relation =
|
|
106
|
+
database = :id
|
|
107
|
+
AND captured_at >= :start_at
|
|
108
|
+
AND schema = :schema
|
|
109
|
+
AND relation = :relation
|
|
109
110
|
ORDER BY
|
|
110
111
|
1 ASC
|
|
111
112
|
SQL
|
|
113
|
+
binds = {id: id, start_at: start_at, schema: schema, relation: relation}
|
|
114
|
+
stats = select_all_stats(sql, binds)
|
|
112
115
|
|
|
113
116
|
stats << {
|
|
114
117
|
captured_at: Time.now,
|
|
@@ -2,7 +2,7 @@ module PgHero
|
|
|
2
2
|
module Methods
|
|
3
3
|
module SuggestedIndexes
|
|
4
4
|
def suggested_indexes_enabled?
|
|
5
|
-
defined?(PgQuery) && Gem::Version.new(PgQuery::VERSION) >= Gem::Version.new("
|
|
5
|
+
defined?(PgQuery) && Gem::Version.new(PgQuery::VERSION) >= Gem::Version.new("6") && query_stats_enabled?
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
# TODO clean this mess
|
|
@@ -102,9 +102,9 @@ module PgHero
|
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
# get stats about columns for relevant tables
|
|
105
|
-
tables = parts.values.
|
|
105
|
+
tables = parts.values.filter_map { |t| t[:table] }.uniq
|
|
106
106
|
# TODO get schema from query structure, then try search path
|
|
107
|
-
schema =
|
|
107
|
+
schema = connection_model.connection_db_config.configuration_hash[:schema] || "public"
|
|
108
108
|
if tables.any?
|
|
109
109
|
row_stats = table_stats(table: tables, schema: schema).to_h { |i| [i[:table], i[:estimated_rows]] }
|
|
110
110
|
col_stats = column_stats(table: tables, schema: schema).group_by { |i| i[:table] }
|
|
@@ -287,33 +287,29 @@ module PgHero
|
|
|
287
287
|
if tree.bool_expr.boolop == :AND_EXPR
|
|
288
288
|
tree.bool_expr.args.flat_map { |v| parse_where(v) }
|
|
289
289
|
else
|
|
290
|
-
raise "Not Implemented"
|
|
290
|
+
raise Error, "Not Implemented"
|
|
291
291
|
end
|
|
292
|
-
elsif aexpr && ["=", "<>", ">", ">=", "<", "<=", "~~", "~~*", "BETWEEN"].include?(aexpr.name.first.string.
|
|
293
|
-
[{column: aexpr.lexpr.column_ref.fields.last.string.
|
|
292
|
+
elsif aexpr && ["=", "<>", ">", ">=", "<", "<=", "~~", "~~*", "BETWEEN"].include?(aexpr.name.first.string.sval)
|
|
293
|
+
[{column: aexpr.lexpr.column_ref.fields.last.string.sval, op: aexpr.name.first.string.sval}]
|
|
294
294
|
elsif tree.null_test
|
|
295
295
|
op = tree.null_test.nulltesttype == :IS_NOT_NULL ? "not_null" : "null"
|
|
296
|
-
[{column: tree.null_test.arg.column_ref.fields.last.string.
|
|
296
|
+
[{column: tree.null_test.arg.column_ref.fields.last.string.sval, op: op}]
|
|
297
297
|
else
|
|
298
|
-
raise "Not Implemented"
|
|
298
|
+
raise Error, "Not Implemented"
|
|
299
299
|
end
|
|
300
300
|
end
|
|
301
301
|
|
|
302
|
-
def str_method
|
|
303
|
-
@str_method ||= Gem::Version.new(PgQuery::VERSION) >= Gem::Version.new("4") ? :sval : :str
|
|
304
|
-
end
|
|
305
|
-
|
|
306
302
|
def parse_sort(sort_clause)
|
|
307
303
|
sort_clause.map do |v|
|
|
308
304
|
{
|
|
309
|
-
column: v.sort_by.node.column_ref.fields.last.string.
|
|
305
|
+
column: v.sort_by.node.column_ref.fields.last.string.sval,
|
|
310
306
|
direction: v.sort_by.sortby_dir == :SORTBY_DESC ? "desc" : "asc"
|
|
311
307
|
}
|
|
312
308
|
end
|
|
313
309
|
end
|
|
314
310
|
|
|
315
311
|
def column_stats(schema: nil, table: nil)
|
|
316
|
-
|
|
312
|
+
sql = <<~SQL
|
|
317
313
|
SELECT
|
|
318
314
|
schemaname AS schema,
|
|
319
315
|
tablename AS table,
|
|
@@ -323,11 +319,14 @@ module PgHero
|
|
|
323
319
|
FROM
|
|
324
320
|
pg_stats
|
|
325
321
|
WHERE
|
|
326
|
-
schemaname =
|
|
327
|
-
#{
|
|
322
|
+
schemaname = :schema
|
|
323
|
+
#{"AND tablename IN (:table)" if table}
|
|
328
324
|
ORDER BY
|
|
329
325
|
1, 2, 3
|
|
330
326
|
SQL
|
|
327
|
+
binds = {schema: schema}
|
|
328
|
+
binds[:table] = Array(table) if table
|
|
329
|
+
select_all(sql, binds)
|
|
331
330
|
end
|
|
332
331
|
end
|
|
333
332
|
end
|
|
@@ -10,8 +10,6 @@ module PgHero
|
|
|
10
10
|
:aws
|
|
11
11
|
elsif gcp_database_id
|
|
12
12
|
:gcp
|
|
13
|
-
elsif azure_resource_id
|
|
14
|
-
:azure
|
|
15
13
|
end
|
|
16
14
|
end
|
|
17
15
|
|
|
@@ -77,54 +75,6 @@ module PgHero
|
|
|
77
75
|
end
|
|
78
76
|
end
|
|
79
77
|
|
|
80
|
-
def azure_stats(metric_name, duration: nil, period: nil, offset: nil, series: false)
|
|
81
|
-
# TODO DRY with RDS stats
|
|
82
|
-
duration = (duration || 1.hour).to_i
|
|
83
|
-
period = (period || 1.minute).to_i
|
|
84
|
-
offset = (offset || 0).to_i
|
|
85
|
-
end_time = Time.at(((Time.now - offset).to_f / period).ceil * period)
|
|
86
|
-
start_time = end_time - duration
|
|
87
|
-
|
|
88
|
-
interval =
|
|
89
|
-
case period
|
|
90
|
-
when 60
|
|
91
|
-
"PT1M"
|
|
92
|
-
when 300
|
|
93
|
-
"PT5M"
|
|
94
|
-
when 900
|
|
95
|
-
"PT15M"
|
|
96
|
-
when 1800
|
|
97
|
-
"PT30M"
|
|
98
|
-
when 3600
|
|
99
|
-
"PT1H"
|
|
100
|
-
else
|
|
101
|
-
raise Error, "Unsupported period"
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
client = Azure::Monitor::Profiles::Latest::Mgmt::Client.new
|
|
105
|
-
# call utc to convert +00:00 to Z
|
|
106
|
-
timespan = "#{start_time.utc.iso8601}/#{end_time.utc.iso8601}"
|
|
107
|
-
results = client.metrics.list(
|
|
108
|
-
azure_resource_id,
|
|
109
|
-
metricnames: metric_name,
|
|
110
|
-
aggregation: "Average",
|
|
111
|
-
timespan: timespan,
|
|
112
|
-
interval: interval
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
data = {}
|
|
116
|
-
result = results.value.first
|
|
117
|
-
if result
|
|
118
|
-
result.timeseries.first.data.each do |point|
|
|
119
|
-
data[point.time_stamp.to_time] = point.average
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
add_missing_data(data, start_time, end_time, period) if series
|
|
124
|
-
|
|
125
|
-
data
|
|
126
|
-
end
|
|
127
|
-
|
|
128
78
|
private
|
|
129
79
|
|
|
130
80
|
def gcp_stats(metric_name, duration: nil, period: nil, offset: nil, series: false)
|
|
@@ -139,25 +89,19 @@ module PgHero
|
|
|
139
89
|
raise Error, "Invalid metric name" unless /\A[a-z\/_]+\z/i.match?(metric_name)
|
|
140
90
|
raise Error, "Invalid database id" unless /\A[a-z0-9\-:]+\z/i.match?(gcp_database_id)
|
|
141
91
|
|
|
142
|
-
# we handle
|
|
92
|
+
# we handle three situations:
|
|
143
93
|
# 1. google-cloud-monitoring-v3
|
|
144
|
-
# 2. google-cloud-monitoring
|
|
145
|
-
# 3. google-
|
|
146
|
-
# 4. google-apis-monitoring_v3
|
|
94
|
+
# 2. google-cloud-monitoring
|
|
95
|
+
# 3. google-apis-monitoring_v3
|
|
147
96
|
begin
|
|
148
97
|
require "google/cloud/monitoring/v3"
|
|
149
98
|
rescue LoadError
|
|
150
|
-
|
|
151
|
-
require "google/cloud/monitoring"
|
|
152
|
-
rescue LoadError
|
|
153
|
-
require "google/apis/monitoring_v3"
|
|
154
|
-
end
|
|
99
|
+
require "google/apis/monitoring_v3"
|
|
155
100
|
end
|
|
156
101
|
|
|
157
102
|
# for situations 1 and 2
|
|
158
|
-
# Google::Cloud::Monitoring.metric_service
|
|
159
|
-
|
|
160
|
-
if defined?(Google::Cloud::Monitoring::V3::MetricService::Client)
|
|
103
|
+
# Google::Cloud::Monitoring.metric_service doesn't work for situation 1
|
|
104
|
+
if defined?(Google::Cloud::Monitoring::V3)
|
|
161
105
|
client = Google::Cloud::Monitoring::V3::MetricService::Client.new
|
|
162
106
|
|
|
163
107
|
interval = Google::Cloud::Monitoring::V3::TimeInterval.new
|
|
@@ -178,29 +122,6 @@ module PgHero
|
|
|
178
122
|
view: Google::Cloud::Monitoring::V3::ListTimeSeriesRequest::TimeSeriesView::FULL,
|
|
179
123
|
aggregation: aggregation
|
|
180
124
|
})
|
|
181
|
-
elsif defined?(Google::Cloud::Monitoring)
|
|
182
|
-
require "google/cloud/monitoring"
|
|
183
|
-
|
|
184
|
-
client = Google::Cloud::Monitoring::Metric.new
|
|
185
|
-
|
|
186
|
-
interval = Google::Monitoring::V3::TimeInterval.new
|
|
187
|
-
interval.end_time = Google::Protobuf::Timestamp.new(seconds: end_time.to_i)
|
|
188
|
-
# subtract period to make sure we get first data point
|
|
189
|
-
interval.start_time = Google::Protobuf::Timestamp.new(seconds: (start_time - period).to_i)
|
|
190
|
-
|
|
191
|
-
aggregation = Google::Monitoring::V3::Aggregation.new
|
|
192
|
-
# may be better to use ALIGN_NEXT_OLDER for space stats to show most recent data point
|
|
193
|
-
# stick with average for now to match AWS
|
|
194
|
-
aggregation.per_series_aligner = Google::Monitoring::V3::Aggregation::Aligner::ALIGN_MEAN
|
|
195
|
-
aggregation.alignment_period = period
|
|
196
|
-
|
|
197
|
-
results = client.list_time_series(
|
|
198
|
-
"projects/#{gcp_database_id.split(":").first}",
|
|
199
|
-
"metric.type = \"cloudsql.googleapis.com/database/#{metric_name}\" AND resource.label.database_id = \"#{gcp_database_id}\"",
|
|
200
|
-
interval,
|
|
201
|
-
Google::Monitoring::V3::ListTimeSeriesRequest::TimeSeriesView::FULL,
|
|
202
|
-
aggregation: aggregation
|
|
203
|
-
)
|
|
204
125
|
else
|
|
205
126
|
client = Google::Apis::MonitoringV3::MonitoringService.new
|
|
206
127
|
|
|
@@ -264,32 +185,11 @@ module PgHero
|
|
|
264
185
|
}
|
|
265
186
|
gcp_stats(metrics[metric_key], **options)
|
|
266
187
|
end
|
|
267
|
-
when :azure
|
|
268
|
-
if metric_key == :free_space
|
|
269
|
-
quota = azure_stats("storage_limit", **options)
|
|
270
|
-
used = azure_stats("storage_used", **options)
|
|
271
|
-
free_space(quota, used)
|
|
272
|
-
else
|
|
273
|
-
replication_lag_stat = azure_flexible_server? ? "physical_replication_delay_in_seconds" : "pg_replica_log_delay_in_seconds"
|
|
274
|
-
metrics = {
|
|
275
|
-
cpu: "cpu_percent",
|
|
276
|
-
connections: "active_connections",
|
|
277
|
-
replication_lag: replication_lag_stat,
|
|
278
|
-
read_iops: "read_iops", # flexible server only
|
|
279
|
-
write_iops: "write_iops" # flexible server only
|
|
280
|
-
}
|
|
281
|
-
raise Error, "Metric not supported" unless metrics[metric_key]
|
|
282
|
-
azure_stats(metrics[metric_key], **options)
|
|
283
|
-
end
|
|
284
188
|
else
|
|
285
189
|
raise NotEnabled, "System stats not enabled"
|
|
286
190
|
end
|
|
287
191
|
end
|
|
288
192
|
|
|
289
|
-
def azure_flexible_server?
|
|
290
|
-
azure_resource_id.include?("/Microsoft.DBforPostgreSQL/flexibleServers/")
|
|
291
|
-
end
|
|
292
|
-
|
|
293
193
|
# only use data points included in both series
|
|
294
194
|
# this also eliminates need to align Time.now
|
|
295
195
|
def free_space(quota, used)
|
|
@@ -44,7 +44,7 @@ module PgHero
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
def table_stats(schema: nil, table: nil)
|
|
47
|
-
|
|
47
|
+
sql = <<~SQL
|
|
48
48
|
SELECT
|
|
49
49
|
nspname AS schema,
|
|
50
50
|
relname AS table,
|
|
@@ -56,11 +56,15 @@ module PgHero
|
|
|
56
56
|
pg_namespace ON pg_namespace.oid = pg_class.relnamespace
|
|
57
57
|
WHERE
|
|
58
58
|
relkind = 'r'
|
|
59
|
-
#{
|
|
60
|
-
#{
|
|
59
|
+
#{"AND nspname = :schema" if schema}
|
|
60
|
+
#{"AND relname IN (:table)" if table}
|
|
61
61
|
ORDER BY
|
|
62
62
|
1, 2
|
|
63
63
|
SQL
|
|
64
|
+
binds = {}
|
|
65
|
+
binds[:schema] = schema if schema
|
|
66
|
+
binds[:table] = Array(table) if table
|
|
67
|
+
select_all(sql, binds)
|
|
64
68
|
end
|
|
65
69
|
end
|
|
66
70
|
end
|
data/lib/pghero/query.rb
ADDED
data/lib/pghero/query_stats.rb
CHANGED
data/lib/pghero/version.rb
CHANGED
data/lib/pghero.rb
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
# dependencies
|
|
2
2
|
require "active_support"
|
|
3
3
|
|
|
4
|
-
# stdlib
|
|
5
|
-
require "forwardable"
|
|
6
|
-
|
|
7
4
|
# methods
|
|
8
5
|
require_relative "pghero/methods/basic"
|
|
9
6
|
require_relative "pghero/methods/connections"
|
|
@@ -21,7 +18,6 @@ require_relative "pghero/methods/space"
|
|
|
21
18
|
require_relative "pghero/methods/suggested_indexes"
|
|
22
19
|
require_relative "pghero/methods/system"
|
|
23
20
|
require_relative "pghero/methods/tables"
|
|
24
|
-
require_relative "pghero/methods/users"
|
|
25
21
|
|
|
26
22
|
require_relative "pghero/database"
|
|
27
23
|
require_relative "pghero/engine" if defined?(Rails)
|
|
@@ -30,6 +26,7 @@ require_relative "pghero/version"
|
|
|
30
26
|
module PgHero
|
|
31
27
|
autoload :Connection, "pghero/connection"
|
|
32
28
|
autoload :Stats, "pghero/stats"
|
|
29
|
+
autoload :Query, "pghero/query"
|
|
33
30
|
autoload :QueryStats, "pghero/query_stats"
|
|
34
31
|
autoload :SpaceStats, "pghero/space_stats"
|
|
35
32
|
|
|
@@ -54,20 +51,6 @@ module PgHero
|
|
|
54
51
|
self.filter_data = ENV["PGHERO_FILTER_DATA"].to_s.size > 0
|
|
55
52
|
|
|
56
53
|
class << self
|
|
57
|
-
extend Forwardable
|
|
58
|
-
def_delegators :primary_database, :aws_access_key_id, :analyze, :analyze_tables, :autoindex, :autovacuum_danger,
|
|
59
|
-
:best_index, :blocked_queries, :connections, :connection_sources, :connection_states, :connection_stats,
|
|
60
|
-
:cpu_usage, :create_user, :database_size, :aws_db_instance_identifier, :disable_query_stats, :drop_user,
|
|
61
|
-
:duplicate_indexes, :enable_query_stats, :explain, :historical_query_stats_enabled?, :index_caching,
|
|
62
|
-
:index_hit_rate, :index_usage, :indexes, :invalid_constraints, :invalid_indexes, :kill, :kill_all, :kill_long_running_queries,
|
|
63
|
-
:last_stats_reset_time, :long_running_queries, :maintenance_info, :missing_indexes, :query_stats,
|
|
64
|
-
:query_stats_available?, :query_stats_enabled?, :query_stats_extension_enabled?, :query_stats_readable?,
|
|
65
|
-
:rds_stats, :read_iops_stats, :aws_region, :relation_sizes, :replica?, :replication_lag, :replication_lag_stats,
|
|
66
|
-
:reset_query_stats, :reset_stats, :running_queries, :aws_secret_access_key, :sequence_danger, :sequences, :settings,
|
|
67
|
-
:slow_queries, :space_growth, :ssl_used?, :suggested_indexes, :suggested_indexes_by_query,
|
|
68
|
-
:suggested_indexes_enabled?, :system_stats_enabled?, :table_caching, :table_hit_rate, :table_stats,
|
|
69
|
-
:total_connections, :transaction_id_danger, :unused_indexes, :unused_tables, :write_iops_stats
|
|
70
|
-
|
|
71
54
|
def time_zone=(time_zone)
|
|
72
55
|
@time_zone = time_zone.is_a?(ActiveSupport::TimeZone) ? time_zone : ActiveSupport::TimeZone[time_zone.to_s]
|
|
73
56
|
end
|
|
@@ -107,6 +90,10 @@ module PgHero
|
|
|
107
90
|
@visualize_url ||= config["visualize_url"] || ENV["PGHERO_VISUALIZE_URL"] || "https://tatiyants.com/pev/#/plans/new"
|
|
108
91
|
end
|
|
109
92
|
|
|
93
|
+
def kill_enabled?
|
|
94
|
+
!config["disable_kill"]
|
|
95
|
+
end
|
|
96
|
+
|
|
110
97
|
def config
|
|
111
98
|
@config ||= file_config || default_config
|
|
112
99
|
end
|
|
@@ -130,7 +117,7 @@ module PgHero
|
|
|
130
117
|
elsif config["databases"] # preferred format
|
|
131
118
|
config
|
|
132
119
|
elsif config_file_exists
|
|
133
|
-
raise "Invalid config file"
|
|
120
|
+
raise Error, "Invalid config file"
|
|
134
121
|
else
|
|
135
122
|
nil
|
|
136
123
|
end
|
|
@@ -144,8 +131,8 @@ module PgHero
|
|
|
144
131
|
databases = {}
|
|
145
132
|
|
|
146
133
|
unless ENV["PGHERO_DATABASE_URL"]
|
|
147
|
-
ActiveRecord::Base.configurations.configs_for(env_name: env,
|
|
148
|
-
databases[db.
|
|
134
|
+
ActiveRecord::Base.configurations.configs_for(env_name: env, include_hidden: true).each do |db|
|
|
135
|
+
databases[db.name] = {"spec" => db.name}
|
|
149
136
|
end
|
|
150
137
|
end
|
|
151
138
|
|
|
@@ -158,8 +145,7 @@ module PgHero
|
|
|
158
145
|
if databases.size == 1
|
|
159
146
|
databases.values.first.merge!(
|
|
160
147
|
"aws_db_instance_identifier" => ENV["PGHERO_DB_INSTANCE_IDENTIFIER"],
|
|
161
|
-
"gcp_database_id" => ENV["PGHERO_GCP_DATABASE_ID"]
|
|
162
|
-
"azure_resource_id" => ENV["PGHERO_AZURE_RESOURCE_ID"]
|
|
148
|
+
"gcp_database_id" => ENV["PGHERO_GCP_DATABASE_ID"]
|
|
163
149
|
)
|
|
164
150
|
end
|
|
165
151
|
|
|
@@ -184,10 +170,6 @@ module PgHero
|
|
|
184
170
|
@databases
|
|
185
171
|
end
|
|
186
172
|
|
|
187
|
-
def primary_database
|
|
188
|
-
databases.values.first
|
|
189
|
-
end
|
|
190
|
-
|
|
191
173
|
def capture_query_stats(verbose: false)
|
|
192
174
|
each_database do |database|
|
|
193
175
|
next unless database.capture_query_stats?
|
|
@@ -224,6 +206,7 @@ module PgHero
|
|
|
224
206
|
# delete previous stats
|
|
225
207
|
# go database by database to use an index
|
|
226
208
|
# stats for old databases are not cleaned up since we can't use an index
|
|
209
|
+
# TODO clean queries
|
|
227
210
|
def clean_query_stats(before: nil)
|
|
228
211
|
each_database do |database|
|
|
229
212
|
database.clean_query_stats(before: before)
|
|
@@ -236,21 +219,6 @@ module PgHero
|
|
|
236
219
|
end
|
|
237
220
|
end
|
|
238
221
|
|
|
239
|
-
# private
|
|
240
|
-
def connection_config(model)
|
|
241
|
-
model.connection_db_config.configuration_hash
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
# private
|
|
245
|
-
def spec_name_key
|
|
246
|
-
:name
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
# private
|
|
250
|
-
def include_replicas_key
|
|
251
|
-
:include_hidden
|
|
252
|
-
end
|
|
253
|
-
|
|
254
222
|
private
|
|
255
223
|
|
|
256
224
|
def each_database
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2014-
|
|
3
|
+
Copyright (c) 2014-2024 Chart.js Contributors
|
|
4
4
|
|
|
5
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
6
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pghero
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 4.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew Kane
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '7.
|
|
18
|
+
version: '7.2'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '7.
|
|
25
|
+
version: '7.2'
|
|
26
26
|
email: andrew@ankane.org
|
|
27
27
|
executables: []
|
|
28
28
|
extensions: []
|
|
@@ -37,7 +37,6 @@ files:
|
|
|
37
37
|
- app/assets/javascripts/pghero/application.js
|
|
38
38
|
- app/assets/javascripts/pghero/chartkick.js
|
|
39
39
|
- app/assets/javascripts/pghero/highlight.min.js
|
|
40
|
-
- app/assets/javascripts/pghero/jquery.js
|
|
41
40
|
- app/assets/javascripts/pghero/nouislider.js
|
|
42
41
|
- app/assets/stylesheets/pghero/application.css
|
|
43
42
|
- app/assets/stylesheets/pghero/arduino-light.css
|
|
@@ -69,6 +68,8 @@ files:
|
|
|
69
68
|
- lib/generators/pghero/templates/config.yml.tt
|
|
70
69
|
- lib/generators/pghero/templates/query_stats.rb.tt
|
|
71
70
|
- lib/generators/pghero/templates/space_stats.rb.tt
|
|
71
|
+
- lib/generators/pghero/templates/upgrade_query_stats.rb.tt
|
|
72
|
+
- lib/generators/pghero/upgrade_query_stats_generator.rb
|
|
72
73
|
- lib/pghero.rb
|
|
73
74
|
- lib/pghero/connection.rb
|
|
74
75
|
- lib/pghero/database.rb
|
|
@@ -89,7 +90,7 @@ files:
|
|
|
89
90
|
- lib/pghero/methods/suggested_indexes.rb
|
|
90
91
|
- lib/pghero/methods/system.rb
|
|
91
92
|
- lib/pghero/methods/tables.rb
|
|
92
|
-
- lib/pghero/
|
|
93
|
+
- lib/pghero/query.rb
|
|
93
94
|
- lib/pghero/query_stats.rb
|
|
94
95
|
- lib/pghero/space_stats.rb
|
|
95
96
|
- lib/pghero/stats.rb
|
|
@@ -100,7 +101,6 @@ files:
|
|
|
100
101
|
- licenses/LICENSE-chartkick.js.txt
|
|
101
102
|
- licenses/LICENSE-date-fns.txt
|
|
102
103
|
- licenses/LICENSE-highlight.js.txt
|
|
103
|
-
- licenses/LICENSE-jquery.txt
|
|
104
104
|
- licenses/LICENSE-kurkle-color.txt
|
|
105
105
|
- licenses/LICENSE-nouislider.txt
|
|
106
106
|
homepage: https://github.com/ankane/pghero
|
|
@@ -114,14 +114,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
114
114
|
requirements:
|
|
115
115
|
- - ">="
|
|
116
116
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: '3.
|
|
117
|
+
version: '3.3'
|
|
118
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
119
|
requirements:
|
|
120
120
|
- - ">="
|
|
121
121
|
- !ruby/object:Gem::Version
|
|
122
122
|
version: '0'
|
|
123
123
|
requirements: []
|
|
124
|
-
rubygems_version:
|
|
124
|
+
rubygems_version: 4.0.16
|
|
125
125
|
specification_version: 4
|
|
126
126
|
summary: A performance dashboard for Postgres
|
|
127
127
|
test_files: []
|