pghero 3.2.0 → 3.3.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 +5 -0
- data/app/assets/javascripts/pghero/application.js +13 -12
- data/app/assets/javascripts/pghero/highlight.min.js +373 -0
- data/app/assets/javascripts/pghero/jquery.js +318 -197
- data/app/assets/javascripts/pghero/nouislider.js +676 -1066
- data/app/assets/stylesheets/pghero/application.css +6 -0
- data/app/assets/stylesheets/pghero/nouislider.css +4 -10
- data/app/controllers/pg_hero/home_controller.rb +30 -9
- data/app/helpers/pg_hero/home_helper.rb +2 -2
- data/app/views/layouts/pg_hero/application.html.erb +1 -1
- data/app/views/pg_hero/home/_query_stats_slider.html.erb +6 -6
- data/app/views/pg_hero/home/connections.html.erb +6 -6
- data/app/views/pg_hero/home/index.html.erb +3 -1
- data/app/views/pg_hero/home/relation_space.html.erb +1 -1
- data/app/views/pg_hero/home/show_query.html.erb +16 -12
- data/app/views/pg_hero/home/space.html.erb +44 -40
- data/app/views/pg_hero/home/system.html.erb +6 -6
- data/lib/generators/pghero/query_stats_generator.rb +1 -0
- data/lib/generators/pghero/space_stats_generator.rb +1 -0
- data/lib/pghero/engine.rb +1 -1
- data/lib/pghero/methods/basic.rb +5 -8
- data/lib/pghero/methods/connections.rb +4 -4
- data/lib/pghero/methods/constraints.rb +1 -1
- data/lib/pghero/methods/indexes.rb +8 -8
- data/lib/pghero/methods/kill.rb +1 -1
- data/lib/pghero/methods/maintenance.rb +3 -3
- data/lib/pghero/methods/queries.rb +2 -2
- data/lib/pghero/methods/query_stats.rb +16 -17
- data/lib/pghero/methods/replication.rb +2 -2
- data/lib/pghero/methods/sequences.rb +2 -2
- data/lib/pghero/methods/space.rb +15 -10
- data/lib/pghero/methods/suggested_indexes.rb +2 -2
- data/lib/pghero/methods/tables.rb +4 -5
- data/lib/pghero/version.rb +1 -1
- data/lib/pghero.rb +2 -0
- metadata +3 -3
- data/app/assets/javascripts/pghero/highlight.pack.js +0 -2
@@ -1,4 +1,3 @@
|
|
1
|
-
/*! nouislider - 14.6.1 - 8/17/2020 */
|
2
1
|
/* Functional styling;
|
3
2
|
* These styles are required for noUiSlider to function.
|
4
3
|
* You don't need to change these rules to apply your design.
|
@@ -39,20 +38,14 @@
|
|
39
38
|
z-index: 1;
|
40
39
|
top: 0;
|
41
40
|
right: 0;
|
41
|
+
height: 100%;
|
42
|
+
width: 100%;
|
42
43
|
-ms-transform-origin: 0 0;
|
43
44
|
-webkit-transform-origin: 0 0;
|
44
45
|
-webkit-transform-style: preserve-3d;
|
45
46
|
transform-origin: 0 0;
|
46
47
|
transform-style: flat;
|
47
48
|
}
|
48
|
-
.noUi-connect {
|
49
|
-
height: 100%;
|
50
|
-
width: 100%;
|
51
|
-
}
|
52
|
-
.noUi-origin {
|
53
|
-
height: 10%;
|
54
|
-
width: 10%;
|
55
|
-
}
|
56
49
|
/* Offset direction
|
57
50
|
*/
|
58
51
|
.noUi-txt-dir-rtl.noUi-horizontal .noUi-origin {
|
@@ -63,6 +56,7 @@
|
|
63
56
|
* connect elements.
|
64
57
|
*/
|
65
58
|
.noUi-vertical .noUi-origin {
|
59
|
+
top: -100%;
|
66
60
|
width: 0;
|
67
61
|
}
|
68
62
|
.noUi-horizontal .noUi-origin {
|
@@ -103,7 +97,7 @@
|
|
103
97
|
width: 28px;
|
104
98
|
height: 34px;
|
105
99
|
right: -6px;
|
106
|
-
|
100
|
+
bottom: -17px;
|
107
101
|
}
|
108
102
|
.noUi-txt-dir-rtl.noUi-horizontal .noUi-handle {
|
109
103
|
left: -17px;
|
@@ -46,11 +46,18 @@ module PgHero
|
|
46
46
|
|
47
47
|
@transaction_id_danger = @database.transaction_id_danger(threshold: 1500000000)
|
48
48
|
|
49
|
-
|
49
|
+
sequences, @sequences_timeout = rescue_timeout([]) { @database.sequences }
|
50
|
+
@readable_sequences, @unreadable_sequences = sequences.partition { |s| s[:readable] }
|
50
51
|
|
51
52
|
@sequence_danger = @database.sequence_danger(threshold: (params[:sequence_threshold] || 0.9).to_f, sequences: @readable_sequences)
|
52
53
|
|
53
|
-
@indexes =
|
54
|
+
@indexes, @indexes_timeout =
|
55
|
+
if @sequences_timeout
|
56
|
+
# skip indexes for faster loading
|
57
|
+
[[], true]
|
58
|
+
else
|
59
|
+
rescue_timeout([]) { @database.indexes }
|
60
|
+
end
|
54
61
|
@invalid_indexes = @database.invalid_indexes(indexes: @indexes)
|
55
62
|
@invalid_constraints = @database.invalid_constraints
|
56
63
|
@duplicate_indexes = @database.duplicate_indexes(indexes: @indexes)
|
@@ -80,7 +87,7 @@ module PgHero
|
|
80
87
|
@days = (params[:days] || 7).to_i
|
81
88
|
@database_size = @database.database_size
|
82
89
|
@only_tables = params[:tables].present?
|
83
|
-
@relation_sizes = @only_tables ? @database.table_sizes : @database.relation_sizes
|
90
|
+
@relation_sizes, @sizes_timeout = rescue_timeout([]) { @only_tables ? @database.table_sizes : @database.relation_sizes }
|
84
91
|
@space_stats_enabled = @database.space_stats_enabled? && !@only_tables
|
85
92
|
if @space_stats_enabled
|
86
93
|
space_growth = @database.space_growth(days: @days, relation_sizes: @relation_sizes)
|
@@ -157,8 +164,9 @@ module PgHero
|
|
157
164
|
)
|
158
165
|
end
|
159
166
|
|
160
|
-
|
161
|
-
|
167
|
+
if !@historical_query_stats_enabled || request.xhr?
|
168
|
+
set_suggested_indexes
|
169
|
+
end
|
162
170
|
|
163
171
|
# fix back button issue with caching
|
164
172
|
response.headers["Cache-Control"] = "must-revalidate, no-store, no-cache, private"
|
@@ -193,7 +201,8 @@ module PgHero
|
|
193
201
|
|
194
202
|
if @tables.any?
|
195
203
|
@row_counts = @database.table_stats(table: @tables).to_h { |i| [i[:table], i[:estimated_rows]] }
|
196
|
-
@
|
204
|
+
indexes, @indexes_timeout = rescue_timeout([]) { @database.indexes }
|
205
|
+
@indexes_by_table = indexes.group_by { |i| i[:table] }
|
197
206
|
end
|
198
207
|
else
|
199
208
|
render_text "Unknown query", status: :not_found
|
@@ -441,14 +450,18 @@ module PgHero
|
|
441
450
|
end
|
442
451
|
|
443
452
|
def set_suggested_indexes(min_average_time = 0, min_calls = 0)
|
453
|
+
if @database.suggested_indexes_enabled? && !@indexes
|
454
|
+
@indexes, @indexes_timeout = rescue_timeout([]) { @database.indexes }
|
455
|
+
end
|
456
|
+
|
444
457
|
@suggested_indexes_by_query =
|
445
|
-
if @database.suggested_indexes_enabled?
|
446
|
-
@database.suggested_indexes_by_query(query_stats: @query_stats.select { |qs| qs[:average_time] >= min_average_time && qs[:calls] >= min_calls })
|
458
|
+
if !@indexes_timeout && @database.suggested_indexes_enabled?
|
459
|
+
@database.suggested_indexes_by_query(query_stats: @query_stats.select { |qs| qs[:average_time] >= min_average_time && qs[:calls] >= min_calls }, indexes: @indexes)
|
447
460
|
else
|
448
461
|
{}
|
449
462
|
end
|
450
463
|
|
451
|
-
@suggested_indexes = @database.suggested_indexes(suggested_indexes_by_query: @suggested_indexes_by_query
|
464
|
+
@suggested_indexes = @database.suggested_indexes(suggested_indexes_by_query: @suggested_indexes_by_query)
|
452
465
|
@query_stats_by_query = @query_stats.index_by { |q| q[:query] }
|
453
466
|
@debug = params[:debug].present?
|
454
467
|
end
|
@@ -496,5 +509,13 @@ module PgHero
|
|
496
509
|
redirect_to root_path, alert: "Query stats not enabled"
|
497
510
|
end
|
498
511
|
end
|
512
|
+
|
513
|
+
# rescue QueryCanceled for case when
|
514
|
+
# statement timeout is less than lock timeout
|
515
|
+
def rescue_timeout(default)
|
516
|
+
[yield, false]
|
517
|
+
rescue ActiveRecord::LockWaitTimeout, ActiveRecord::QueryCanceled
|
518
|
+
[default, true]
|
519
|
+
end
|
499
520
|
end
|
500
521
|
end
|
@@ -12,8 +12,8 @@ module PgHero
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
15
|
+
def pghero_js_value(value)
|
16
|
+
json_escape(value.to_json(root: false)).html_safe
|
17
17
|
end
|
18
18
|
|
19
19
|
def pghero_remove_index(query)
|
@@ -7,7 +7,7 @@
|
|
7
7
|
<%= favicon_link_tag "pghero/favicon.png" %>
|
8
8
|
<% if defined?(Propshaft::Railtie) %>
|
9
9
|
<%= stylesheet_link_tag "pghero/nouislider", "pghero/arduino-light", "pghero/application" %>
|
10
|
-
<%= javascript_include_tag "pghero/jquery", "pghero/nouislider", "pghero/Chart.bundle", "pghero/chartkick", "pghero/highlight.
|
10
|
+
<%= javascript_include_tag "pghero/jquery", "pghero/nouislider", "pghero/Chart.bundle", "pghero/chartkick", "pghero/highlight.min", "pghero/application" %>
|
11
11
|
<% else %>
|
12
12
|
<%= stylesheet_link_tag "pghero/application" %>
|
13
13
|
<%= javascript_include_tag "pghero/application" %>
|
@@ -5,12 +5,12 @@
|
|
5
5
|
</div>
|
6
6
|
|
7
7
|
<script>
|
8
|
-
<%=
|
9
|
-
<%=
|
10
|
-
<%=
|
11
|
-
<%=
|
12
|
-
<%=
|
13
|
-
<%=
|
8
|
+
var sort = <%= pghero_js_value(@sort) %>;
|
9
|
+
var minAverageTime = <%= pghero_js_value(@min_average_time) %>;
|
10
|
+
var minCalls = <%= pghero_js_value(@min_calls) %>;
|
11
|
+
var debug = <%= pghero_js_value(@debug) %>;
|
12
|
+
var startAt = <%= pghero_js_value(params[:start_at] ? @start_at.to_i * 1000 : nil) %>;
|
13
|
+
var endAt = <%= pghero_js_value(@end_at.to_i * 1000) %>;
|
14
14
|
|
15
15
|
initSlider();
|
16
16
|
</script>
|
@@ -6,24 +6,24 @@
|
|
6
6
|
<% if @total_connections > 0 %>
|
7
7
|
<h3>By Database</h3>
|
8
8
|
|
9
|
-
<div id="chart-1" class="chart
|
9
|
+
<div id="chart-1" class="chart pie-chart">Loading...</div>
|
10
10
|
<script>
|
11
|
-
new Chartkick.PieChart("chart-1", <%=
|
11
|
+
new Chartkick.PieChart("chart-1", <%= pghero_js_value(@connections_by_database) %>);
|
12
12
|
</script>
|
13
13
|
|
14
14
|
<h3>By User</h3>
|
15
15
|
|
16
|
-
<div id="chart-2" class="chart
|
16
|
+
<div id="chart-2" class="chart pie-chart">Loading...</div>
|
17
17
|
<script>
|
18
|
-
new Chartkick.PieChart("chart-2", <%=
|
18
|
+
new Chartkick.PieChart("chart-2", <%= pghero_js_value(@connections_by_user) %>);
|
19
19
|
</script>
|
20
20
|
|
21
21
|
<% if @connections_by_ssl_status %>
|
22
22
|
<h3>By Security</h3>
|
23
23
|
|
24
|
-
<div id="chart-3" class="chart
|
24
|
+
<div id="chart-3" class="chart pie-chart">Loading...</div>
|
25
25
|
<script>
|
26
|
-
new Chartkick.PieChart("chart-3", <%=
|
26
|
+
new Chartkick.PieChart("chart-3", <%= pghero_js_value(@connections_by_ssl_status) %>);
|
27
27
|
</script>
|
28
28
|
<% end %>
|
29
29
|
|
@@ -55,9 +55,11 @@
|
|
55
55
|
Vacuuming healthy
|
56
56
|
<% end %>
|
57
57
|
</div>
|
58
|
-
<div class="alert alert-<%= @sequence_danger && @sequence_danger.empty? ? "success" : "warning" %>">
|
58
|
+
<div class="alert alert-<%= @sequence_danger && @sequence_danger.empty? && !@sequences_timeout ? "success" : "warning" %>">
|
59
59
|
<% if @sequence_danger.any? %>
|
60
60
|
<%= pluralize(@sequence_danger.size, "column") %> approaching overflow
|
61
|
+
<% elsif @sequences_timeout %>
|
62
|
+
Sequences not available (system catalog locked)
|
61
63
|
<% else %>
|
62
64
|
No columns near integer overflow
|
63
65
|
<% end %>
|
@@ -9,6 +9,6 @@
|
|
9
9
|
<h1>Size</h1>
|
10
10
|
<div id="chart-1" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
11
11
|
<script>
|
12
|
-
new Chartkick.LineChart("chart-1", <%=
|
12
|
+
new Chartkick.LineChart("chart-1", <%= pghero_js_value(@chart_data) %>, {colors: ["#5bc0de"], legend: false, min: null, bytes: true, library: {plugins: {tooltip: {intersect: false, mode: "index"}}}})
|
13
13
|
</script>
|
14
14
|
</div>
|
@@ -49,19 +49,19 @@
|
|
49
49
|
<h1>Total Time <small>ms</small></h1>
|
50
50
|
<div id="chart-1" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
51
51
|
<script>
|
52
|
-
new Chartkick.LineChart("chart-1", <%=
|
52
|
+
new Chartkick.LineChart("chart-1", <%= pghero_js_value(@chart_data) %>, {colors: ["#5bc0de"], legend: false, library: {plugins: {tooltip: {intersect: false, mode: "index"}}}})
|
53
53
|
</script>
|
54
54
|
|
55
55
|
<h1>Average Time <small>ms</small></h1>
|
56
56
|
<div id="chart-2" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
57
57
|
<script>
|
58
|
-
new Chartkick.LineChart("chart-2", <%=
|
58
|
+
new Chartkick.LineChart("chart-2", <%= pghero_js_value(@chart2_data) %>, {colors: ["#5bc0de"], legend: false, library: {plugins: {tooltip: {intersect: false, mode: "index"}}}})
|
59
59
|
</script>
|
60
60
|
|
61
61
|
<h1>Calls</h1>
|
62
62
|
<div id="chart-3" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
63
63
|
<script>
|
64
|
-
new Chartkick.LineChart("chart-3", <%=
|
64
|
+
new Chartkick.LineChart("chart-3", <%= pghero_js_value(@chart3_data) %>, {colors: ["#5bc0de"], legend: false, library: {plugins: {tooltip: {intersect: false, mode: "index"}}}})
|
65
65
|
</script>
|
66
66
|
<% else %>
|
67
67
|
<p>
|
@@ -88,15 +88,19 @@
|
|
88
88
|
<td><%= table %></td>
|
89
89
|
<td><%= @row_counts[table] %></td>
|
90
90
|
<td>
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
91
|
+
<% if @indexes_timeout %>
|
92
|
+
Not available
|
93
|
+
<% else %>
|
94
|
+
<ul>
|
95
|
+
<% @indexes_by_table[table].to_a.sort_by { |i| [i[:primary] ? 0 : 1, i[:columns]] }.each do |i3| %>
|
96
|
+
<li>
|
97
|
+
<%= i3[:columns].join(", ") %><% if i3[:using] != "btree" %>
|
98
|
+
<%= i3[:using].to_s.upcase %><% end %>
|
99
|
+
<% if i3[:primary] %> PRIMARY<% elsif i3[:unique] %> UNIQUE<% end %>
|
100
|
+
</li>
|
101
|
+
<% end %>
|
102
|
+
</ul>
|
103
|
+
<% end %>
|
100
104
|
</td>
|
101
105
|
</tr>
|
102
106
|
<% end %>
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<% if @system_stats_enabled %>
|
7
7
|
<div id="chart-1" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
8
8
|
<script>
|
9
|
-
new Chartkick.LineChart("chart-1", <%=
|
9
|
+
new Chartkick.LineChart("chart-1", <%= pghero_js_value(free_space_stats_path) %>, {colors: ["#5bc0de"], bytes: true, library: {plugins: {tooltip: {intersect: false, mode: "index"}}}})
|
10
10
|
</script>
|
11
11
|
<% end %>
|
12
12
|
|
@@ -37,47 +37,51 @@
|
|
37
37
|
</div>
|
38
38
|
<% end %>
|
39
39
|
|
40
|
-
|
41
|
-
<
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
<% if @space_stats_enabled %>
|
46
|
-
<th style="width: 15%;"><%= link_to "#{@days}d Growth", @header_options.merge(sort: "growth") %></th>
|
47
|
-
<% end %>
|
48
|
-
</tr>
|
49
|
-
</thead>
|
50
|
-
<tbody>
|
51
|
-
<% @relation_sizes.each do |query| %>
|
40
|
+
<% if @sizes_timeout %>
|
41
|
+
<p>Breakdown not available (system catalog locked)</p>
|
42
|
+
<% else %>
|
43
|
+
<table class="table space-table">
|
44
|
+
<thead>
|
52
45
|
<tr>
|
53
|
-
<
|
54
|
-
|
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>
|
46
|
+
<th><%= link_to (@only_tables ? "Table" : "Relation"), @header_options.merge(sort: "name") %></th>
|
47
|
+
<th style="width: 15%;"><%= link_to "Size", @header_options %></th>
|
70
48
|
<% if @space_stats_enabled %>
|
71
|
-
<
|
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>
|
49
|
+
<th style="width: 15%;"><%= link_to "#{@days}d Growth", @header_options.merge(sort: "growth") %></th>
|
78
50
|
<% end %>
|
79
51
|
</tr>
|
80
|
-
|
81
|
-
|
82
|
-
|
52
|
+
</thead>
|
53
|
+
<tbody>
|
54
|
+
<% @relation_sizes.each do |query| %>
|
55
|
+
<tr>
|
56
|
+
<td style="<%= query[:type] == "index" ? "font-style: italic;" : "" %>">
|
57
|
+
<span style="word-break: break-all;">
|
58
|
+
<% name = query[:relation] || query[:table] %>
|
59
|
+
<% if @space_stats_enabled %>
|
60
|
+
<%= link_to name, relation_space_path(name, schema: query[:schema]), target: "_blank", style: "color: inherit;" %>
|
61
|
+
<% else %>
|
62
|
+
<%= name %>
|
63
|
+
<% end %>
|
64
|
+
</span>
|
65
|
+
<% if query[:schema] != "public" %>
|
66
|
+
<span class="text-muted"><%= query[:schema] %></span>
|
67
|
+
<% end %>
|
68
|
+
<% if @unused_index_names.include?(query[:relation]) %>
|
69
|
+
<span class="unused-index">UNUSED</span>
|
70
|
+
<% end %>
|
71
|
+
</td>
|
72
|
+
<td><%= query[:size] %></td>
|
73
|
+
<% if @space_stats_enabled %>
|
74
|
+
<td>
|
75
|
+
<% if @growth_bytes_by_relation[[query[:schema], query[:relation]]] %>
|
76
|
+
<% if @growth_bytes_by_relation[[query[:schema], query[:relation]]] < 0 %>-<% end %><%= PgHero.pretty_size(@growth_bytes_by_relation[[query[:schema], query[:relation]]].abs) %>
|
77
|
+
<% else %>
|
78
|
+
<span class="text-muted">Unknown</span>
|
79
|
+
<% end %>
|
80
|
+
</td>
|
81
|
+
<% end %>
|
82
|
+
</tr>
|
83
|
+
<% end %>
|
84
|
+
</tbody>
|
85
|
+
</table>
|
86
|
+
<% end %>
|
83
87
|
</div>
|
@@ -1,34 +1,34 @@
|
|
1
1
|
<div class="content">
|
2
2
|
<p id="periods">
|
3
3
|
<% @periods.each do |name, options| %>
|
4
|
-
<%= link_to name, system_path(options) %>
|
4
|
+
<%= link_to name, system_path(params: options) %>
|
5
5
|
<% end %>
|
6
6
|
</p>
|
7
|
-
<% path_options = {duration: @duration, period: @period} %>
|
7
|
+
<% path_options = {params: {duration: @duration, period: @period}} %>
|
8
8
|
|
9
9
|
<h1>CPU</h1>
|
10
10
|
<div id="chart-1" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
11
11
|
<script>
|
12
|
-
new Chartkick.LineChart("chart-1", <%=
|
12
|
+
new Chartkick.LineChart("chart-1", <%= pghero_js_value(cpu_usage_path(path_options)) %>, {max: 100, colors: ["#5bc0de"], suffix: "%", library: {plugins: {tooltip: {intersect: false, mode: "index"}}}})
|
13
13
|
</script>
|
14
14
|
|
15
15
|
<h1>Load</h1>
|
16
16
|
<div id="chart-2" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
17
17
|
<script>
|
18
|
-
new Chartkick.LineChart("chart-2", <%=
|
18
|
+
new Chartkick.LineChart("chart-2", <%= pghero_js_value(load_stats_path(path_options)) %>, {colors: ["#5bc0de", "#d9534f"], library: {plugins: {tooltip: {intersect: false, mode: "nearest"}}}})
|
19
19
|
</script>
|
20
20
|
|
21
21
|
<h1>Connections</h1>
|
22
22
|
<div id="chart-3" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
23
23
|
<script>
|
24
|
-
new Chartkick.LineChart("chart-3", <%=
|
24
|
+
new Chartkick.LineChart("chart-3", <%= pghero_js_value(connection_stats_path(path_options)) %>, {colors: ["#5bc0de"], library: {plugins: {tooltip: {intersect: false, mode: "index"}}}})
|
25
25
|
</script>
|
26
26
|
|
27
27
|
<% if @database.replica? %>
|
28
28
|
<h1>Replication Lag</h1>
|
29
29
|
<div id="chart-4" class="chart" style="margin-bottom: 20px;">Loading...</div>
|
30
30
|
<script>
|
31
|
-
new Chartkick.LineChart("chart-4", <%=
|
31
|
+
new Chartkick.LineChart("chart-4", <%= pghero_js_value(replication_lag_stats_path(path_options)) %>, {colors: ["#5bc0de"], library: {plugins: {tooltip: {intersect: false, mode: "index"}}}})
|
32
32
|
</script>
|
33
33
|
<% end %>
|
34
34
|
</div>
|
data/lib/pghero/engine.rb
CHANGED
@@ -5,7 +5,7 @@ module PgHero
|
|
5
5
|
initializer "pghero", group: :all do |app|
|
6
6
|
# check if Rails api mode
|
7
7
|
if app.config.respond_to?(:assets)
|
8
|
-
if defined?(Sprockets) && Sprockets::VERSION >=
|
8
|
+
if defined?(Sprockets) && Sprockets::VERSION.to_i >= 4
|
9
9
|
app.config.assets.precompile << "pghero/application.js"
|
10
10
|
app.config.assets.precompile << "pghero/application.css"
|
11
11
|
app.config.assets.precompile << "pghero/favicon.png"
|
data/lib/pghero/methods/basic.rb
CHANGED
@@ -112,15 +112,8 @@ module PgHero
|
|
112
112
|
::PgHero::Stats.connection
|
113
113
|
end
|
114
114
|
|
115
|
-
def insert_stats(table, columns, values)
|
116
|
-
values = values.map { |v| "(#{v.map { |v2| quote(v2) }.join(",")})" }.join(",")
|
117
|
-
columns = columns.map { |v| quote_table_name(v) }.join(",")
|
118
|
-
stats_connection.execute("INSERT INTO #{quote_table_name(table)} (#{columns}) VALUES #{values}")
|
119
|
-
end
|
120
|
-
|
121
|
-
# from ActiveSupport
|
122
115
|
def squish(str)
|
123
|
-
str.to_s.
|
116
|
+
str.to_s.squish
|
124
117
|
end
|
125
118
|
|
126
119
|
def add_source(sql)
|
@@ -135,6 +128,10 @@ module PgHero
|
|
135
128
|
connection.quote_table_name(value)
|
136
129
|
end
|
137
130
|
|
131
|
+
def quote_column_name(value)
|
132
|
+
connection.quote_column_name(value)
|
133
|
+
end
|
134
|
+
|
138
135
|
def unquote(part)
|
139
136
|
if part && part.start_with?('"')
|
140
137
|
part[1..-2]
|
@@ -3,7 +3,7 @@ module PgHero
|
|
3
3
|
module Connections
|
4
4
|
def connections
|
5
5
|
if server_version_num >= 90500
|
6
|
-
select_all
|
6
|
+
select_all <<~SQL
|
7
7
|
SELECT
|
8
8
|
pg_stat_activity.pid,
|
9
9
|
datname AS database,
|
@@ -20,7 +20,7 @@ module PgHero
|
|
20
20
|
pg_stat_activity.pid
|
21
21
|
SQL
|
22
22
|
else
|
23
|
-
select_all
|
23
|
+
select_all <<~SQL
|
24
24
|
SELECT
|
25
25
|
pid,
|
26
26
|
datname AS database,
|
@@ -41,7 +41,7 @@ module PgHero
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def connection_states
|
44
|
-
states = select_all
|
44
|
+
states = select_all <<~SQL
|
45
45
|
SELECT
|
46
46
|
state,
|
47
47
|
COUNT(*) AS connections
|
@@ -57,7 +57,7 @@ module PgHero
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def connection_sources
|
60
|
-
select_all
|
60
|
+
select_all <<~SQL
|
61
61
|
SELECT
|
62
62
|
datname AS database,
|
63
63
|
usename AS user,
|
@@ -2,7 +2,7 @@ module PgHero
|
|
2
2
|
module Methods
|
3
3
|
module Indexes
|
4
4
|
def index_hit_rate
|
5
|
-
select_one
|
5
|
+
select_one <<~SQL
|
6
6
|
SELECT
|
7
7
|
(sum(idx_blks_hit)) / nullif(sum(idx_blks_hit + idx_blks_read), 0) AS rate
|
8
8
|
FROM
|
@@ -11,7 +11,7 @@ module PgHero
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def index_caching
|
14
|
-
select_all
|
14
|
+
select_all <<~SQL
|
15
15
|
SELECT
|
16
16
|
schemaname AS schema,
|
17
17
|
relname AS table,
|
@@ -29,7 +29,7 @@ module PgHero
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def index_usage
|
32
|
-
select_all
|
32
|
+
select_all <<~SQL
|
33
33
|
SELECT
|
34
34
|
schemaname AS schema,
|
35
35
|
relname AS table,
|
@@ -47,7 +47,7 @@ module PgHero
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def missing_indexes
|
50
|
-
select_all
|
50
|
+
select_all <<~SQL
|
51
51
|
SELECT
|
52
52
|
schemaname AS schema,
|
53
53
|
relname AS table,
|
@@ -69,7 +69,7 @@ module PgHero
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def unused_indexes(max_scans: 50, across: [])
|
72
|
-
result = select_all_size
|
72
|
+
result = select_all_size <<~SQL
|
73
73
|
SELECT
|
74
74
|
schemaname AS schema,
|
75
75
|
relname AS table,
|
@@ -104,7 +104,7 @@ module PgHero
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def last_stats_reset_time
|
107
|
-
select_one
|
107
|
+
select_one <<~SQL
|
108
108
|
SELECT
|
109
109
|
pg_stat_get_db_stat_reset_time(oid) AS reset_time
|
110
110
|
FROM
|
@@ -126,7 +126,7 @@ module PgHero
|
|
126
126
|
# TODO parse array properly
|
127
127
|
# https://stackoverflow.com/questions/2204058/list-columns-with-indexes-in-postgresql
|
128
128
|
def indexes
|
129
|
-
indexes = select_all(
|
129
|
+
indexes = select_all(<<~SQL
|
130
130
|
SELECT
|
131
131
|
schemaname AS schema,
|
132
132
|
t.relname AS table,
|
@@ -186,7 +186,7 @@ module PgHero
|
|
186
186
|
# thanks @jberkus and @mbanck
|
187
187
|
def index_bloat(min_size: nil)
|
188
188
|
min_size ||= index_bloat_bytes
|
189
|
-
select_all
|
189
|
+
select_all <<~SQL
|
190
190
|
WITH btree_index_atts AS (
|
191
191
|
SELECT
|
192
192
|
nspname, relname, reltuples, relpages, indrelid, relam,
|
data/lib/pghero/methods/kill.rb
CHANGED
@@ -9,7 +9,7 @@ module PgHero
|
|
9
9
|
max_value = max_value.to_i
|
10
10
|
threshold = threshold.to_i
|
11
11
|
|
12
|
-
select_all
|
12
|
+
select_all <<~SQL
|
13
13
|
SELECT
|
14
14
|
n.nspname AS schema,
|
15
15
|
c.relname AS table,
|
@@ -35,7 +35,7 @@ module PgHero
|
|
35
35
|
|
36
36
|
def vacuum_progress
|
37
37
|
if server_version_num >= 90600
|
38
|
-
select_all
|
38
|
+
select_all <<~SQL
|
39
39
|
SELECT
|
40
40
|
pid,
|
41
41
|
phase
|
@@ -50,7 +50,7 @@ module PgHero
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def maintenance_info
|
53
|
-
select_all
|
53
|
+
select_all <<~SQL
|
54
54
|
SELECT
|
55
55
|
schemaname AS schema,
|
56
56
|
relname AS table,
|