pghero 3.1.0 → 3.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/app/assets/javascripts/pghero/Chart.bundle.js +23379 -19766
- data/app/assets/javascripts/pghero/application.js +13 -12
- data/app/assets/javascripts/pghero/chartkick.js +834 -764
- data/app/assets/javascripts/pghero/highlight.min.js +440 -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 +8 -2
- data/app/assets/stylesheets/pghero/nouislider.css +4 -10
- data/app/controllers/pg_hero/home_controller.rb +41 -12
- 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/queries.html.erb +4 -2
- 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 +19 -19
- data/lib/pghero/methods/replication.rb +2 -2
- data/lib/pghero/methods/sequences.rb +2 -2
- data/lib/pghero/methods/space.rb +18 -12
- data/lib/pghero/methods/suggested_indexes.rb +11 -6
- data/lib/pghero/methods/system.rb +10 -4
- data/lib/pghero/methods/tables.rb +4 -5
- data/lib/pghero/version.rb +1 -1
- data/lib/pghero.rb +28 -26
- data/lib/tasks/pghero.rake +11 -1
- data/licenses/LICENSE-chart.js.txt +1 -1
- data/licenses/LICENSE-date-fns.txt +21 -20
- data/licenses/LICENSE-kurkle-color.txt +9 -0
- metadata +5 -4
- data/app/assets/javascripts/pghero/highlight.pack.js +0 -2
|
@@ -187,7 +187,7 @@ hr {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
#slider-container {
|
|
190
|
-
padding: 6px
|
|
190
|
+
padding: 6px 8px 14px 8px;
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
.queries-table th a, .space-table th a {
|
|
@@ -195,7 +195,7 @@ hr {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
#slider {
|
|
198
|
-
margin
|
|
198
|
+
margin: 0px 14px 18px 14px;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
#range-start {
|
|
@@ -485,6 +485,12 @@ body {
|
|
|
485
485
|
color: #999;
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
+
.pie-chart {
|
|
489
|
+
height: 260px;
|
|
490
|
+
line-height: 260px;
|
|
491
|
+
margin-bottom: 20px;
|
|
492
|
+
}
|
|
493
|
+
|
|
488
494
|
.unused-index {
|
|
489
495
|
color: #f0ad4e;
|
|
490
496
|
font-size: 11px;
|
|
@@ -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
|
|
@@ -239,9 +248,16 @@ module PgHero
|
|
|
239
248
|
stats =
|
|
240
249
|
case @database.system_stats_provider
|
|
241
250
|
when :azure
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
251
|
+
if @database.send(:azure_flexible_server?)
|
|
252
|
+
[
|
|
253
|
+
{name: "Read IOPS", data: @database.read_iops_stats(**system_params).map { |k, v| [k, v ? v.round : v] }, library: chart_library_options},
|
|
254
|
+
{name: "Write IOPS", data: @database.write_iops_stats(**system_params).map { |k, v| [k, v ? v.round : v] }, library: chart_library_options}
|
|
255
|
+
]
|
|
256
|
+
else
|
|
257
|
+
[
|
|
258
|
+
{name: "IO Consumption", data: @database.azure_stats("io_consumption_percent", **system_params), library: chart_library_options}
|
|
259
|
+
]
|
|
260
|
+
end
|
|
245
261
|
when :gcp
|
|
246
262
|
[
|
|
247
263
|
{name: "Read Ops", data: @database.read_iops_stats(**system_params).map { |k, v| [k, v ? v.round : v] }, library: chart_library_options},
|
|
@@ -395,6 +411,7 @@ module PgHero
|
|
|
395
411
|
redirect_backward alert: "The database user does not have permission to enable query stats"
|
|
396
412
|
end
|
|
397
413
|
|
|
414
|
+
# TODO disable if historical query stats enabled?
|
|
398
415
|
def reset_query_stats
|
|
399
416
|
success =
|
|
400
417
|
if @database.server_version_num >= 120000
|
|
@@ -440,14 +457,18 @@ module PgHero
|
|
|
440
457
|
end
|
|
441
458
|
|
|
442
459
|
def set_suggested_indexes(min_average_time = 0, min_calls = 0)
|
|
460
|
+
if @database.suggested_indexes_enabled? && !@indexes
|
|
461
|
+
@indexes, @indexes_timeout = rescue_timeout([]) { @database.indexes }
|
|
462
|
+
end
|
|
463
|
+
|
|
443
464
|
@suggested_indexes_by_query =
|
|
444
|
-
if @database.suggested_indexes_enabled?
|
|
445
|
-
@database.suggested_indexes_by_query(query_stats: @query_stats.select { |qs| qs[:average_time] >= min_average_time && qs[:calls] >= min_calls })
|
|
465
|
+
if !@indexes_timeout && @database.suggested_indexes_enabled?
|
|
466
|
+
@database.suggested_indexes_by_query(query_stats: @query_stats.select { |qs| qs[:average_time] >= min_average_time && qs[:calls] >= min_calls }, indexes: @indexes)
|
|
446
467
|
else
|
|
447
468
|
{}
|
|
448
469
|
end
|
|
449
470
|
|
|
450
|
-
@suggested_indexes = @database.suggested_indexes(suggested_indexes_by_query: @suggested_indexes_by_query
|
|
471
|
+
@suggested_indexes = @database.suggested_indexes(suggested_indexes_by_query: @suggested_indexes_by_query)
|
|
451
472
|
@query_stats_by_query = @query_stats.index_by { |q| q[:query] }
|
|
452
473
|
@debug = params[:debug].present?
|
|
453
474
|
end
|
|
@@ -495,5 +516,13 @@ module PgHero
|
|
|
495
516
|
redirect_to root_path, alert: "Query stats not enabled"
|
|
496
517
|
end
|
|
497
518
|
end
|
|
519
|
+
|
|
520
|
+
# rescue QueryCanceled for case when
|
|
521
|
+
# statement timeout is less than lock timeout
|
|
522
|
+
def rescue_timeout(default)
|
|
523
|
+
[yield, false]
|
|
524
|
+
rescue ActiveRecord::LockWaitTimeout, ActiveRecord::QueryCanceled
|
|
525
|
+
[default, true]
|
|
526
|
+
end
|
|
498
527
|
end
|
|
499
528
|
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 %>
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<div class="content">
|
|
2
|
-
<% if @query_stats_enabled %>
|
|
2
|
+
<% if @query_stats_enabled && !@historical_query_stats_enabled %>
|
|
3
3
|
<%= button_to "Reset", reset_query_stats_path, class: "btn btn-danger", style: "float: right;" %>
|
|
4
4
|
<% end %>
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
<% if !@historical_query_stats_enabled %>
|
|
7
|
+
<h1 style="float: left;">Queries</h1>
|
|
8
|
+
<% end %>
|
|
7
9
|
|
|
8
10
|
<% if @historical_query_stats_enabled %>
|
|
9
11
|
<%= render partial: "query_stats_slider" %>
|
|
@@ -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,
|