pghero 0.0.3 → 0.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 72c9e058b5ed49715e3376e81828ab8e16f37cf9
4
- data.tar.gz: 0371e228982aa29fc28780d81c41a7669f84e5f0
3
+ metadata.gz: f906933d95e8d3370c8c5e0770bea23b496d3163
4
+ data.tar.gz: 26d9a09ea1d0a655ef91247c5d10eb01f63e4c92
5
5
  SHA512:
6
- metadata.gz: fc8ba7e762a1cd496a704096983022024d27bd15169e963c4c2b24263c3e35f3014536aa285d32163f83e5c2482d8adefd2193c9fd495b0b3f1ae10831825b22
7
- data.tar.gz: 3c7580581995c32e229d259b8e2f50b29a12fb15f34ccf8895e690344cd8f708a0efe4675e086bcd451803418efdc4788f7d1667c87947f57c97a92c9c83c9ff
6
+ metadata.gz: 1967500ff522d67529f888cb03b47533fdf2ac4dcef9f316bf9ef3b135e6fdbc0c7edf9d47260f85547e818dd517e0daea5507601a07060ac3793eb4f28dfe2b
7
+ data.tar.gz: 9593a2e9a276fa692d6ea085313406cf5ca04c1b8ec50365137b807572bfdccdb6809d2098477b331a0ce28eec0ea44d8bc97887555747d94889faea9d3dacaa
@@ -0,0 +1,3 @@
1
+ ## 0.1.0
2
+
3
+ - First major release
data/README.md CHANGED
@@ -1,16 +1,16 @@
1
1
  # PgHero
2
2
 
3
- :tada: Database insights made easy
3
+ Database insights made easy
4
4
 
