pghero 2.4.1 → 2.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/README.md +19 -8
- data/app/assets/javascripts/pghero/application.js +1 -1
- data/app/controllers/pg_hero/home_controller.rb +79 -19
- data/app/helpers/pg_hero/home_helper.rb +11 -0
- data/app/views/pg_hero/home/_live_queries_table.html.erb +3 -1
- data/app/views/pg_hero/home/connections.html.erb +9 -0
- data/app/views/pg_hero/home/index.html.erb +2 -2
- data/app/views/pg_hero/home/maintenance.html.erb +16 -2
- data/app/views/pg_hero/home/space.html.erb +1 -1
- data/app/views/pg_hero/home/tune.html.erb +2 -1
- data/lib/generators/pghero/templates/config.yml.tt +11 -4
- data/lib/pghero.rb +29 -13
- data/lib/pghero/database.rb +81 -21
- data/lib/pghero/methods/basic.rb +32 -7
- data/lib/pghero/methods/connections.rb +35 -0
- data/lib/pghero/methods/explain.rb +1 -1
- data/lib/pghero/methods/maintenance.rb +3 -1
- data/lib/pghero/methods/queries.rb +6 -2
- data/lib/pghero/methods/query_stats.rb +93 -25
- data/lib/pghero/methods/space.rb +4 -0
- data/lib/pghero/methods/suggested_indexes.rb +1 -1
- data/lib/pghero/methods/system.rb +227 -15
- data/lib/pghero/methods/users.rb +4 -0
- data/lib/pghero/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 38e408389854af6f5a2a71ee70bc3d66c257a299b1a1fec255c279819db00482
|
4
|
+
data.tar.gz: 2f492cf50fd99032ec9ac9cfd03df70ef5c4d45ea55a0c14a5c608102b5aa106
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: edc72b96fb3c766f41682c22aa2287a99c610435687794d0a09395821a129709b0e464f7e9031d43cac7a6d1bc334cb419885dc32b87d9d4e58a75d810c3ba30
|
7
|
+
data.tar.gz: a4ad92136153cb3f607bddb0b289d3c180a6a653f7a6eac9b47ca6fe7fa04626923f7b84e80951577c9a94314c2487ac7dccb4cdd5f2483a3076473893bac13e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
## 2.7.0 (2020-08-04)
|
2
|
+
|
3
|
+
- Fixed CSRF vulnerability with non-session based authentication
|
4
|
+
- Added `database`, `user`, and `query_hash` options to `reset_query_stats` method
|
5
|
+
|
6
|
+
## 2.6.0 (2020-07-09)
|
7
|
+
|
8
|
+
- Added support for Postgres 13 beta 2
|
9
|
+
- Added support for non-integer explain timeout
|
10
|
+
|
11
|
+
## 2.5.1 (2020-06-23)
|
12
|
+
|
13
|
+
- Added support for `google-cloud-monitoring` >= 1
|
14
|
+
- Added support for `google-cloud-monitoring-v3`
|
15
|
+
- Fixed system stats not showing up in Rails 6 with environment variables
|
16
|
+
|
17
|
+
## 2.5.0 (2020-05-24)
|
18
|
+
|
19
|
+
- Added system stats for Google Cloud SQL and Azure Database
|
20
|
+
- Added experimental `filter_data` option
|
21
|
+
- Localized times on maintenance page
|
22
|
+
- Improved connection pooling
|
23
|
+
- Improved error message for non-Postgres connections
|
24
|
+
- Fixed more deprecation warnings in Ruby 2.7
|
25
|
+
|
26
|
+
## 2.4.2 (2020-04-16)
|
27
|
+
|
28
|
+
- Added `connections` method
|
29
|
+
- Fixed deprecation warnings in Ruby 2.7
|
30
|
+
|
1
31
|
## 2.4.1 (2019-11-21)
|
2
32
|
|
3
33
|
- Fixed file permissions on `highlight.pack.js`
|
data/README.md
CHANGED
@@ -10,17 +10,21 @@ A performance dashboard for Postgres
|
|
10
10
|
|
11
11
|
---
|
12
12
|
|
13
|
-
[![Screenshot](https://pghero.dokkuapp.com/assets/
|
13
|
+
[![Screenshot](https://pghero.dokkuapp.com/assets/pghero-f8abe426e6bf54bb7dba87b425bb809740ebd386208bcd280a7e802b053a1023.png)](https://pghero.dokkuapp.com/)
|
14
|
+
|
15
|
+
:tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
|
16
|
+
|
17
|
+
[![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)
|
14
18
|
|
15
19
|
## Installation
|
16
20
|
|
17
|
-
PgHero is available as a
|
21
|
+
PgHero is available as a Docker image, Linux package, and Rails engine.
|
18
22
|
|
19
23
|
Select your preferred method of installation to get started.
|
20
24
|
|
21
|
-
- [Rails](guides/Rails.md)
|
22
|
-
- [Linux](guides/Linux.md)
|
23
25
|
- [Docker](guides/Docker.md)
|
26
|
+
- [Linux](guides/Linux.md)
|
27
|
+
- [Rails](guides/Rails.md)
|
24
28
|
|
25
29
|
## Related Projects
|
26
30
|
|
@@ -31,10 +35,17 @@ Select your preferred method of installation to get started.
|
|
31
35
|
|
32
36
|
## Credits
|
33
37
|
|
34
|
-
A big thanks to [Craig Kerstiens](http://www.craigkerstiens.com/2013/01/10/more-on-postgres-performance/) and [Heroku](https://blog.heroku.com/archives/2013/5/10/more_insight_into_your_database_with_pgextras) for the initial queries and [Bootswatch](https://github.com/thomaspark/bootswatch) for the theme
|
38
|
+
A big thanks to [Craig Kerstiens](http://www.craigkerstiens.com/2013/01/10/more-on-postgres-performance/) and [Heroku](https://blog.heroku.com/archives/2013/5/10/more_insight_into_your_database_with_pgextras) for the initial queries and [Bootswatch](https://github.com/thomaspark/bootswatch) for the theme.
|
35
39
|
|
36
|
-
|
40
|
+
## History
|
37
41
|
|
38
|
-
|
42
|
+
View the [changelog](https://github.com/ankane/pghero/blob/master/CHANGELOG.md)
|
43
|
+
|
44
|
+
## Contributing
|
45
|
+
|
46
|
+
Everyone is encouraged to help improve this project. Here are a few ways you can help:
|
39
47
|
|
40
|
-
[
|
48
|
+
- [Report bugs](https://github.com/ankane/pghero/issues)
|
49
|
+
- Fix bugs and [submit pull requests](https://github.com/ankane/pghero/pulls)
|
50
|
+
- Write, clarify, or fix documentation
|
51
|
+
- Suggest or add new features
|
@@ -59,7 +59,7 @@ function initSlider() {
|
|
59
59
|
html = "Now";
|
60
60
|
}
|
61
61
|
} else {
|
62
|
-
html =
|
62
|
+
html = time.getDate() + " " + months[time.getMonth()] + " " + pad(time.getHours()) + ":" + pad(time.getMinutes());
|
63
63
|
}
|
64
64
|
$(selector).html(html);
|
65
65
|
}
|
@@ -2,7 +2,7 @@ module PgHero
|
|
2
2
|
class HomeController < ActionController::Base
|
3
3
|
layout "pg_hero/application"
|
4
4
|
|
5
|
-
protect_from_forgery
|
5
|
+
protect_from_forgery with: :exception
|
6
6
|
|
7
7
|
http_basic_authenticate_with name: PgHero.username, password: PgHero.password if PgHero.password
|
8
8
|
|
@@ -13,6 +13,11 @@ module PgHero
|
|
13
13
|
before_action :ensure_query_stats, only: [:queries]
|
14
14
|
|
15
15
|
if PgHero.config["override_csp"]
|
16
|
+
# note: this does not take into account asset hosts
|
17
|
+
# which can be a string with %d or a proc
|
18
|
+
# https://api.rubyonrails.org/classes/ActionView/Helpers/AssetUrlHelper.html
|
19
|
+
# users should set CSP manually if needed
|
20
|
+
# see https://github.com/ankane/pghero/issues/297
|
16
21
|
after_action do
|
17
22
|
response.headers["Content-Security-Policy"] = "default-src 'self' 'unsafe-inline'"
|
18
23
|
end
|
@@ -198,6 +203,11 @@ module PgHero
|
|
198
203
|
"1 week" => {duration: 1.week, period: 30.minutes},
|
199
204
|
"2 weeks" => {duration: 2.weeks, period: 1.hours}
|
200
205
|
}
|
206
|
+
if @database.system_stats_provider == :azure
|
207
|
+
# doesn't support 10, just 5 and 15
|
208
|
+
@periods["1 day"][:period] = 15.minutes
|
209
|
+
end
|
210
|
+
|
201
211
|
@duration = (params[:duration] || 1.hour).to_i
|
202
212
|
@period = (params[:period] || 60.seconds).to_i
|
203
213
|
|
@@ -209,22 +219,36 @@ module PgHero
|
|
209
219
|
end
|
210
220
|
|
211
221
|
def cpu_usage
|
212
|
-
render json: [{name: "CPU", data: @database.cpu_usage(system_params).map { |k, v| [k, v.round] }, library: chart_library_options}]
|
222
|
+
render json: [{name: "CPU", data: @database.cpu_usage(**system_params).map { |k, v| [k, v ? v.round : v] }, library: chart_library_options}]
|
213
223
|
end
|
214
224
|
|
215
225
|
def connection_stats
|
216
|
-
render json: [{name: "Connections", data: @database.connection_stats(system_params), library: chart_library_options}]
|
226
|
+
render json: [{name: "Connections", data: @database.connection_stats(**system_params), library: chart_library_options}]
|
217
227
|
end
|
218
228
|
|
219
229
|
def replication_lag_stats
|
220
|
-
render json: [{name: "Lag", data: @database.replication_lag_stats(system_params), library: chart_library_options}]
|
230
|
+
render json: [{name: "Lag", data: @database.replication_lag_stats(**system_params), library: chart_library_options}]
|
221
231
|
end
|
222
232
|
|
223
233
|
def load_stats
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
234
|
+
stats =
|
235
|
+
case @database.system_stats_provider
|
236
|
+
when :azure
|
237
|
+
[
|
238
|
+
{name: "IO Consumption", data: @database.azure_stats("io_consumption_percent", **system_params), library: chart_library_options}
|
239
|
+
]
|
240
|
+
when :gcp
|
241
|
+
[
|
242
|
+
{name: "Read Ops", data: @database.read_iops_stats(**system_params).map { |k, v| [k, v ? v.round : v] }, library: chart_library_options},
|
243
|
+
{name: "Write Ops", data: @database.write_iops_stats(**system_params).map { |k, v| [k, v ? v.round : v] }, library: chart_library_options}
|
244
|
+
]
|
245
|
+
else
|
246
|
+
[
|
247
|
+
{name: "Read IOPS", data: @database.read_iops_stats(**system_params).map { |k, v| [k, v ? v.round : v] }, library: chart_library_options},
|
248
|
+
{name: "Write IOPS", data: @database.write_iops_stats(**system_params).map { |k, v| [k, v ? v.round : v] }, library: chart_library_options}
|
249
|
+
]
|
250
|
+
end
|
251
|
+
render json: stats
|
228
252
|
end
|
229
253
|
|
230
254
|
def free_space_stats
|
@@ -270,17 +294,48 @@ module PgHero
|
|
270
294
|
|
271
295
|
def connections
|
272
296
|
@title = "Connections"
|
273
|
-
|
274
|
-
|
297
|
+
connections = @database.connections
|
298
|
+
|
299
|
+
@total_connections = connections.count
|
300
|
+
@connection_sources = group_connections(connections, [:database, :user, :source, :ip])
|
301
|
+
@connections_by_database = group_connections_by_key(connections, :database)
|
302
|
+
@connections_by_user = group_connections_by_key(connections, :user)
|
303
|
+
|
304
|
+
if params[:security] && @database.server_version_num >= 90500
|
305
|
+
connections.each do |connection|
|
306
|
+
connection[:ssl_status] =
|
307
|
+
if connection[:ssl]
|
308
|
+
# no way to tell if client used verify-full
|
309
|
+
# so connection may not be actually secure
|
310
|
+
"SSL"
|
311
|
+
else
|
312
|
+
# variety of reasons for no SSL
|
313
|
+
if !connection[:database].present?
|
314
|
+
"Internal Process"
|
315
|
+
elsif !connection[:ip]
|
316
|
+
if connection[:state]
|
317
|
+
"Socket"
|
318
|
+
else
|
319
|
+
# tcp or socket, don't have permission to tell
|
320
|
+
"No SSL"
|
321
|
+
end
|
322
|
+
else
|
323
|
+
# tcp
|
324
|
+
# could separate out localhost since this should be safe
|
325
|
+
"No SSL"
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
275
329
|
|
276
|
-
|
277
|
-
|
330
|
+
@connections_by_ssl_status = group_connections_by_key(connections, :ssl_status)
|
331
|
+
end
|
278
332
|
end
|
279
333
|
|
280
334
|
def maintenance
|
281
335
|
@title = "Maintenance"
|
282
336
|
@maintenance_info = @database.maintenance_info
|
283
337
|
@time_zone = PgHero.time_zone
|
338
|
+
@show_dead_rows = params[:dead_rows]
|
284
339
|
end
|
285
340
|
|
286
341
|
def kill
|
@@ -363,7 +418,8 @@ module PgHero
|
|
363
418
|
def system_params
|
364
419
|
{
|
365
420
|
duration: params[:duration],
|
366
|
-
period: params[:period]
|
421
|
+
period: params[:period],
|
422
|
+
series: true
|
367
423
|
}.delete_if { |_, v| v.nil? }
|
368
424
|
end
|
369
425
|
|
@@ -376,18 +432,22 @@ module PgHero
|
|
376
432
|
@show_details = @historical_query_stats_enabled && @database.supports_query_hash?
|
377
433
|
end
|
378
434
|
|
379
|
-
def group_connections(
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
435
|
+
def group_connections(connections, keys)
|
436
|
+
connections
|
437
|
+
.group_by { |conn| conn.slice(*keys) }
|
438
|
+
.map { |k, v| k.merge(total_connections: v.count) }
|
439
|
+
.sort_by { |v| [-v[:total_connections]] + keys.map { |k| v[k].to_s } }
|
440
|
+
end
|
441
|
+
|
442
|
+
def group_connections_by_key(connections, key)
|
443
|
+
group_connections(connections, [key]).map { |v| [v[key], v[:total_connections]] }.to_h
|
385
444
|
end
|
386
445
|
|
387
446
|
def check_api
|
388
447
|
render_text "No support for Rails API. See https://github.com/pghero/pghero for a standalone app." if Rails.application.config.try(:api_only)
|
389
448
|
end
|
390
449
|
|
450
|
+
# TODO return error status code
|
391
451
|
def render_text(message)
|
392
452
|
render plain: message
|
393
453
|
end
|
@@ -15,5 +15,16 @@ module PgHero
|
|
15
15
|
def pghero_js_var(name, value)
|
16
16
|
"var #{name} = #{json_escape(value.to_json(root: false))};".html_safe
|
17
17
|
end
|
18
|
+
|
19
|
+
def pghero_remove_index(query)
|
20
|
+
if query[:columns]
|
21
|
+
columns = query[:columns].map(&:to_sym)
|
22
|
+
columns = columns.first if columns.size == 1
|
23
|
+
end
|
24
|
+
ret = String.new("remove_index #{query[:table].to_sym.inspect}")
|
25
|
+
ret << ", name: #{(query[:name] || query[:index]).to_s.inspect}"
|
26
|
+
ret << ", column: #{columns.inspect}" if columns
|
27
|
+
ret
|
28
|
+
end
|
18
29
|
end
|
19
30
|
end
|
@@ -30,7 +30,9 @@
|
|
30
30
|
<% end %>
|
31
31
|
</td>
|
32
32
|
<td class="text-right">
|
33
|
-
|
33
|
+
<% unless @database.filter_data %>
|
34
|
+
<%= button_to "Explain", explain_path, params: {query: query[:query]}, form: {target: "_blank"}, class: "btn btn-info" %>
|
35
|
+
<% end %>
|
34
36
|
<%= button_to "Kill", kill_path(pid: query[:pid]), class: "btn btn-danger" %>
|
35
37
|
</td>
|
36
38
|
</tr>
|
@@ -18,6 +18,15 @@
|
|
18
18
|
new Chartkick.PieChart("chart-2", <%= json_escape(@connections_by_user.to_json).html_safe %>);
|
19
19
|
</script>
|
20
20
|
|
21
|
+
<% if @connections_by_ssl_status %>
|
22
|
+
<h3>By Security</h3>
|
23
|
+
|
24
|
+
<div id="chart-3" class="chart" style="height: 260px; line-height: 260px; margin-bottom: 20px;">Loading...</div>
|
25
|
+
<script>
|
26
|
+
new Chartkick.PieChart("chart-3", <%= json_escape(@connections_by_ssl_status.to_json).html_safe %>);
|
27
|
+
</script>
|
28
|
+
<% end %>
|
29
|
+
|
21
30
|
<%= render partial: "connections_table", locals: {connection_sources: @connection_sources} %>
|
22
31
|
<% end %>
|
23
32
|
</div>
|
@@ -387,7 +387,7 @@
|
|
387
387
|
<pre>rails generate migration remove_unneeded_indexes</pre>
|
388
388
|
<p>And paste</p>
|
389
389
|
<pre style="overflow: scroll; white-space: pre; word-break: normal;"><% @duplicate_indexes.each do |query| %>
|
390
|
-
|
390
|
+
<%= pghero_remove_index(query[:unneeded_index]) %><% end %></pre>
|
391
391
|
</div>
|
392
392
|
|
393
393
|
<table class="table duplicate-indexes">
|
@@ -491,7 +491,7 @@ pg_stat_statements.track = all</pre>
|
|
491
491
|
<pre>rails generate migration remove_unused_indexes</pre>
|
492
492
|
<p>And paste</p>
|
493
493
|
<pre style="overflow: scroll; white-space: pre; word-break: normal;"><% @unused_indexes.each do |query| %>
|
494
|
-
|
494
|
+
<%= pghero_remove_index(query)%><% end %></pre>
|
495
495
|
</div>
|
496
496
|
|
497
497
|
<table class="table">
|
@@ -7,6 +7,9 @@
|
|
7
7
|
<th>Table</th>
|
8
8
|
<th style="width: 20%;">Last Vacuum</th>
|
9
9
|
<th style="width: 20%;">Last Analyze</th>
|
10
|
+
<% if @show_dead_rows %>
|
11
|
+
<th style="width: 20%;">Dead Rows</th>
|
12
|
+
<% end %>
|
10
13
|
</tr>
|
11
14
|
</thead>
|
12
15
|
<tbody>
|
@@ -21,7 +24,7 @@
|
|
21
24
|
<td>
|
22
25
|
<% time = [table[:last_autovacuum], table[:last_vacuum]].compact.max %>
|
23
26
|
<% if time %>
|
24
|
-
<%= time.in_time_zone(@time_zone)
|
27
|
+
<%= l time.in_time_zone(@time_zone), format: :short %>
|
25
28
|
<% else %>
|
26
29
|
<span class="text-muted">Unknown</span>
|
27
30
|
<% end %>
|
@@ -29,11 +32,22 @@
|
|
29
32
|
<td>
|
30
33
|
<% time = [table[:last_autoanalyze], table[:last_analyze]].compact.max %>
|
31
34
|
<% if time %>
|
32
|
-
<%= time.in_time_zone(@time_zone)
|
35
|
+
<%= l time.in_time_zone(@time_zone), format: :short %>
|
33
36
|
<% else %>
|
34
37
|
<span class="text-muted">Unknown</span>
|
35
38
|
<% end %>
|
36
39
|
</td>
|
40
|
+
<% if @show_dead_rows %>
|
41
|
+
<td>
|
42
|
+
<% if table[:live_rows] != 0 %>
|
43
|
+
<%# use live rows only for denominator to make it easier to compare with autovacuum_vacuum_scale_factor %>
|
44
|
+
<%# it's not a true percentage, since it can go above 100% %>
|
45
|
+
<%= (100.0 * table[:dead_rows] / table[:live_rows]).round %>%
|
46
|
+
<% else %>
|
47
|
+
<span class="text-muted">Unknown</span>
|
48
|
+
<% end %>
|
49
|
+
</td>
|
50
|
+
<% end %>
|
37
51
|
</tr>
|
38
52
|
<% end %>
|
39
53
|
</tbody>
|
@@ -33,7 +33,7 @@
|
|
33
33
|
<pre>rails generate migration remove_unused_indexes</pre>
|
34
34
|
<p>And paste</p>
|
35
35
|
<pre style="overflow: scroll; white-space: pre; word-break: normal;"><% @unused_indexes.sort_by { |q| [-q[:size_bytes], q[:index]] }.each do |query| %>
|
36
|
-
|
36
|
+
<%= pghero_remove_index(query) %><% end %></pre>
|
37
37
|
</div>
|
38
38
|
<% end %>
|
39
39
|
|
@@ -18,7 +18,8 @@
|
|
18
18
|
</tbody>
|
19
19
|
</table>
|
20
20
|
|
21
|
-
|
21
|
+
<% version_parts = @database.server_version.split(" ").first.split(".") %>
|
22
|
+
<p>Check out <%= link_to "PgTune", "https://pgtune.leopard.in.ua/", target: "_blank" %> for recommendations. DB version is <%= version_parts[0].to_i >= 10 ? version_parts[0] : version_parts.first(2).join(".") %>.</p>
|
22
23
|
</div>
|
23
24
|
|
24
25
|
<% if @autovacuum_settings %>
|
@@ -3,6 +3,11 @@ databases:
|
|
3
3
|
# Database URL (defaults to app database)
|
4
4
|
# url: <%%= ENV["DATABASE_URL"] %>
|
5
5
|
|
6
|
+
# System stats
|
7
|
+
# aws_db_instance_identifier: my-instance
|
8
|
+
# gcp_database_id: my-project:my-instance
|
9
|
+
# azure_resource_id: my-resource-id
|
10
|
+
|
6
11
|
# Add more databases
|
7
12
|
# other:
|
8
13
|
# url: <%%= ENV["OTHER_DATABASE_URL"] %>
|
@@ -27,13 +32,15 @@ databases:
|
|
27
32
|
|
28
33
|
# Basic authentication
|
29
34
|
# username: admin
|
30
|
-
# password:
|
35
|
+
# password: <%%= ENV["PGHERO_PASSWORD"] %>
|
31
36
|
|
32
37
|
# Stats database URL (defaults to app database)
|
33
38
|
# stats_database_url: <%%= ENV["PGHERO_STATS_DATABASE_URL"] %>
|
34
39
|
|
35
40
|
# AWS configuration (defaults to app AWS config)
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# aws_secret_access_key: ...
|
41
|
+
# aws_access_key_id: <%%= ENV["AWS_ACCESS_KEY_ID"] %>
|
42
|
+
# aws_secret_access_key: <%%= ENV["AWS_SECRET_ACCESS_KEY"] %>
|
39
43
|
# aws_region: us-east-1
|
44
|
+
|
45
|
+
# Filter data from queries (experimental)
|
46
|
+
# filter_data: true
|
data/lib/pghero.rb
CHANGED
@@ -34,24 +34,27 @@ module PgHero
|
|
34
34
|
class Error < StandardError; end
|
35
35
|
class NotEnabled < Error; end
|
36
36
|
|
37
|
+
MUTEX = Mutex.new
|
38
|
+
|
37
39
|
# settings
|
38
40
|
class << self
|
39
|
-
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
|
41
|
+
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, :filter_data
|
40
42
|
end
|
41
43
|
self.long_running_query_sec = (ENV["PGHERO_LONG_RUNNING_QUERY_SEC"] || 60).to_i
|
42
44
|
self.slow_query_ms = (ENV["PGHERO_SLOW_QUERY_MS"] || 20).to_i
|
43
45
|
self.slow_query_calls = (ENV["PGHERO_SLOW_QUERY_CALLS"] || 100).to_i
|
44
|
-
self.explain_timeout_sec = (ENV["PGHERO_EXPLAIN_TIMEOUT_SEC"] || 10).
|
46
|
+
self.explain_timeout_sec = (ENV["PGHERO_EXPLAIN_TIMEOUT_SEC"] || 10).to_f
|
45
47
|
self.total_connections_threshold = (ENV["PGHERO_TOTAL_CONNECTIONS_THRESHOLD"] || 500).to_i
|
46
48
|
self.cache_hit_rate_threshold = 99
|
47
49
|
self.env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
48
50
|
self.show_migrations = true
|
49
51
|
self.config_path = ENV["PGHERO_CONFIG_PATH"] || "config/pghero.yml"
|
52
|
+
self.filter_data = ENV["PGHERO_FILTER_DATA"].to_s.size > 0
|
50
53
|
|
51
54
|
class << self
|
52
55
|
extend Forwardable
|
53
56
|
def_delegators :primary_database, :access_key_id, :analyze, :analyze_tables, :autoindex, :autovacuum_danger,
|
54
|
-
:best_index, :blocked_queries, :connection_sources, :connection_states, :connection_stats,
|
57
|
+
:best_index, :blocked_queries, :connections, :connection_sources, :connection_states, :connection_stats,
|
55
58
|
:cpu_usage, :create_user, :database_size, :db_instance_identifier, :disable_query_stats, :drop_user,
|
56
59
|
:duplicate_indexes, :enable_query_stats, :explain, :historical_query_stats_enabled?, :index_caching,
|
57
60
|
:index_hit_rate, :index_usage, :indexes, :invalid_constraints, :invalid_indexes, :kill, :kill_all, :kill_long_running_queries,
|
@@ -116,11 +119,18 @@ module PgHero
|
|
116
119
|
|
117
120
|
if databases.empty?
|
118
121
|
databases["primary"] = {
|
119
|
-
"url" => ENV["PGHERO_DATABASE_URL"] || ActiveRecord::Base.connection_config
|
120
|
-
"db_instance_identifier" => ENV["PGHERO_DB_INSTANCE_IDENTIFIER"]
|
122
|
+
"url" => ENV["PGHERO_DATABASE_URL"] || ActiveRecord::Base.connection_config
|
121
123
|
}
|
122
124
|
end
|
123
125
|
|
126
|
+
if databases.size == 1
|
127
|
+
databases.values.first.merge!(
|
128
|
+
"db_instance_identifier" => ENV["PGHERO_DB_INSTANCE_IDENTIFIER"],
|
129
|
+
"gcp_database_id" => ENV["PGHERO_GCP_DATABASE_ID"],
|
130
|
+
"azure_resource_id" => ENV["PGHERO_AZURE_RESOURCE_ID"]
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
124
134
|
{
|
125
135
|
"databases" => databases
|
126
136
|
}
|
@@ -128,14 +138,20 @@ module PgHero
|
|
128
138
|
end
|
129
139
|
end
|
130
140
|
|
141
|
+
# ensure we only have one copy of databases
|
142
|
+
# so there's only one connection pool per database
|
131
143
|
def databases
|
132
|
-
@databases
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
144
|
+
unless defined?(@databases)
|
145
|
+
# only use mutex on initialization
|
146
|
+
MUTEX.synchronize do
|
147
|
+
# return if another process initialized while we were waiting
|
148
|
+
return @databases if defined?(@databases)
|
149
|
+
|
150
|
+
@databases = config["databases"].map { |id, c| [id.to_sym, Database.new(id, c)] }.to_h
|
151
|
+
end
|
138
152
|
end
|
153
|
+
|
154
|
+
@databases
|
139
155
|
end
|
140
156
|
|
141
157
|
def primary_database
|
@@ -180,13 +196,13 @@ module PgHero
|
|
180
196
|
# stats for old databases are not cleaned up since we can't use an index
|
181
197
|
def clean_query_stats
|
182
198
|
each_database do |database|
|
183
|
-
|
199
|
+
database.clean_query_stats
|
184
200
|
end
|
185
201
|
end
|
186
202
|
|
187
203
|
def clean_space_stats
|
188
204
|
each_database do |database|
|
189
|
-
|
205
|
+
database.clean_space_stats
|
190
206
|
end
|
191
207
|
end
|
192
208
|
|