pghero 1.1.0 → 1.1.1
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 +6 -0
- data/app/controllers/pg_hero/home_controller.rb +3 -2
- data/app/views/layouts/pg_hero/application.html.erb +4 -0
- data/app/views/pg_hero/home/_connections_table.html.erb +2 -2
- data/app/views/pg_hero/home/_queries_table.html.erb +4 -4
- data/app/views/pg_hero/home/_query_stats_slider.html.erb +28 -8
- data/app/views/pg_hero/home/index.html.erb +2 -4
- data/app/views/pg_hero/home/index_usage.html.erb +4 -4
- data/app/views/pg_hero/home/queries.html.erb +1 -1
- data/guides/Heroku.md +29 -0
- data/guides/Linux.md +47 -2
- data/guides/Rails.md +10 -1
- data/lib/pghero.rb +30 -9
- data/lib/pghero/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aca2a34abdf1aa45e33214683e898ef7b38cf4b5
|
4
|
+
data.tar.gz: d538fd497d9e85adef16551f3e4f54b2c0f81ae0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3af44d86c65e316555e8a6ac6a8692214025649d2c4b05e64244b55e1bd6b7b538d5b13efc857238330d51adc8bd9cd6e222678679bbc5dd31740017a0d95191
|
7
|
+
data.tar.gz: 6ccddb176e5131effd878d5efb7074812918e3fe5ed80733e84a9621edf77127a1c3222ec6d3f011e974020bd6752539b853ce809c2aa50ca2982fd01b02703b
|
data/CHANGELOG.md
CHANGED
@@ -16,7 +16,7 @@ module PgHero
|
|
16
16
|
@index_hit_rate = PgHero.index_hit_rate
|
17
17
|
@table_hit_rate = PgHero.table_hit_rate
|
18
18
|
@missing_indexes = PgHero.missing_indexes
|
19
|
-
@unused_indexes = PgHero.unused_indexes
|
19
|
+
@unused_indexes = PgHero.unused_indexes.select { |q| q["index_scans"].to_i == 0 }
|
20
20
|
@good_cache_rate = @table_hit_rate >= 0.99 && @index_hit_rate >= 0.99
|
21
21
|
@query_stats_available = PgHero.query_stats_available?
|
22
22
|
@total_connections = PgHero.total_connections
|
@@ -47,6 +47,7 @@ module PgHero
|
|
47
47
|
def queries
|
48
48
|
@title = "Queries"
|
49
49
|
@historical_query_stats_enabled = PgHero.historical_query_stats_enabled?
|
50
|
+
@sort = %w[average_time calls].include?(params[:sort]) ? params[:sort] : nil
|
50
51
|
|
51
52
|
@query_stats =
|
52
53
|
begin
|
@@ -58,7 +59,7 @@ module PgHero
|
|
58
59
|
if @historical_query_stats_enabled && !request.xhr?
|
59
60
|
[]
|
60
61
|
else
|
61
|
-
PgHero.query_stats(historical: true, start_at: @start_at, end_at: @end_at)
|
62
|
+
PgHero.query_stats(historical: true, start_at: @start_at, end_at: @end_at, sort: @sort)
|
62
63
|
end
|
63
64
|
rescue
|
64
65
|
@error = true
|
@@ -9,14 +9,14 @@
|
|
9
9
|
<thead>
|
10
10
|
<tr>
|
11
11
|
<th>Top Sources</th>
|
12
|
-
<th>Connections</th>
|
12
|
+
<th style="width: 20%;">Connections</th>
|
13
13
|
</tr>
|
14
14
|
</thead>
|
15
15
|
<tbody>
|
16
16
|
<% PgHero.connection_sources.first(10).each do |source| %>
|
17
17
|
<tr>
|
18
18
|
<td><%= source["source"] %> <span class="text-muted"><%= source["ip"] %></span></td>
|
19
|
-
<td
|
19
|
+
<td><%= number_with_delimiter(source["total_connections"]) %></td>
|
20
20
|
</tr>
|
21
21
|
<% end %>
|
22
22
|
</tbody>
|
@@ -1,10 +1,10 @@
|
|
1
|
-
<table class="table">
|
1
|
+
<table class="table queries-table">
|
2
2
|
<% unless local_assigns[:xhr] %>
|
3
3
|
<thead>
|
4
4
|
<tr>
|
5
|
-
<th style="width: 33.33%;"
|
6
|
-
<th style="width: 33.33%;"
|
7
|
-
<th style="width: 33.33%;"
|
5
|
+
<th style="width: 33.33%;"><%= local_assigns[:sort_headers] ? link_to("Total Time", {sort: nil}, data: {sort: nil}) : "Total Time" %></th>
|
6
|
+
<th style="width: 33.33%;"><%= local_assigns[:sort_headers] ? link_to("Average Time", {sort: "average_time"}, data: {sort: "average_time"}) : "Average Time" %></th>
|
7
|
+
<th style="width: 33.33%;"><%= local_assigns[:sort_headers] ? link_to("Calls", {sort: "calls"}, data: {sort: "calls"}) : "Calls" %></th>
|
8
8
|
</tr>
|
9
9
|
</thead>
|
10
10
|
<% end %>
|
@@ -42,6 +42,8 @@
|
|
42
42
|
return (num < 10) ? "0" + num : num;
|
43
43
|
}
|
44
44
|
|
45
|
+
var sort = <%= @sort.to_json.html_safe %>;
|
46
|
+
|
45
47
|
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
46
48
|
|
47
49
|
var days = 1;
|
@@ -49,7 +51,7 @@
|
|
49
51
|
var sliderStartAt = roundTime(now) - days * 24 * 60 * 60 * 1000;
|
50
52
|
var sliderMax = 24 * 12 * days;
|
51
53
|
|
52
|
-
var startAt = <%= @start_at.to_i
|
54
|
+
var startAt = <%= params[:start_at] ? @start_at.to_i * 1000 : "sliderStartAt" %>;
|
53
55
|
var min = (startAt > 0) ? (startAt - sliderStartAt) / (1000 * 60 * 5) : 0;
|
54
56
|
|
55
57
|
var endAt = <%= @end_at.to_i %> * 1000;
|
@@ -96,24 +98,40 @@
|
|
96
98
|
return time.toISOString();
|
97
99
|
}
|
98
100
|
|
101
|
+
function queriesPath(params) {
|
102
|
+
var path = "queries";
|
103
|
+
if (params.start_at || params.end_at || params.sort) {
|
104
|
+
path += "?" + $.param(params);
|
105
|
+
}
|
106
|
+
return path;
|
107
|
+
}
|
108
|
+
|
99
109
|
function refreshStats(push) {
|
100
110
|
var values = $slider.val();
|
101
|
-
var startAt = timeAt(values[0]);
|
111
|
+
var startAt = push ? timeAt(values[0]) : new Date(window.startAt);
|
102
112
|
var endAt = timeAt(values[1]);
|
103
113
|
|
104
114
|
var params = {}
|
105
|
-
if (startAt
|
115
|
+
if (startAt.getTime() != sliderStartAt) {
|
106
116
|
params.start_at = timeParam(startAt);
|
107
117
|
}
|
108
118
|
if (endAt < now) {
|
109
119
|
params.end_at = timeParam(endAt);
|
110
120
|
}
|
111
|
-
|
112
|
-
|
113
|
-
if (params.start_at || params.end_at) {
|
114
|
-
path += "?" + $.param(params);
|
121
|
+
if (sort) {
|
122
|
+
params.sort = sort;
|
115
123
|
}
|
116
124
|
|
125
|
+
var path = queriesPath(params);
|
126
|
+
|
127
|
+
$(".queries-table th a").each( function () {
|
128
|
+
var p = $.extend({}, params, {sort: $(this).data("sort")});
|
129
|
+
if (!p.sort) {
|
130
|
+
delete p.sort;
|
131
|
+
}
|
132
|
+
$(this).attr("href", queriesPath(p));
|
133
|
+
});
|
134
|
+
|
117
135
|
$("#queries").html('<tr><td colspan="3"><p class="queries-info text-muted">...</p></td></tr>').load(path);
|
118
136
|
|
119
137
|
if (push && history.pushState) {
|
@@ -125,5 +143,7 @@
|
|
125
143
|
refreshStats(true);
|
126
144
|
});
|
127
145
|
updateText();
|
128
|
-
$(
|
146
|
+
$( function () {
|
147
|
+
refreshStats(false);
|
148
|
+
});
|
129
149
|
</script>
|
@@ -174,16 +174,14 @@ remove_index <%= query["table"].to_sym.inspect %>, name: <%= query["index"].to_s
|
|
174
174
|
<thead>
|
175
175
|
<tr>
|
176
176
|
<th>Name</th>
|
177
|
-
<th>Index Size</th>
|
178
|
-
<th>Index Scans</th>
|
177
|
+
<th style="width: 20%;">Index Size</th>
|
179
178
|
</tr>
|
180
179
|
</thead>
|
181
180
|
<tbody>
|
182
181
|
<% @unused_indexes.each do |query| %>
|
183
182
|
<tr>
|
184
183
|
<td><%= query["index"] %><div class="text-muted">on <%= query["table"] %></div></td>
|
185
|
-
<td
|
186
|
-
<td style="width: 15%;"><%= query["index_scans"] %></td>
|
184
|
+
<td><%= query["index_size"] %></td>
|
187
185
|
</tr>
|
188
186
|
<% end %>
|
189
187
|
</tbody>
|
@@ -5,16 +5,16 @@
|
|
5
5
|
<thead>
|
6
6
|
<tr>
|
7
7
|
<th>Table</th>
|
8
|
-
<th>% of Time Index Used</th>
|
9
|
-
<th>Rows</th>
|
8
|
+
<th style="width: 30%;">% of Time Index Used</th>
|
9
|
+
<th style="width: 20%;">Rows</th>
|
10
10
|
</tr>
|
11
11
|
</thead>
|
12
12
|
<tbody>
|
13
13
|
<% @index_usage.each do |query| %>
|
14
14
|
<tr>
|
15
15
|
<td><%= query["table"] %></td>
|
16
|
-
<td
|
17
|
-
<td
|
16
|
+
<td><%= query["percent_of_times_index_used"] %></td>
|
17
|
+
<td><%= number_with_delimiter(query["rows_in_table"]) %></td>
|
18
18
|
</tr>
|
19
19
|
<% end %>
|
20
20
|
</tbody>
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<% if @error %>
|
14
14
|
<div class="alert alert-danger">Cannot understand start or end time.</div>
|
15
15
|
<% elsif @query_stats.any? || @historical_query_stats_enabled %>
|
16
|
-
<%= render partial: "queries_table", locals: {queries: @query_stats} %>
|
16
|
+
<%= render partial: "queries_table", locals: {queries: @query_stats, sort_headers: true} %>
|
17
17
|
<% else %>
|
18
18
|
<p>Stats are not available yet. Come back soon!</p>
|
19
19
|
<% end %>
|
data/guides/Heroku.md
CHANGED
@@ -21,6 +21,35 @@ For databases outside of Heroku, query stats can be enabled from the dashboard.
|
|
21
21
|
|
22
22
|
If you run into issues, [view the guide](Query-Stats.md).
|
23
23
|
|
24
|
+
## Historical Query Stats
|
25
|
+
|
26
|
+
To track query stats over time, create a table to store them.
|
27
|
+
|
28
|
+
```sql
|
29
|
+
CREATE TABLE "pghero_query_stats" ("id" serial primary key, "database" text, "query" text, "total_time" float, "calls" bigint, "captured_at" timestamp)
|
30
|
+
CREATE INDEX "index_pghero_query_stats_on_database_and_captured_at" ON "pghero_query_stats" ("database", "captured_at")
|
31
|
+
```
|
32
|
+
|
33
|
+
This table can be in the current database or another database. If another database, run:
|
34
|
+
|
35
|
+
```sh
|
36
|
+
heroku config:set PGHERO_STATS_DATABASE_URL=...
|
37
|
+
```
|
38
|
+
|
39
|
+
Schedule the task below to run every 5 minutes.
|
40
|
+
|
41
|
+
```sh
|
42
|
+
rake pghero:capture_query_stats
|
43
|
+
```
|
44
|
+
|
45
|
+
Or with a scheduler like Clockwork, use:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
PgHero.capture_query_stats
|
49
|
+
```
|
50
|
+
|
51
|
+
After this, a time range slider will appear on the Queries tab.
|
52
|
+
|
24
53
|
## System Stats
|
25
54
|
|
26
55
|
CPU usage is available for Amazon RDS. Add these variables to your environment:
|
data/guides/Linux.md
CHANGED
@@ -5,7 +5,9 @@ Packaged for:
|
|
5
5
|
- Ubuntu 14.04 (Trusty)
|
6
6
|
- Ubuntu 12.04 (Precise)
|
7
7
|
- Debian 7 (Wheezy)
|
8
|
-
-
|
8
|
+
- Debian 8 (Jesse)
|
9
|
+
- CentOS / RHEL 6
|
10
|
+
- CentOS / RHEL 7
|
9
11
|
- Fedora 20
|
10
12
|
- SUSE Linux Enterprise Server
|
11
13
|
|
@@ -40,7 +42,16 @@ sudo apt-get update
|
|
40
42
|
sudo apt-get install pghero
|
41
43
|
```
|
42
44
|
|
43
|
-
|
45
|
+
Debian 8 (Jesse)
|
46
|
+
|
47
|
+
```sh
|
48
|
+
wget -qO - https://deb.packager.io/key | sudo apt-key add -
|
49
|
+
echo "deb https://deb.packager.io/gh/pghero/pghero jessie master" | sudo tee /etc/apt/sources.list.d/pghero.list
|
50
|
+
sudo apt-get update
|
51
|
+
sudo apt-get install pghero
|
52
|
+
```
|
53
|
+
|
54
|
+
CentOS / RHEL 6
|
44
55
|
|
45
56
|
```sh
|
46
57
|
sudo rpm --import https://rpm.packager.io/key
|
@@ -51,6 +62,17 @@ enabled=1" | sudo tee /etc/yum.repos.d/pghero.repo
|
|
51
62
|
sudo yum install pghero
|
52
63
|
```
|
53
64
|
|
65
|
+
CentOS / RHEL 7
|
66
|
+
|
67
|
+
```sh
|
68
|
+
sudo rpm --import https://rpm.packager.io/key
|
69
|
+
echo "[pghero]
|
70
|
+
name=Repository for pghero/pghero application.
|
71
|
+
baseurl=https://rpm.packager.io/gh/pghero/pghero/centos7/master
|
72
|
+
enabled=1" | sudo tee /etc/yum.repos.d/pghero.repo
|
73
|
+
sudo yum install pghero
|
74
|
+
```
|
75
|
+
|
54
76
|
Fedora 20
|
55
77
|
|
56
78
|
```sh
|
@@ -126,6 +148,29 @@ sudo service pghero restart
|
|
126
148
|
|
127
149
|
Query stats can be enabled from the dashboard. If you run into issues, [view the guide](Query-Stats.md).
|
128
150
|
|
151
|
+
## Historical Query Stats
|
152
|
+
|
153
|
+
To track query stats over time, create a table to store them.
|
154
|
+
|
155
|
+
```sql
|
156
|
+
CREATE TABLE "pghero_query_stats" ("id" serial primary key, "database" text, "query" text, "total_time" float, "calls" bigint, "captured_at" timestamp)
|
157
|
+
CREATE INDEX "index_pghero_query_stats_on_database_and_captured_at" ON "pghero_query_stats" ("database", "captured_at")
|
158
|
+
```
|
159
|
+
|
160
|
+
This table can be in the current database or another database. If another database, run:
|
161
|
+
|
162
|
+
```sh
|
163
|
+
sudo pghero config:set PGHERO_STATS_DATABASE_URL=...
|
164
|
+
```
|
165
|
+
|
166
|
+
Schedule the task below to run every 5 minutes.
|
167
|
+
|
168
|
+
```sh
|
169
|
+
sudo pghero run rake pghero:capture_query_stats
|
170
|
+
```
|
171
|
+
|
172
|
+
After this, a time range slider will appear on the Queries tab.
|
173
|
+
|
129
174
|
## System Stats
|
130
175
|
|
131
176
|
CPU usage is available for Amazon RDS. Add these variables to your environment:
|
data/guides/Rails.md
CHANGED
@@ -32,6 +32,7 @@ PgHero.relation_sizes
|
|
32
32
|
PgHero.index_hit_rate
|
33
33
|
PgHero.table_hit_rate
|
34
34
|
PgHero.total_connections
|
35
|
+
PgHero.locks
|
35
36
|
```
|
36
37
|
|
37
38
|
Kill queries
|
@@ -68,6 +69,8 @@ PgHero.replication_lag
|
|
68
69
|
|
69
70
|
## Users
|
70
71
|
|
72
|
+
**Note:** It’s unsafe to pass user input to these commands.
|
73
|
+
|
71
74
|
Create a user
|
72
75
|
|
73
76
|
```ruby
|
@@ -89,6 +92,12 @@ Set the password
|
|
89
92
|
PgHero.create_user("zelda", password: "hyrule")
|
90
93
|
```
|
91
94
|
|
95
|
+
Grant access to only certain tables
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
PgHero.create_user("navi", tables: ["triforce"])
|
99
|
+
```
|
100
|
+
|
92
101
|
Drop a user
|
93
102
|
|
94
103
|
```ruby
|
@@ -141,7 +150,7 @@ PgHero.capture_query_stats
|
|
141
150
|
|
142
151
|
After this, a time range slider will appear on the Queries tab.
|
143
152
|
|
144
|
-
By default,
|
153
|
+
By default, query stats are stored in your app’s database. Change this with:
|
145
154
|
|
146
155
|
```ruby
|
147
156
|
ENV["PGHERO_STATS_DATABASE_URL"]
|
data/lib/pghero.rb
CHANGED
@@ -325,7 +325,8 @@ module PgHero
|
|
325
325
|
value["total_percent"] = value["total_minutes"] * 100.0 / (current_query_stats[query]["all_queries_total_minutes"].to_f + historical_query_stats[query]["all_queries_total_minutes"].to_f)
|
326
326
|
query_stats << value
|
327
327
|
end
|
328
|
-
|
328
|
+
sort = options[:sort] || "total_minutes"
|
329
|
+
query_stats.sort_by { |q| -q[sort] }.first(100)
|
329
330
|
end
|
330
331
|
|
331
332
|
def slow_queries(options = {})
|
@@ -477,13 +478,21 @@ module PgHero
|
|
477
478
|
"GRANT USAGE ON SCHEMA #{schema} TO #{user}"
|
478
479
|
]
|
479
480
|
if options[:readonly]
|
480
|
-
|
481
|
-
|
481
|
+
if options[:tables]
|
482
|
+
commands.concat table_grant_commands("SELECT", options[:tables], user)
|
483
|
+
else
|
484
|
+
commands << "GRANT SELECT ON ALL TABLES IN SCHEMA #{schema} TO #{user}"
|
485
|
+
commands << "ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema} GRANT SELECT ON TABLES TO #{user}"
|
486
|
+
end
|
482
487
|
else
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
488
|
+
if options[:tables]
|
489
|
+
commands.concat table_grant_commands("ALL PRIVILEGES", options[:tables], user)
|
490
|
+
else
|
491
|
+
commands << "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA #{schema} TO #{user}"
|
492
|
+
commands << "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA #{schema} TO #{user}"
|
493
|
+
commands << "ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema} GRANT ALL PRIVILEGES ON TABLES TO #{user}"
|
494
|
+
commands << "ALTER DEFAULT PRIVILEGES IN SCHEMA #{schema} GRANT ALL PRIVILEGES ON SEQUENCES TO #{user}"
|
495
|
+
end
|
487
496
|
end
|
488
497
|
|
489
498
|
# run commands
|
@@ -581,10 +590,17 @@ module PgHero
|
|
581
590
|
|
582
591
|
private
|
583
592
|
|
593
|
+
def table_grant_commands(privilege, tables, user)
|
594
|
+
tables.map do |table|
|
595
|
+
"GRANT #{privilege} ON TABLE #{table} TO #{user}"
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
584
599
|
# http://www.craigkerstiens.com/2013/01/10/more-on-postgres-performance/
|
585
600
|
def current_query_stats(options = {})
|
586
601
|
if query_stats_enabled?
|
587
602
|
limit = options[:limit] || 100
|
603
|
+
sort = options[:sort] || "total_minutes"
|
588
604
|
select_all <<-SQL
|
589
605
|
WITH query_stats AS (
|
590
606
|
SELECT
|
@@ -609,7 +625,7 @@ module PgHero
|
|
609
625
|
FROM
|
610
626
|
query_stats
|
611
627
|
ORDER BY
|
612
|
-
|
628
|
+
#{quote_table_name(sort)} DESC
|
613
629
|
LIMIT #{limit.to_i}
|
614
630
|
SQL
|
615
631
|
else
|
@@ -619,6 +635,7 @@ module PgHero
|
|
619
635
|
|
620
636
|
def historical_query_stats(options = {})
|
621
637
|
if historical_query_stats_enabled?
|
638
|
+
sort = options[:sort] || "total_minutes"
|
622
639
|
stats_connection.select_all squish <<-SQL
|
623
640
|
WITH query_stats AS (
|
624
641
|
SELECT
|
@@ -645,7 +662,7 @@ module PgHero
|
|
645
662
|
FROM
|
646
663
|
query_stats
|
647
664
|
ORDER BY
|
648
|
-
|
665
|
+
#{quote_table_name(sort)} DESC
|
649
666
|
LIMIT 100
|
650
667
|
SQL
|
651
668
|
else
|
@@ -695,5 +712,9 @@ module PgHero
|
|
695
712
|
def quote(value)
|
696
713
|
connection.quote(value)
|
697
714
|
end
|
715
|
+
|
716
|
+
def quote_table_name(value)
|
717
|
+
connection.quote_table_name(value)
|
718
|
+
end
|
698
719
|
end
|
699
720
|
end
|
data/lib/pghero/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pghero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-07-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|