5
5
  [View the demo](https://pghero.herokuapp.com/)
6
6
 
7
- ![Screenshot](https://pghero.herokuapp.com/assets/screenshot-720ebfd2af9031f0565e4cc84cae99c0.png)
7
+ ![Screenshot](https://pghero.herokuapp.com/assets/screenshot-f7b70ae13b1f2ab0ea44ad005208c477.png)
8
8
 
9
9
  Supports PostgreSQL 9.2+
10
10
 
11
11
  For pure SQL, check out [PgHero.sql](https://github.com/ankane/pghero.sql)
12
12
 
13
- :clap: Initial queries and inspiration by [Heroku](https://blog.heroku.com/archives/2013/5/10/more_insight_into_your_database_with_pgextras)
13
+ Initial queries by [Heroku](https://blog.heroku.com/archives/2013/5/10/more_insight_into_your_database_with_pgextras) :clap:
14
14
 
15
15
  ## Installation
16
16
 
@@ -26,7 +26,7 @@ And mount the dashboard in your router.
26
26
  mount PgHero::Engine, at: "pghero"
27
27
  ```
28
28
 
29
- Be sure to [protect the dashboard](#security) in production.
29
+ Be sure to [secure the dashboard](#security) in production.
30
30
 
31
31
  ## Insights
32
32
 
@@ -71,7 +71,14 @@ end
71
71
  - better explanations on dashboard
72
72
  - use `pg_stat_statements` extension for more detailed insights
73
73
  - poll for running queries
74
- - better design (no bootstrap)
74
+
75
+ ## Thanks
76
+
77
+ Thanks to [Heroku](https://blog.heroku.com/archives/2013/5/10/more_insight_into_your_database_with_pgextras) for the initial queries and [Bootswatch](https://github.com/thomaspark/bootswatch) for the theme.
78
+
79
+ ## History
80
+
81
+ View the [changelog](https://github.com/ankane/pghero/blob/master/CHANGELOG.md)
75
82
 
76
83
  ## Contributing
77
84
 
@@ -5,61 +5,326 @@
5
5
 
6
6
  <meta charset="utf-8" />
7
7
 
8
- <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
9
-
10
8
  <style>
11
9
  body {
12
- padding-top: 20px;
10
+ font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
11
+ margin: 0;
12
+ padding: 20px;
13
+ font-size: 14px;
14
+ line-height: 1.4;
15
+ color: #333;
16
+ background-color: #2b3e50;
17
+ }
18
+
19
+ a, a:visited, a:active {
20
+ color: #428bca;
21
+ text-decoration: none;
22
+ }
23
+
24
+ a:hover {
25
+ text-decoration: underline;
26
+ }
27
+
28
+ table {
29
+ width: 100%;
30
+ border-collapse: collapse;
31
+ border-spacing: 0;
32
+ margin-bottom: 20px;
33
+ }
34
+
35
+ th {
36
+ text-align: left;
37
+ border-bottom: solid 2px #ddd;
13
38
  }
14
39
 
15
40
  h1 {
16
- margin-top: 6px;
41
+ margin-top: 0;
17
42
  font-size: 20px;
43
+ font-weight: bold;
18
44
  }
19
45
 
20
46
  h1, p {
21
47
  margin-bottom: 20px;
22
48
  }
23
49
 
50
+ ul {
51
+ list-style-type: none;
52
+ padding: 0;
53
+ margin: 0;
54
+ }
55
+
56
+ table td, table th {
57
+ padding: 8px;
58
+ }
59
+
60
+ td {
61
+ border-top: solid 1px #ddd;
62
+ }
63
+
64
+ pre {
65
+ background-color: #ddd;
66
+ padding: 10px;
67
+ white-space: pre-wrap;
68
+ }
69
+
24
70
  .container {
25
71
  max-width: 1000px;
72
+ margin-left: auto;
73
+ margin-right: auto;
74
+ }
75
+
76
+ .text-muted {
77
+ color: #999;
26
78
  }
27
79
 
28
- #status {
80
+ #status, .content, .alert {
29
81
  margin-bottom: 20px;
30
82
  }
31
83
 
84
+ .content {
85
+ padding: 20px 20px 1px 20px;
86
+ background-color: #eee;
87
+ }
88
+
89
+ .queries td {
90
+ vertical-align: middle;
91
+ }
92
+
32
93
  #status div {
33
94
  margin-bottom: 0;
95
+ }
96
+
97
+ /* nav */
98
+
99
+ .nav a {
100
+ color: #fff;
101
+ text-decoration: none;
102
+ display: block;
103
+ padding: 12px 20px;
104
+ }
105
+
106
+ .nav li.active a {
107
+ background-color: #df691a;
108
+ }
109
+
110
+ .nav a:hover {
111
+ background-color: #4e5d6c;
112
+ }
113
+
114
+ /* buttons */
115
+
116
+ .btn {
117
+ display: inline-block;
34
118
  border: none;
35
- border-radius: 0;
119
+ color: #fff;
120
+ padding: 12px 20px;
121
+ font-size: inherit;
122
+ font-family: inherit;
123
+ line-height: 1;
124
+ cursor: pointer;
125
+ outline: none;
126
+ margin: 0;
127
+ }
128
+
129
+ .btn-danger {
130
+ background-color: #df691a;
36
131
  }
37
132
 
133
+ .btn-info {
134
+ background-color: #5bc0de;
135
+ }
136
+
137
+ /* alerts */
138
+
38
139
  .alert {
39
- padding-top: 12px;
40
- padding-bottom: 12px;
140
+ padding: 12px 20px;
141
+ color: #fff;
41
142
  }
42
143
 
43
- .queries > tbody > tr > td {
44
- vertical-align: middle;
144
+ .alert-info {
145
+ background-color: #5bc0de;
45
146
  }
46
147
 
47
- .queries pre {
48
- white-space: pre-wrap;
148
+ .alert-success {
149
+ background-color: #5cb85c;
150
+ }
151
+
152
+ .alert-warning {
153
+ background-color: #f0ad4e;
154
+ }
155
+
156
+ .alert a {
157
+ color: #fff;
158
+ text-decoration: none;
159
+ }
160
+
161
+ /*
162
+ Simple Grid
163
+ Learn More - http://dallasbass.com/simple-grid-a-lightweight-responsive-css-grid/
164
+ Project Page - http://thisisdallas.github.com/Simple-Grid/
165
+ Author - Dallas Bass
166
+ Site - dallasbass.com
167
+ */
168
+
169
+ *, *:after, *:before {
170
+ -webkit-box-sizing: border-box;
171
+ -moz-box-sizing: border-box;
172
+ box-sizing: border-box;
173
+ }
174
+
175
+ body {
176
+ margin: 0px;
177
+ }
178
+
179
+ [class*='col-'] {
180
+ float: left;
181
+ padding-right: 20px;
182
+ }
183
+
184
+ [class*='col-']:last-of-type {
185
+ padding-right: 0px;
186
+ }
187
+
188
+ .grid {
189
+ width: 100%;
190
+
191
+ margin: 0 auto;
192
+ overflow: hidden;
193
+ }
194
+
195
+ .grid:after {
196
+ content: "";
197
+ display: table;
198
+ clear: both;
199
+ }
200
+
201
+ .grid-pad {
202
+ padding: 20px 0 0px 20px;
203
+ }
204
+
205
+ .grid-pad > [class*='col-']:last-of-type {
206
+ padding-right: 20px;
207
+ }
208
+
209
+ .push-right {
210
+ float: right;
211
+ }
212
+
213
+ /* Content Columns */
214
+
215
+ .col-1-1 {
216
+ width: 100%;
217
+ }
218
+ .col-2-3, .col-8-12 {
219
+ width: 66.66%;
220
+ }
221
+
222
+ .col-1-2, .col-6-12 {
223
+ width: 50%;
224
+ }
225
+
226
+ .col-1-3, .col-4-12 {
227
+ width: 33.33%;
228
+ }
229
+
230
+ .col-1-4, .col-3-12 {
231
+ width: 25%;
232
+ }
233
+
234
+ .col-1-5 {
235
+ width: 20%;
236
+ }
237
+
238
+ .col-1-6, .col-2-12 {
239
+ width: 16.667%;
240
+ }
241
+
242
+ .col-1-7 {
243
+ width: 14.28%;
244
+ }
245
+
246
+ .col-1-8 {
247
+ width: 12.5%;
248
+ }
249
+
250
+ .col-1-9 {
251
+ width: 11.1%;
252
+ }
253
+
254
+ .col-1-10 {
255
+ width: 10%;
256
+ }
257
+
258
+ .col-1-11 {
259
+ width: 9.09%;
260
+ }
261
+
262
+ .col-1-12 {
263
+ width: 8.33%
264
+ }
265
+
266
+ /* Layout Columns */
267
+
268
+ .col-11-12 {
269
+ width: 91.66%
270
+ }
271
+
272
+ .col-10-12 {
273
+ width: 83.333%;
274
+ }
275
+
276
+ .col-9-12 {
277
+ width: 75%;
278
+ }
279
+
280
+ .col-5-12 {
281
+ width: 41.66%;
282
+ }
283
+
284
+ .col-7-12 {
285
+ width: 58.33%
286
+ }
287
+
288
+ @media handheld, only screen and (max-width: 767px) {
289
+
290
+
291
+ .grid {
292
+ width: 100%;
293
+ min-width: 0;
294
+ margin-left: 0px;
295
+ margin-right: 0px;
296
+ padding-left: 0px;
297
+ padding-right: 0px;
298
+ }
299
+
300
+ [class*='col-'] {
301
+ width: auto;
302
+ float: none;
303
+ margin-left: 0px;
304
+ margin-right: 0px;
305
+ margin-top: 10px;
306
+ margin-bottom: 10px;
307
+ padding-right: 0px;
308
+ padding-left: 0px;
309
+ }
49
310
  }
50
311
  </style>
51
312
  </head>
52
313
  <body>
53
314
  <div class="container">
54
- <% if notice %>
55
- <div class="alert alert-info"><%= notice %></div>
56
- <% elsif Rails.env.development? %>
57
- <div class="alert alert-info">Do not use development information to make decisions about your production environment</div>
58
- <% end %>
59
-
60
- <div class="row">
61
- <div class="col-xs-3">
62
- <ul class="nav nav-pills nav-stacked">
315
+ <div class="alert alert-info">
316
+ <% if notice %>
317
+ <%= notice %>
318
+ <% elsif Rails.env.development? %>
319
+ Do not use development information to make decisions about your production environment
320
+ <% else %>
321
+ <%= link_to "PgHero", root_path %>
322
+ <% end %>
323
+ </div>
324
+
325
+ <div class="grid">
326
+ <div class="col-3-12">
327
+ <ul class="nav">
63
328
  <!-- poor man's active_link_to -->
64
329
  <li class="<%= controller.action_name == "index" ? "active" : "" %>"><%= link_to "Status", root_path %></li>
65
330
  <li class="<%= controller.action_name == "indexes" ? "active" : "" %>"><%= link_to "Indexes", indexes_path %></li>
@@ -68,7 +333,7 @@
68
333
  </ul>
69
334
  </div>
70
335
 
71
- <div class="col-xs-9">
336
+ <div class="col-9-12">
72
337
  <%= yield %>
73
338
  </div>
74
339
  </div>
@@ -16,11 +16,11 @@
16
16
  <td><%= query["state"] %></td>
17
17
  <td><%= query["source"] %></td>
18
18
  <td><%= query["waiting"] == "t" ? "true" : "false" %></td>
19
- <td><%= time_ago_in_words(query["started_at"], include_seconds: true) %></td>
19
+ <td><% query["started_at"] ? time_ago_in_words(query["started_at"], include_seconds: true) : nil %></td>
20
20
  <td class="text-right"><%= button_to "Kill", kill_path(pid: query["pid"]), class: "btn btn-info" %></td>
21
21
  </tr>
22
22
  <tr>
23
- <td colspan="6" style="border-top: none; padding-top: 0;"><pre><%= query["query"] %></pre></td>
23
+ <td colspan="6" style="border-top: none; padding: 0;"><pre><%= query["query"] %></pre></td>
24
24
  </tr>
25
25
  <% end %>
26
26
  </tbody>
@@ -10,12 +10,12 @@
10
10
  <% if @good_cache_rate %>
11
11
  Cache hit rate above 99%
12
12
  <% else %>
13
- Cache hit rate under 99%
13
+ Low cache hit rate
14
14
  <% end %>
15
15
  </div>
16
16
  <div class="alert alert-<%= @missing_indexes.empty? ? "success" : "warning" %>">
17
17
  <% if @missing_indexes.any? %>
18
- <%= pluralize(@missing_indexes.size, "possibly missing index", "possibly missing indexes") %>
18
+ <%= pluralize(@missing_indexes.size, "table appears", "tables appear") %> to be missing indexes
19
19
  <% else %>
20
20
  No missing indexes
21
21
  <% end %>
@@ -30,72 +30,81 @@
30
30
  </div>
31
31
 
32
32
  <% if @long_running_queries.any? %>
33
- <h1>Long Running Queries</h1>
33
+ <div class="content">
34
+ <h1>Long Running Queries</h1>
34
35
 
35
- <%= render partial: "queries_table", locals: {queries: @long_running_queries} %>
36
+ <%= render partial: "queries_table", locals: {queries: @long_running_queries} %>
37
+ </div>
36
38
  <% end %>
37
39
 
38
40
  <% if !@good_cache_rate %>
39
- <h1>Low Cache Hit Rate</h1>
41
+ <div class="content">
42
+ <h1>Low Cache Hit Rate</h1>
40
43
 
41
- <p>
42
- Index Hit Rate: <%= (@index_hit_rate * 100).round(1) %>%
43
- <br />
44
- Table Hit Rate: <%= (@table_hit_rate * 100).round(1) %>%
45
- </p>
44
+ <p>
45
+ Index Hit Rate: <%= (@index_hit_rate * 100).round(1) %>%
46
+ <br />
47
+ Table Hit Rate: <%= (@table_hit_rate * 100).round(1) %>%
48
+ </p>
46
49
 
47
- <p>
48
- Try adding more memory.
49
- <!-- TODO better suggestions -->
50
- </p>
50
+ <p>
51
+ The cache hit rate <%= link_to "should be above 99%", "https://devcenter.heroku.com/articles/understanding-postgres-data-caching", target: "_blank" %> in most cases. You can often increase this by adding more memory.
52
+ <!-- TODO better suggestions -->
53
+ </p>
54
+ </div>
51
55
  <% end %>
52
56
 
57
+
53
58
  <% if @missing_indexes.any? %>
54
- <h1>Missing Indexes</h1>
59
+ <div class="content">
60
+ <h1>Missing Indexes</h1>
55
61
 
56
- <p>These tables have a large number of rows and a lower % of time the index is used. Consider adding indexes where it makes sense.</p>
62
+ <p>These tables have a large number of rows but indexes are not used often. Add indexes for faster queries.</p>
57
63
 
58
- <table class="table">
59
- <thead>
60
- <tr>
61
- <th>Table</th>
62
- <th>% of Time Index Used</th>
63
- <th>Rows in Table</th>
64
- </tr>
65
- </thead>
66
- <tbody>
67
- <% @missing_indexes.each do |query| %>
64
+ <table class="table">
65
+ <thead>
68
66
  <tr>
69
- <td><%= query["table"] %></td>
70
- <td style="width: 30%;"><%= query["percent_of_times_index_used"] %></td>
71
- <td style="width: 20%;"><%= number_with_delimiter(query["rows_in_table"]) %></td>
67
+ <th>Table</th>
68
+ <th>% of Time Index Used</th>
69
+ <th>Rows in Table</th>
72
70
  </tr>
73
- <% end %>
74
- </tbody>
75
- </table>
71
+ </thead>
72
+ <tbody>
73
+ <% @missing_indexes.each do |query| %>
74
+ <tr>
75
+ <td><%= query["table"] %></td>
76
+ <td style="width: 30%;"><%= query["percent_of_times_index_used"] %></td>
77
+ <td style="width: 20%;"><%= number_with_delimiter(query["rows_in_table"]) %></td>
78
+ </tr>
79
+ <% end %>
80
+ </tbody>
81
+ </table>
82
+ </div>
76
83
  <% end %>
77
84
 
78
85
  <% if @unused_indexes.any? %>
79
- <h1>Unused Indexes</h1>
86
+ <div class="content">
87
+ <h1>Unused Indexes</h1>
80
88
 
81
- <p>Unused indexes cause unnecessary overhead.</p>
89
+ <p>Unused indexes cause unnecessary overhead. Remove them for faster writes.</p>
82
90
 
83
- <table class="table">
84
- <thead>
85
- <tr>
86
- <th>Name</th>
87
- <th>Index Size</th>
88
- <th>Index Scans</th>
89
- </tr>
90
- </thead>
91
- <tbody>
92
- <% @unused_indexes.each do |query| %>
91
+ <table class="table">
92
+ <thead>
93
93
  <tr>
94
- <td><%= query["index"] %><div class="text-muted">on <%= query["table"] %></div></td>
95
- <td style="width: 15%;"><%= query["index_size"] %></td>
96
- <td style="width: 15%;"><%= query["index_scans"] %></td>
94
+ <th>Name</th>
95
+ <th>Index Size</th>
96
+ <th>Index Scans</th>
97
97
  </tr>
98
- <% end %>
99
- </tbody>
100
- </table>
98
+ </thead>
99
+ <tbody>
100
+ <% @unused_indexes.each do |query| %>
101
+ <tr>
102
+ <td><%= query["index"] %><div class="text-muted">on <%= query["table"] %></div></td>
103
+ <td style="width: 15%;"><%= query["index_size"] %></td>
104
+ <td style="width: 15%;"><%= query["index_scans"] %></td>
105
+ </tr>
106
+ <% end %>
107
+ </tbody>
108
+ </table>
109
+ </div>
101
110
  <% end %>
@@ -1,20 +1,22 @@
1
- <h1>Index Usage</h1>
1
+ <div class="content">
2
+ <h1>Index Usage</h1>
2
3
 
3
- <table class="table">
4
- <thead>
5
- <tr>
6
- <th>Table</th>
7
- <th>% of Time Index Used</th>
8
- <th>Rows in Table</th>
9
- </tr>
10
- </thead>
11
- <tbody>
12
- <% @index_usage.each do |query| %>
4
+ <table class="table">
5
+ <thead>
13
6
  <tr>
14
- <td><%= query["table"] %></td>
15
- <td style="width: 30%;"><%= query["percent_of_times_index_used"] %></td>
16
- <td style="width: 20%;"><%= number_with_delimiter(query["rows_in_table"]) %></td>
7
+ <th>Table</th>
8
+ <th>% of Time Index Used</th>
9
+ <th>Rows in Table</th>
17
10
  </tr>
18
- <% end %>
19
- </tbody>
20
- </table>
11
+ </thead>
12
+ <tbody>
13
+ <% @index_usage.each do |query| %>
14
+ <tr>
15
+ <td><%= query["table"] %></td>
16
+ <td style="width: 30%;"><%= query["percent_of_times_index_used"] %></td>
17
+ <td style="width: 20%;"><%= number_with_delimiter(query["rows_in_table"]) %></td>
18
+ </tr>
19
+ <% end %>
20
+ </tbody>
21
+ </table>
22
+ </div>
@@ -1,7 +1,9 @@
1
- <h1>Queries</h1>
1
+ <div class="content">
2
+ <h1>Queries</h1>
2
3
 
3
- <%= render partial: "queries_table", locals: {queries: @running_queries} %>
4
+ <%= render partial: "queries_table", locals: {queries: @running_queries} %>
4
5
 
5
- <p><%= button_to "Kill all connections", kill_all_path, class: "btn btn-danger" %></p>
6
+ <p><%= button_to "Kill all connections", kill_all_path, class: "btn btn-danger" %></p>
6
7
 
7
- <p class="text-muted">You may need to restart your Rails server afterwards.</p>
8
+ <p class="text-muted">You may need to restart your Rails server afterwards.</p>
9
+ </div>
@@ -1,22 +1,24 @@
1
- <h1>Largest Relations</h1>
1
+ <div class="content">
2
+ <h1>Largest Relations</h1>
2
3
 
3
- <p>Database Size: <%= @database_size %></p>
4
+ <p>Database Size: <%= @database_size %></p>
4
5
 
5
- <table class="table">
6
- <thead>
7
- <tr>
8
- <th>Name</th>
9
- <th style="width: 20%;">Type</th>
10
- <th style="width: 20%;">Size</th>
11
- </tr>
12
- </thead>
13
- <tbody>
14
- <% @relation_sizes.each do |query| %>
6
+ <table class="table">
7
+ <thead>
15
8
  <tr>
16
- <td><%= query["name"] %></td>
17
- <td><%= query["type"] %></td>
18
- <td><%= query["size"] %></td>
9
+ <th>Name</th>
10
+ <th style="width: 20%;">Type</th>
11
+ <th style="width: 20%;">Size</th>
19
12
  </tr>
20
- <% end %>
21
- </tbody>
22
- </table>
13
+ </thead>
14
+ <tbody>
15
+ <% @relation_sizes.each do |query| %>
16
+ <tr>
17
+ <td><%= query["name"] %></td>
18
+ <td><%= query["type"] %></td>
19
+ <td><%= query["size"] %></td>
20
+ </tr>
21
+ <% end %>
22
+ </tbody>
23
+ </table>
24
+ </div>
@@ -1,3 +1,3 @@
1
1
  module PgHero
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
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: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-22 00:00:00.000000000 Z
11
+ date: 2014-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -46,6 +46,7 @@ extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
48
  - ".gitignore"
49
+ - CHANGELOG.md
49
50
  - Gemfile
50
51
  - LICENSE.txt
51
52
  - README.md