pghero_fork 2.7.3

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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +391 -0
  3. data/CONTRIBUTING.md +42 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +3 -0
  6. data/app/assets/images/pghero/favicon.png +0 -0
  7. data/app/assets/javascripts/pghero/Chart.bundle.js +20755 -0
  8. data/app/assets/javascripts/pghero/application.js +158 -0
  9. data/app/assets/javascripts/pghero/chartkick.js +2436 -0
  10. data/app/assets/javascripts/pghero/highlight.pack.js +2 -0
  11. data/app/assets/javascripts/pghero/jquery.js +10872 -0
  12. data/app/assets/javascripts/pghero/nouislider.js +2672 -0
  13. data/app/assets/stylesheets/pghero/application.css +514 -0
  14. data/app/assets/stylesheets/pghero/arduino-light.css +86 -0
  15. data/app/assets/stylesheets/pghero/nouislider.css +310 -0
  16. data/app/controllers/pg_hero/home_controller.rb +449 -0
  17. data/app/helpers/pg_hero/home_helper.rb +30 -0
  18. data/app/views/layouts/pg_hero/application.html.erb +68 -0
  19. data/app/views/pg_hero/home/_connections_table.html.erb +16 -0
  20. data/app/views/pg_hero/home/_live_queries_table.html.erb +51 -0
  21. data/app/views/pg_hero/home/_queries_table.html.erb +72 -0
  22. data/app/views/pg_hero/home/_query_stats_slider.html.erb +16 -0
  23. data/app/views/pg_hero/home/_suggested_index.html.erb +18 -0
  24. data/app/views/pg_hero/home/connections.html.erb +32 -0
  25. data/app/views/pg_hero/home/explain.html.erb +27 -0
  26. data/app/views/pg_hero/home/index.html.erb +518 -0
  27. data/app/views/pg_hero/home/index_bloat.html.erb +72 -0
  28. data/app/views/pg_hero/home/live_queries.html.erb +11 -0
  29. data/app/views/pg_hero/home/maintenance.html.erb +55 -0
  30. data/app/views/pg_hero/home/queries.html.erb +33 -0
  31. data/app/views/pg_hero/home/relation_space.html.erb +14 -0
  32. data/app/views/pg_hero/home/show_query.html.erb +106 -0
  33. data/app/views/pg_hero/home/space.html.erb +83 -0
  34. data/app/views/pg_hero/home/system.html.erb +34 -0
  35. data/app/views/pg_hero/home/tune.html.erb +53 -0
  36. data/config/routes.rb +32 -0
  37. data/lib/generators/pghero/config_generator.rb +13 -0
  38. data/lib/generators/pghero/query_stats_generator.rb +18 -0
  39. data/lib/generators/pghero/space_stats_generator.rb +18 -0
  40. data/lib/generators/pghero/templates/config.yml.tt +46 -0
  41. data/lib/generators/pghero/templates/query_stats.rb.tt +15 -0
  42. data/lib/generators/pghero/templates/space_stats.rb.tt +13 -0
  43. data/lib/pghero.rb +246 -0
  44. data/lib/pghero/connection.rb +5 -0
  45. data/lib/pghero/database.rb +175 -0
  46. data/lib/pghero/engine.rb +16 -0
  47. data/lib/pghero/methods/basic.rb +160 -0
  48. data/lib/pghero/methods/connections.rb +77 -0
  49. data/lib/pghero/methods/constraints.rb +30 -0
  50. data/lib/pghero/methods/explain.rb +29 -0
  51. data/lib/pghero/methods/indexes.rb +332 -0
  52. data/lib/pghero/methods/kill.rb +28 -0
  53. data/lib/pghero/methods/maintenance.rb +93 -0
  54. data/lib/pghero/methods/queries.rb +75 -0
  55. data/lib/pghero/methods/query_stats.rb +349 -0
  56. data/lib/pghero/methods/replication.rb +74 -0
  57. data/lib/pghero/methods/sequences.rb +124 -0
  58. data/lib/pghero/methods/settings.rb +37 -0
  59. data/lib/pghero/methods/space.rb +141 -0
  60. data/lib/pghero/methods/suggested_indexes.rb +329 -0
  61. data/lib/pghero/methods/system.rb +287 -0
  62. data/lib/pghero/methods/tables.rb +68 -0
  63. data/lib/pghero/methods/users.rb +87 -0
  64. data/lib/pghero/query_stats.rb +5 -0
  65. data/lib/pghero/space_stats.rb +5 -0
  66. data/lib/pghero/stats.rb +6 -0
  67. data/lib/pghero/version.rb +3 -0
  68. data/lib/tasks/pghero.rake +27 -0
  69. data/licenses/LICENSE-chart.js.txt +9 -0
  70. data/licenses/LICENSE-chartkick.js.txt +22 -0
  71. data/licenses/LICENSE-highlight.js.txt +29 -0
  72. data/licenses/LICENSE-jquery.txt +20 -0
  73. data/licenses/LICENSE-moment.txt +22 -0
  74. data/licenses/LICENSE-nouislider.txt +21 -0
  75. 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
+ &lt; 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 %>