pghero_fork 2.7.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +391 -0
- data/CONTRIBUTING.md +42 -0
- data/LICENSE.txt +22 -0
- data/README.md +3 -0
- data/app/assets/images/pghero/favicon.png +0 -0
- data/app/assets/javascripts/pghero/Chart.bundle.js +20755 -0
- data/app/assets/javascripts/pghero/application.js +158 -0
- data/app/assets/javascripts/pghero/chartkick.js +2436 -0
- data/app/assets/javascripts/pghero/highlight.pack.js +2 -0
- data/app/assets/javascripts/pghero/jquery.js +10872 -0
- data/app/assets/javascripts/pghero/nouislider.js +2672 -0
- data/app/assets/stylesheets/pghero/application.css +514 -0
- data/app/assets/stylesheets/pghero/arduino-light.css +86 -0
- data/app/assets/stylesheets/pghero/nouislider.css +310 -0
- data/app/controllers/pg_hero/home_controller.rb +449 -0
- data/app/helpers/pg_hero/home_helper.rb +30 -0
- data/app/views/layouts/pg_hero/application.html.erb +68 -0
- data/app/views/pg_hero/home/_connections_table.html.erb +16 -0
- data/app/views/pg_hero/home/_live_queries_table.html.erb +51 -0
- data/app/views/pg_hero/home/_queries_table.html.erb +72 -0
- data/app/views/pg_hero/home/_query_stats_slider.html.erb +16 -0
- data/app/views/pg_hero/home/_suggested_index.html.erb +18 -0
- data/app/views/pg_hero/home/connections.html.erb +32 -0
- data/app/views/pg_hero/home/explain.html.erb +27 -0
- data/app/views/pg_hero/home/index.html.erb +518 -0
- data/app/views/pg_hero/home/index_bloat.html.erb +72 -0
- data/app/views/pg_hero/home/live_queries.html.erb +11 -0
- data/app/views/pg_hero/home/maintenance.html.erb +55 -0
- data/app/views/pg_hero/home/queries.html.erb +33 -0
- data/app/views/pg_hero/home/relation_space.html.erb +14 -0
- data/app/views/pg_hero/home/show_query.html.erb +106 -0
- data/app/views/pg_hero/home/space.html.erb +83 -0
- data/app/views/pg_hero/home/system.html.erb +34 -0
- data/app/views/pg_hero/home/tune.html.erb +53 -0
- data/config/routes.rb +32 -0
- data/lib/generators/pghero/config_generator.rb +13 -0
- data/lib/generators/pghero/query_stats_generator.rb +18 -0
- data/lib/generators/pghero/space_stats_generator.rb +18 -0
- data/lib/generators/pghero/templates/config.yml.tt +46 -0
- data/lib/generators/pghero/templates/query_stats.rb.tt +15 -0
- data/lib/generators/pghero/templates/space_stats.rb.tt +13 -0
- data/lib/pghero.rb +246 -0
- data/lib/pghero/connection.rb +5 -0
- data/lib/pghero/database.rb +175 -0
- data/lib/pghero/engine.rb +16 -0
- data/lib/pghero/methods/basic.rb +160 -0
- data/lib/pghero/methods/connections.rb +77 -0
- data/lib/pghero/methods/constraints.rb +30 -0
- data/lib/pghero/methods/explain.rb +29 -0
- data/lib/pghero/methods/indexes.rb +332 -0
- data/lib/pghero/methods/kill.rb +28 -0
- data/lib/pghero/methods/maintenance.rb +93 -0
- data/lib/pghero/methods/queries.rb +75 -0
- data/lib/pghero/methods/query_stats.rb +349 -0
- data/lib/pghero/methods/replication.rb +74 -0
- data/lib/pghero/methods/sequences.rb +124 -0
- data/lib/pghero/methods/settings.rb +37 -0
- data/lib/pghero/methods/space.rb +141 -0
- data/lib/pghero/methods/suggested_indexes.rb +329 -0
- data/lib/pghero/methods/system.rb +287 -0
- data/lib/pghero/methods/tables.rb +68 -0
- data/lib/pghero/methods/users.rb +87 -0
- data/lib/pghero/query_stats.rb +5 -0
- data/lib/pghero/space_stats.rb +5 -0
- data/lib/pghero/stats.rb +6 -0
- data/lib/pghero/version.rb +3 -0
- data/lib/tasks/pghero.rake +27 -0
- data/licenses/LICENSE-chart.js.txt +9 -0
- data/licenses/LICENSE-chartkick.js.txt +22 -0
- data/licenses/LICENSE-highlight.js.txt +29 -0
- data/licenses/LICENSE-jquery.txt +20 -0
- data/licenses/LICENSE-moment.txt +22 -0
- data/licenses/LICENSE-nouislider.txt +21 -0
- metadata +130 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
<div class="content">
|
2
|
+
<h1>Index Bloat</h1>
|
3
|
+
|
4
|
+
<% if @index_bloat.any? %>
|
5
|
+
|
6
|
+
<p>Indexes can become <%= link_to "bloated over time", "https://www.compose.com/articles/postgresql-bloat-origins-monitoring-and-managing/", target: "_blank" %>. Recreate them to remove bloat.</p>
|
7
|
+
|
8
|
+
<p>For each index, run:</p>
|
9
|
+
<pre><code>CREATE INDEX CONCURRENTLY new_index ...;
|
10
|
+
ANALYZE table;
|
11
|
+
DROP INDEX CONCURRENTLY index;
|
12
|
+
ANALYZE table;
|
13
|
+
ALTER INDEX new_index RENAME TO index;</code></pre>
|
14
|
+
|
15
|
+
<p>
|
16
|
+
<% if @show_sql %>
|
17
|
+
<%= link_to "Hide SQL", {} %>
|
18
|
+
<% else %>
|
19
|
+
<%= link_to "Show SQL", {sql: "t"} %>
|
20
|
+
<% end %>
|
21
|
+
for each index
|
22
|
+
</p>
|
23
|
+
|
24
|
+
<table class="table">
|
25
|
+
<thead>
|
26
|
+
<tr>
|
27
|
+
<th>Index</th>
|
28
|
+
<th style="width: 15%;">Bloat</th>
|
29
|
+
<th style="width: 15%;">Size</th>
|
30
|
+
</tr>
|
31
|
+
</thead>
|
32
|
+
<tbody>
|
33
|
+
<% @index_bloat.each do |index| %>
|
34
|
+
<tr>
|
35
|
+
<td>
|
36
|
+
<span style="word-break: break-all;"><%= index[:index] %></span>
|
37
|
+
<% if index[:primary] %>
|
38
|
+
<span class="primary-key">PRIMARY</span>
|
39
|
+
<% end %>
|
40
|
+
</td>
|
41
|
+
<td><%= PgHero.pretty_size(index[:bloat_bytes]) %></td>
|
42
|
+
<td><%= PgHero.pretty_size(index[:index_bytes]) %></td>
|
43
|
+
</tr>
|
44
|
+
<% if @show_sql && !index[:primary] %>
|
45
|
+
<tr>
|
46
|
+
<td colspan="3" style="border-top: none; padding: 0;">
|
47
|
+
<% new_index = "new_#{index[:index]}".first(63) %>
|
48
|
+
<pre><code><%= index[:definition].sub(" INDEX ", " INDEX CONCURRENTLY \n ").sub(index[:index], new_index) %>;
|
49
|
+
|
50
|
+
ANALYZE <%= pghero_pretty_ident(index[:table], schema: index[:schema]) %>;
|
51
|
+
|
52
|
+
DROP INDEX CONCURRENTLY
|
53
|
+
<%= pghero_pretty_ident(index[:index]) %>;
|
54
|
+
|
55
|
+
ANALYZE <%= pghero_pretty_ident(index[:table], schema: index[:schema]) %>;
|
56
|
+
|
57
|
+
ALTER INDEX <%= pghero_pretty_ident(new_index) %>
|
58
|
+
RENAME TO <%= pghero_pretty_ident(index[:index]) %>;</code></pre>
|
59
|
+
</td>
|
60
|
+
</tr>
|
61
|
+
<% end %>
|
62
|
+
<% end %>
|
63
|
+
</tbody>
|
64
|
+
</table>
|
65
|
+
<% else %>
|
66
|
+
<p>No significant index bloat!</p>
|
67
|
+
<% end %>
|
68
|
+
</div>
|
69
|
+
|
70
|
+
<script>
|
71
|
+
highlightQueries();
|
72
|
+
</script>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<div class="content">
|
2
|
+
<h1>Live Queries</h1>
|
3
|
+
|
4
|
+
<p><%= pluralize(@running_queries.size, "query") %></p>
|
5
|
+
|
6
|
+
<%= render partial: "live_queries_table", locals: {queries: @running_queries, vacuum_progress: @vacuum_progress} %>
|
7
|
+
|
8
|
+
<p><%= button_to "Kill all connections", kill_all_path, class: "btn btn-danger" %></p>
|
9
|
+
|
10
|
+
<p class="text-muted">You may need to restart your app servers afterwards.</p>
|
11
|
+
</div>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<div class="content">
|
2
|
+
<h1>Maintenance</h1>
|
3
|
+
|
4
|
+
<table class="table">
|
5
|
+
<thead>
|
6
|
+
<tr>
|
7
|
+
<th>Table</th>
|
8
|
+
<th style="width: 20%;">Last Vacuum</th>
|
9
|
+
<th style="width: 20%;">Last Analyze</th>
|
10
|
+
<% if @show_dead_rows %>
|
11
|
+
<th style="width: 20%;">Dead Rows</th>
|
12
|
+
<% end %>
|
13
|
+
</tr>
|
14
|
+
</thead>
|
15
|
+
<tbody>
|
16
|
+
<% @maintenance_info.each do |table| %>
|
17
|
+
<tr>
|
18
|
+
<td>
|
19
|
+
<%= table[:table] %>
|
20
|
+
<% if table[:schema] != "public" %>
|
21
|
+
<span class="text-muted"><%= table[:schema] %></span>
|
22
|
+
<% end %>
|
23
|
+
</td>
|
24
|
+
<td>
|
25
|
+
<% time = [table[:last_autovacuum], table[:last_vacuum]].compact.max %>
|
26
|
+
<% if time %>
|
27
|
+
<%= l time.in_time_zone(@time_zone), format: :short %>
|
28
|
+
<% else %>
|
29
|
+
<span class="text-muted">Unknown</span>
|
30
|
+
<% end %>
|
31
|
+
</td>
|
32
|
+
<td>
|
33
|
+
<% time = [table[:last_autoanalyze], table[:last_analyze]].compact.max %>
|
34
|
+
<% if time %>
|
35
|
+
<%= l time.in_time_zone(@time_zone), format: :short %>
|
36
|
+
<% else %>
|
37
|
+
<span class="text-muted">Unknown</span>
|
38
|
+
<% end %>
|
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 %>
|
51
|
+
</tr>
|
52
|
+
<% end %>
|
53
|
+
</tbody>
|
54
|
+
</table>
|
55
|
+
</div>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<div class="content">
|
2
|
+
<% if @query_stats_enabled %>
|
3
|
+
<%= button_to "Reset", reset_query_stats_path, class: "btn btn-danger", style: "float: right;" %>
|
4
|
+
<% end %>
|
5
|
+
|
6
|
+
<h1 style="float: left;">Queries</h1>
|
7
|
+
|
8
|
+
<% if @historical_query_stats_enabled %>
|
9
|
+
<%= render partial: "query_stats_slider" %>
|
10
|
+
<% elsif @database.query_stats_table_exists? && (columns = @database.missing_query_stats_columns).any? %>
|
11
|
+
<div style="clear: both;">
|
12
|
+
<p>Add missing columns to re-enable historical query stats.</p>
|
13
|
+
<pre><code><% @database.missing_query_stats_columns.each do |column| %>ALTER TABLE pghero_query_stats ADD COLUMN "<%= column %>" <%= column == "query_hash" ? "bigint" : "text" %>;
|
14
|
+
<% end %></code></pre>
|
15
|
+
<p>Then restart the web server.</p>
|
16
|
+
</div>
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<% if @query_stats_enabled %>
|
20
|
+
<% if @error %>
|
21
|
+
<div class="alert alert-danger">Cannot understand start or end time.</div>
|
22
|
+
<% elsif @query_stats.any? || @historical_query_stats_enabled %>
|
23
|
+
<%= render partial: "queries_table", locals: {queries: @query_stats, sort_headers: true} %>
|
24
|
+
<script>
|
25
|
+
highlightQueries();
|
26
|
+
</script>
|
27
|
+
<% else %>
|
28
|
+
<p>Stats are not available yet. Come back soon!</p>
|
29
|
+
<% end %>
|
30
|
+
<% else %>
|
31
|
+
<p>Query stats are not enabled.</p>
|
32
|
+
<% end %>
|
33
|
+
</div>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div class="content">
|
2
|
+
<h1>
|
3
|
+
<%= @relation %>
|
4
|
+
<% if @schema != "public" %>
|
5
|
+
<small><%= @schema %></small>
|
6
|
+
<% end %>
|
7
|
+
</h1>
|
8
|
+
|
9
|
+
<h1>Size</h1>
|
10
|
+
<div id="chart-1" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
11
|
+
<script>
|
12
|
+
new Chartkick.LineChart("chart-1", <%= json_escape(@chart_data.to_json).html_safe %>, {colors: ["#5bc0de"], legend: false, min: null, bytes: true, library: {tooltips: {intersect: false, mode: "index"}}})
|
13
|
+
</script>
|
14
|
+
</div>
|
@@ -0,0 +1,106 @@
|
|
1
|
+
<div class="content">
|
2
|
+
<pre><code style="max-height: 230px; overflow: hidden;" onclick="this.style.maxHeight = 'none';"><%= @query %></code></pre>
|
3
|
+
<script>
|
4
|
+
highlightQueries()
|
5
|
+
</script>
|
6
|
+
|
7
|
+
<% if @explainable_query %>
|
8
|
+
<p>
|
9
|
+
<%= button_to "Explain", explain_path, params: {query: @explainable_query}, form: {target: "_blank"}, class: "btn btn-info" %>
|
10
|
+
</p>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<% if @origins && @origins.keys.select { |k| k.length > 0 }.any? %>
|
14
|
+
<table style="table-layout: auto;">
|
15
|
+
<thead>
|
16
|
+
<tr>
|
17
|
+
<th colspan="2">
|
18
|
+
<div style="float: right;">Approx. Time</div>
|
19
|
+
Origin
|
20
|
+
</th>
|
21
|
+
</tr>
|
22
|
+
</thead>
|
23
|
+
<tbody>
|
24
|
+
<% @origins.sort_by { |o, c| [-c, o.to_s] }.each do |origin, count| %>
|
25
|
+
<tr>
|
26
|
+
<td class="origin" style="width: 90%;">
|
27
|
+
<% if origin.length > 0 %>
|
28
|
+
<%= origin %>
|
29
|
+
<% else %>
|
30
|
+
<span class="text-muted">Unknown</span>
|
31
|
+
<% end %>
|
32
|
+
</td>
|
33
|
+
<td style="text-align: right; width: 10%;">
|
34
|
+
<% pct = (100.0 * count / @total_count).round %>
|
35
|
+
<% if pct == 0 %>
|
36
|
+
< 1%
|
37
|
+
<% else %>
|
38
|
+
<%= pct %>%
|
39
|
+
<% end %>
|
40
|
+
</td>
|
41
|
+
</tr>
|
42
|
+
<% end %>
|
43
|
+
</tbody>
|
44
|
+
</table>
|
45
|
+
<% end %>
|
46
|
+
|
47
|
+
<!-- chart -->
|
48
|
+
<% if @chart_data %>
|
49
|
+
<h1>Total Time <small>ms</small></h1>
|
50
|
+
<div id="chart-1" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
51
|
+
<script>
|
52
|
+
new Chartkick.LineChart("chart-1", <%= json_escape(@chart_data.to_json).html_safe %>, {colors: ["#5bc0de"], legend: false, library: {tooltips: {intersect: false, mode: "index"}}})
|
53
|
+
</script>
|
54
|
+
|
55
|
+
<h1>Average Time <small>ms</small></h1>
|
56
|
+
<div id="chart-2" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
57
|
+
<script>
|
58
|
+
new Chartkick.LineChart("chart-2", <%= json_escape(@chart2_data.to_json).html_safe %>, {colors: ["#5bc0de"], legend: false, library: {tooltips: {intersect: false, mode: "index"}}})
|
59
|
+
</script>
|
60
|
+
|
61
|
+
<h1>Calls</h1>
|
62
|
+
<div id="chart-3" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
63
|
+
<script>
|
64
|
+
new Chartkick.LineChart("chart-3", <%= json_escape(@chart3_data.to_json).html_safe %>, {colors: ["#5bc0de"], legend: false, library: {tooltips: {intersect: false, mode: "index"}}})
|
65
|
+
</script>
|
66
|
+
<% else %>
|
67
|
+
<p>
|
68
|
+
Enable
|
69
|
+
<%= link_to "historical query stats", "https://github.com/ankane/pghero", target: "_blank" %>
|
70
|
+
to see more details
|
71
|
+
</p>
|
72
|
+
<% end %>
|
73
|
+
|
74
|
+
<!-- table info -->
|
75
|
+
<% if @tables.any? %>
|
76
|
+
<h1>Tables</h1>
|
77
|
+
<table>
|
78
|
+
<thead>
|
79
|
+
<tr>
|
80
|
+
<th style="width: 25%;">Name</th>
|
81
|
+
<th style="width: 25%;">Rows</th>
|
82
|
+
<th>Indexes</th>
|
83
|
+
</tr>
|
84
|
+
</thead>
|
85
|
+
<tbody>
|
86
|
+
<% @tables.each do |table| %>
|
87
|
+
<tr>
|
88
|
+
<td><%= table %></td>
|
89
|
+
<td><%= @row_counts[table] %></td>
|
90
|
+
<td>
|
91
|
+
<ul>
|
92
|
+
<% @indexes_by_table[table].to_a.sort_by { |i| [i[:primary] ? 0 : 1, i[:columns]] }.each do |i3| %>
|
93
|
+
<li>
|
94
|
+
<%= i3[:columns].join(", ") %><% if i3[:using] != "btree" %>
|
95
|
+
<%= i3[:using].to_s.upcase %><% end %>
|
96
|
+
<% if i3[:primary] %> PRIMARY<% elsif i3[:unique] %> UNIQUE<% end %>
|
97
|
+
</li>
|
98
|
+
<% end %>
|
99
|
+
</ul>
|
100
|
+
</td>
|
101
|
+
</tr>
|
102
|
+
<% end %>
|
103
|
+
</tbody>
|
104
|
+
</table>
|
105
|
+
<% end %>
|
106
|
+
</div>
|
@@ -0,0 +1,83 @@
|
|
1
|
+
<div class="content">
|
2
|
+
<h1>Space</h1>
|
3
|
+
|
4
|
+
<p>Database Size: <%= @database_size %></p>
|
5
|
+
|
6
|
+
<% if @system_stats_enabled %>
|
7
|
+
<div id="chart-1" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
8
|
+
<script>
|
9
|
+
new Chartkick.LineChart("chart-1", <%= json_escape(free_space_stats_path.to_json).html_safe %>, {colors: ["#5bc0de"], bytes: true, library: {tooltips: {intersect: false, mode: "index"}}})
|
10
|
+
</script>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<!--
|
14
|
+
<% if @index_bloat.any? %>
|
15
|
+
<p>Check out <%= link_to "index bloat", index_bloat_path %> for an easy way to reclaim space.</p>
|
16
|
+
<% end %>
|
17
|
+
-->
|
18
|
+
|
19
|
+
<% if @unused_indexes.any? %>
|
20
|
+
<p>
|
21
|
+
<%= pluralize(@unused_indexes.size, "unused index") %>. Remove them
|
22
|
+
<% if @show_migrations %>
|
23
|
+
<a href="javascript: void(0);" onclick="document.getElementById('migration').style.display = 'block';">with a migration</a>
|
24
|
+
<% end %>
|
25
|
+
for faster writes.
|
26
|
+
|
27
|
+
<% if @database.replicating? %>
|
28
|
+
Check they aren’t used on replicas.
|
29
|
+
<% end %>
|
30
|
+
</p>
|
31
|
+
|
32
|
+
<div id="migration" style="display: none;">
|
33
|
+
<pre>rails generate migration remove_unused_indexes</pre>
|
34
|
+
<p>And paste</p>
|
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
|
+
<%= pghero_remove_index(query) %><% end %></pre>
|
37
|
+
</div>
|
38
|
+
<% end %>
|
39
|
+
|
40
|
+
<table class="table space-table">
|
41
|
+
<thead>
|
42
|
+
<tr>
|
43
|
+
<th><%= link_to "Relation", {sort: "name"} %></th>
|
44
|
+
<th style="width: 15%;"><%= link_to "Size", {} %></th>
|
45
|
+
<% if @space_stats_enabled %>
|
46
|
+
<th style="width: 15%;"><%= link_to "#{@days}d Growth", {sort: "growth"} %></th>
|
47
|
+
<% end %>
|
48
|
+
</tr>
|
49
|
+
</thead>
|
50
|
+
<tbody>
|
51
|
+
<% @relation_sizes.each do |query| %>
|
52
|
+
<tr>
|
53
|
+
<td style="<%= query[:type] == "index" ? "font-style: italic;" : "" %>">
|
54
|
+
<span style="word-break: break-all;">
|
55
|
+
<% name = query[:relation] || query[:table] %>
|
56
|
+
<% if @space_stats_enabled %>
|
57
|
+
<%= link_to name, relation_space_path(name, schema: query[:schema]), target: "_blank", style: "color: inherit;" %>
|
58
|
+
<% else %>
|
59
|
+
<%= name %>
|
60
|
+
<% end %>
|
61
|
+
</span>
|
62
|
+
<% if query[:schema] != "public" %>
|
63
|
+
<span class="text-muted"><%= query[:schema] %></span>
|
64
|
+
<% end %>
|
65
|
+
<% if @unused_index_names.include?(query[:relation]) %>
|
66
|
+
<span class="unused-index">UNUSED</span>
|
67
|
+
<% end %>
|
68
|
+
</td>
|
69
|
+
<td><%= query[:size] %></td>
|
70
|
+
<% if @space_stats_enabled %>
|
71
|
+
<td>
|
72
|
+
<% if @growth_bytes_by_relation[[query[:schema], query[:relation]]] %>
|
73
|
+
<% if @growth_bytes_by_relation[[query[:schema], query[:relation]]] < 0 %>-<% end %><%= PgHero.pretty_size(@growth_bytes_by_relation[[query[:schema], query[:relation]]].abs) %>
|
74
|
+
<% else %>
|
75
|
+
<span class="text-muted">Unknown</span>
|
76
|
+
<% end %>
|
77
|
+
</td>
|
78
|
+
<% end %>
|
79
|
+
</tr>
|
80
|
+
<% end %>
|
81
|
+
</tbody>
|
82
|
+
</table>
|
83
|
+
</div>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<div class="content">
|
2
|
+
<p id="periods">
|
3
|
+
<% @periods.each do |name, options| %>
|
4
|
+
<%= link_to name, system_path(options) %>
|
5
|
+
<% end %>
|
6
|
+
</p>
|
7
|
+
<% path_options = {duration: @duration, period: @period} %>
|
8
|
+
|
9
|
+
<h1>CPU</h1>
|
10
|
+
<div id="chart-1" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
11
|
+
<script>
|
12
|
+
new Chartkick.LineChart("chart-1", <%= json_escape(cpu_usage_path(path_options).to_json).html_safe %>, {max: 100, colors: ["#5bc0de"], suffix: "%", library: {tooltips: {intersect: false, mode: "index"}}})
|
13
|
+
</script>
|
14
|
+
|
15
|
+
<h1>Load</h1>
|
16
|
+
<div id="chart-2" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
17
|
+
<script>
|
18
|
+
new Chartkick.LineChart("chart-2", <%= json_escape(load_stats_path(path_options).to_json).html_safe %>, {colors: ["#5bc0de", "#d9534f"], library: {tooltips: {intersect: false, mode: "nearest"}}})
|
19
|
+
</script>
|
20
|
+
|
21
|
+
<h1>Connections</h1>
|
22
|
+
<div id="chart-3" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
23
|
+
<script>
|
24
|
+
new Chartkick.LineChart("chart-3", <%= json_escape(connection_stats_path(path_options).to_json).html_safe %>, {colors: ["#5bc0de"], library: {tooltips: {intersect: false, mode: "index"}}})
|
25
|
+
</script>
|
26
|
+
|
27
|
+
<% if @database.replica? %>
|
28
|
+
<h1>Replication Lag</h1>
|
29
|
+
<div id="chart-4" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
30
|
+
<script>
|
31
|
+
new Chartkick.LineChart("chart-4", <%= json_escape(replication_lag_stats_path(path_options).to_json).html_safe %>, {colors: ["#5bc0de"], library: {tooltips: {intersect: false, mode: "index"}}})
|
32
|
+
</script>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
@@ -0,0 +1,53 @@
|
|
1
|
+
<div class="content">
|
2
|
+
<h1>Tune</h1>
|
3
|
+
|
4
|
+
<table class="table">
|
5
|
+
<thead>
|
6
|
+
<tr>
|
7
|
+
<th>Setting</th>
|
8
|
+
<th style="width: 20%;">Value</th>
|
9
|
+
</tr>
|
10
|
+
</thead>
|
11
|
+
<tbody>
|
12
|
+
<% @settings.each do |setting, value| %>
|
13
|
+
<tr>
|
14
|
+
<td><%= setting %></td>
|
15
|
+
<td><%= value %></td>
|
16
|
+
</tr>
|
17
|
+
<% end %>
|
18
|
+
</tbody>
|
19
|
+
</table>
|
20
|
+
|
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>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<% if @autovacuum_settings %>
|
26
|
+
<div class="content">
|
27
|
+
<h1>Autovacuum</h1>
|
28
|
+
|
29
|
+
<table class="table">
|
30
|
+
<thead>
|
31
|
+
<tr>
|
32
|
+
<th>Setting</th>
|
33
|
+
<th style="width: 20%;">Value</th>
|
34
|
+
</tr>
|
35
|
+
</thead>
|
36
|
+
<tbody>
|
37
|
+
<% @autovacuum_settings.each do |setting, value| %>
|
38
|
+
<tr>
|
39
|
+
<td><%= setting %></td>
|
40
|
+
<td>
|
41
|
+
<%= value %>
|
42
|
+
<% if setting == :autovacuum_vacuum_cost_limit && value == "-1" %>
|
43
|
+
<span class="text-muted"><%= @database.vacuum_settings[:vacuum_cost_limit] %></span>
|
44
|
+
<% end %>
|
45
|
+
</td>
|
46
|
+
</tr>
|
47
|
+
<% end %>
|
48
|
+
</tbody>
|
49
|
+
</table>
|
50
|
+
|
51
|
+
<p>Check out <%= link_to "Autovacuum Tuning Basics", "https://blog.2ndquadrant.com/autovacuum-tuning-basics/", target: "_blank" %> for recommendations.</p>
|
52
|
+
</div>
|
53
|
+
<% end %>
|